66
77use PhpParser \Node ;
88use PhpParser \Node \Expr \ArrayDimFetch ;
9+ use PhpParser \Node \Expr \BinaryOp \Minus ;
910use PhpParser \Node \Expr \FuncCall ;
11+ use PhpParser \Node \Scalar \Int_ ;
1012use Rector \NodeTypeResolver \Node \AttributeKey ;
1113use Rector \PHPStan \ScopeFetcher ;
1214use Rector \Rector \AbstractRector ;
@@ -34,11 +36,15 @@ public function getRuleDefinition(): RuleDefinition
3436 <<<'CODE_SAMPLE'
3537echo $array[array_key_first($array)];
3638echo $array[array_key_last($array)];
39+ echo array_values($array)[0];
40+ echo array_values($array)[count($array) - 1];
3741CODE_SAMPLE
3842 ,
3943 <<<'CODE_SAMPLE'
4044echo array_first($array);
4145echo array_last($array);
46+ echo array_first($array);
47+ echo array_last($array);
4248CODE_SAMPLE
4349 ),
4450 ]
@@ -57,6 +63,24 @@ public function getNodeTypes(): array
5763 * @param ArrayDimFetch $node
5864 */
5965 public function refactor (Node $ node ): ?FuncCall
66+ {
67+ if ($ node ->dim instanceof FuncCall) {
68+ return $ this ->refactorArrayKeyPattern ($ node );
69+ }
70+
71+ if ($ node ->var instanceof FuncCall && ($ node ->dim instanceof Int_ || $ node ->dim instanceof Minus)) {
72+ return $ this ->refactorArrayValuesPattern ($ node );
73+ }
74+
75+ return null ;
76+ }
77+
78+ public function provideMinPhpVersion (): int
79+ {
80+ return PhpVersionFeature::ARRAY_FIRST_LAST ;
81+ }
82+
83+ private function refactorArrayKeyPattern (ArrayDimFetch $ node ): ?FuncCall
6084 {
6185 if (! $ node ->dim instanceof FuncCall) {
6286 return null ;
@@ -78,12 +102,7 @@ public function refactor(Node $node): ?FuncCall
78102 return null ;
79103 }
80104
81- $ scope = ScopeFetcher::fetch ($ node ->var );
82- if ($ scope ->isInExpressionAssign ($ node )) {
83- return null ;
84- }
85-
86- if ($ node ->getAttribute (AttributeKey::IS_UNSET_VAR )) {
105+ if ($ this ->shouldSkip ($ node , $ node ->var )) {
87106 return null ;
88107 }
89108
@@ -94,8 +113,77 @@ public function refactor(Node $node): ?FuncCall
94113 return $ this ->nodeFactory ->createFuncCall ($ functionName , [$ node ->var ]);
95114 }
96115
97- public function provideMinPhpVersion ( ): int
116+ private function refactorArrayValuesPattern ( ArrayDimFetch $ node ): ? FuncCall
98117 {
99- return PhpVersionFeature::ARRAY_FIRST_LAST ;
118+ if (! $ node ->var instanceof FuncCall) {
119+ return null ;
120+ }
121+
122+ if (! $ this ->isName ($ node ->var , 'array_values ' )) {
123+ return null ;
124+ }
125+
126+ if ($ node ->var ->isFirstClassCallable ()) {
127+ return null ;
128+ }
129+
130+ if (count ($ node ->var ->getArgs ()) !== 1 ) {
131+ return null ;
132+ }
133+
134+ if ($ this ->shouldSkip ($ node , $ node )) {
135+ return null ;
136+ }
137+
138+ $ arrayArg = $ node ->var ->getArgs ()[0 ]
139+ ->value ;
140+
141+ if ($ node ->dim instanceof Int_ && $ node ->dim ->value === 0 ) {
142+ return $ this ->nodeFactory ->createFuncCall ('array_first ' , [$ arrayArg ]);
143+ }
144+
145+ if ($ node ->dim instanceof Minus) {
146+ if (! $ node ->dim ->left instanceof FuncCall) {
147+ return null ;
148+ }
149+
150+ if (! $ this ->isName ($ node ->dim ->left , 'count ' )) {
151+ return null ;
152+ }
153+
154+ if ($ node ->dim ->left ->isFirstClassCallable ()) {
155+ return null ;
156+ }
157+
158+ if (count ($ node ->dim ->left ->getArgs ()) !== 1 ) {
159+ return null ;
160+ }
161+
162+ if (! $ node ->dim ->right instanceof Int_ || $ node ->dim ->right ->value !== 1 ) {
163+ return null ;
164+ }
165+
166+ if (! $ this ->nodeComparator ->areNodesEqual ($ arrayArg , $ node ->dim ->left ->getArgs ()[0 ]->value )) {
167+ return null ;
168+ }
169+
170+ return $ this ->nodeFactory ->createFuncCall ('array_last ' , [$ arrayArg ]);
171+ }
172+
173+ return null ;
174+ }
175+
176+ private function shouldSkip (ArrayDimFetch $ node , Node $ scopeNode ): bool
177+ {
178+ $ scope = ScopeFetcher::fetch ($ scopeNode );
179+ if ($ scope ->isInExpressionAssign ($ node )) {
180+ return true ;
181+ }
182+
183+ if ($ node ->getAttribute (AttributeKey::IS_UNSET_VAR )) {
184+ return true ;
185+ }
186+
187+ return false ;
100188 }
101189}
0 commit comments