55
66use PhpParser \Node ;
77use PhpParser \Node \Expr \ArrayDimFetch ;
8+ use PhpParser \Node \Expr \BinaryOp \Minus ;
89use PhpParser \Node \Expr \FuncCall ;
10+ use PhpParser \Node \Scalar \Int_ ;
911use Rector \NodeTypeResolver \Node \AttributeKey ;
1012use Rector \PHPStan \ScopeFetcher ;
1113use Rector \Rector \AbstractRector ;
@@ -32,10 +34,14 @@ public function getRuleDefinition(): RuleDefinition
3234 return new RuleDefinition ('Make use of array_first() and array_last() ' , [new CodeSample (<<<'CODE_SAMPLE'
3335echo $array[array_key_first($array)];
3436echo $array[array_key_last($array)];
37+ echo array_values($array)[0];
38+ echo array_values($array)[count($array) - 1];
3539CODE_SAMPLE
3640, <<<'CODE_SAMPLE'
3741echo array_first($array);
3842echo array_last($array);
43+ echo array_first($array);
44+ echo array_last($array);
3945CODE_SAMPLE
4046)]);
4147 }
@@ -50,6 +56,20 @@ public function getNodeTypes(): array
5056 * @param ArrayDimFetch $node
5157 */
5258 public function refactor (Node $ node ): ?FuncCall
59+ {
60+ if ($ node ->dim instanceof FuncCall) {
61+ return $ this ->refactorArrayKeyPattern ($ node );
62+ }
63+ if ($ node ->var instanceof FuncCall && ($ node ->dim instanceof Int_ || $ node ->dim instanceof Minus)) {
64+ return $ this ->refactorArrayValuesPattern ($ node );
65+ }
66+ return null ;
67+ }
68+ public function provideMinPhpVersion (): int
69+ {
70+ return PhpVersionFeature::ARRAY_FIRST_LAST ;
71+ }
72+ private function refactorArrayKeyPattern (ArrayDimFetch $ node ): ?FuncCall
5373 {
5474 if (!$ node ->dim instanceof FuncCall) {
5575 return null ;
@@ -66,18 +86,65 @@ public function refactor(Node $node): ?FuncCall
6686 if (!$ this ->nodeComparator ->areNodesEqual ($ node ->var , $ node ->dim ->getArgs ()[0 ]->value )) {
6787 return null ;
6888 }
69- $ scope = ScopeFetcher::fetch ($ node ->var );
70- if ($ scope ->isInExpressionAssign ($ node )) {
71- return null ;
72- }
73- if ($ node ->getAttribute (AttributeKey::IS_UNSET_VAR )) {
89+ if ($ this ->shouldSkip ($ node , $ node ->var )) {
7490 return null ;
7591 }
7692 $ functionName = $ this ->isName ($ node ->dim , self ::ARRAY_KEY_FIRST ) ? 'array_first ' : 'array_last ' ;
7793 return $ this ->nodeFactory ->createFuncCall ($ functionName , [$ node ->var ]);
7894 }
79- public function provideMinPhpVersion ( ): int
95+ private function refactorArrayValuesPattern ( ArrayDimFetch $ node ): ? FuncCall
8096 {
81- return PhpVersionFeature::ARRAY_FIRST_LAST ;
97+ if (!$ node ->var instanceof FuncCall) {
98+ return null ;
99+ }
100+ if (!$ this ->isName ($ node ->var , 'array_values ' )) {
101+ return null ;
102+ }
103+ if ($ node ->var ->isFirstClassCallable ()) {
104+ return null ;
105+ }
106+ if (count ($ node ->var ->getArgs ()) !== 1 ) {
107+ return null ;
108+ }
109+ if ($ this ->shouldSkip ($ node , $ node )) {
110+ return null ;
111+ }
112+ $ arrayArg = $ node ->var ->getArgs ()[0 ]->value ;
113+ if ($ node ->dim instanceof Int_ && $ node ->dim ->value === 0 ) {
114+ return $ this ->nodeFactory ->createFuncCall ('array_first ' , [$ arrayArg ]);
115+ }
116+ if ($ node ->dim instanceof Minus) {
117+ if (!$ node ->dim ->left instanceof FuncCall) {
118+ return null ;
119+ }
120+ if (!$ this ->isName ($ node ->dim ->left , 'count ' )) {
121+ return null ;
122+ }
123+ if ($ node ->dim ->left ->isFirstClassCallable ()) {
124+ return null ;
125+ }
126+ if (count ($ node ->dim ->left ->getArgs ()) !== 1 ) {
127+ return null ;
128+ }
129+ if (!$ node ->dim ->right instanceof Int_ || $ node ->dim ->right ->value !== 1 ) {
130+ return null ;
131+ }
132+ if (!$ this ->nodeComparator ->areNodesEqual ($ arrayArg , $ node ->dim ->left ->getArgs ()[0 ]->value )) {
133+ return null ;
134+ }
135+ return $ this ->nodeFactory ->createFuncCall ('array_last ' , [$ arrayArg ]);
136+ }
137+ return null ;
138+ }
139+ private function shouldSkip (ArrayDimFetch $ node , Node $ scopeNode ): bool
140+ {
141+ $ scope = ScopeFetcher::fetch ($ scopeNode );
142+ if ($ scope ->isInExpressionAssign ($ node )) {
143+ return \true;
144+ }
145+ if ($ node ->getAttribute (AttributeKey::IS_UNSET_VAR )) {
146+ return \true;
147+ }
148+ return \false;
82149 }
83150}
0 commit comments