From 1033c384f41b1e00d301480d2543e0785eac9d4a Mon Sep 17 00:00:00 2001 From: Orest Divintari Date: Wed, 14 Jan 2026 19:07:07 +0200 Subject: [PATCH 1/4] Support array_values in ArrayFirstLastRector --- .../Fixture/array_values_pattern.php.inc | 33 +++++++ ...ip_array_values_different_variable.php.inc | 13 +++ .../ArrayDimFetch/ArrayFirstLastRector.php | 88 ++++++++++++++++++- 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/array_values_pattern.php.inc create mode 100644 rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_array_values_different_variable.php.inc diff --git a/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/array_values_pattern.php.inc b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/array_values_pattern.php.inc new file mode 100644 index 00000000000..8931a04fd8f --- /dev/null +++ b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/array_values_pattern.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_array_values_different_variable.php.inc b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_array_values_different_variable.php.inc new file mode 100644 index 00000000000..21f7abf72cb --- /dev/null +++ b/rules-tests/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector/Fixture/skip_array_values_different_variable.php.inc @@ -0,0 +1,13 @@ +dim instanceof FuncCall) { + return $this->refactorArrayKeyPattern($node); + } + + if ($node->var instanceof FuncCall && ($node->dim instanceof Int_ || $node->dim instanceof Minus)) { + return $this->refactorArrayValuesPattern($node); + } + + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_FIRST_LAST; + } + + private function refactorArrayKeyPattern(ArrayDimFetch $node): ?FuncCall { if (! $node->dim instanceof FuncCall) { return null; @@ -100,8 +124,68 @@ public function refactor(Node $node): ?FuncCall return $this->nodeFactory->createFuncCall($functionName, [$node->var]); } - public function provideMinPhpVersion(): int + private function refactorArrayValuesPattern(ArrayDimFetch $node): ?FuncCall { - return PhpVersionFeature::ARRAY_FIRST_LAST; + if (! $node->var instanceof FuncCall) { + return null; + } + + if (! $this->isName($node->var, 'array_values')) { + return null; + } + + if ($node->var->isFirstClassCallable()) { + return null; + } + + if (count($node->var->getArgs()) !== 1) { + return null; + } + + $scope = ScopeFetcher::fetch($node); + if ($scope->isInExpressionAssign($node)) { + return null; + } + + if ($node->getAttribute(AttributeKey::IS_UNSET_VAR)) { + return null; + } + + $arrayArg = $node->var->getArgs()[0] + ->value; + + if ($node->dim instanceof Int_ && $node->dim->value === 0) { + return $this->nodeFactory->createFuncCall('array_first', [$arrayArg]); + } + + if ($node->dim instanceof Minus) { + if (! $node->dim->left instanceof FuncCall) { + return null; + } + + if (! $this->isName($node->dim->left, 'count')) { + return null; + } + + if ($node->dim->left->isFirstClassCallable()) { + return null; + } + + if (count($node->dim->left->getArgs()) !== 1) { + return null; + } + + if (! $node->dim->right instanceof Int_ || $node->dim->right->value !== 1) { + return null; + } + + if (! $this->nodeComparator->areNodesEqual($arrayArg, $node->dim->left->getArgs()[0]->value)) { + return null; + } + + return $this->nodeFactory->createFuncCall('array_last', [$arrayArg]); + } + + return null; } } From be2d81ab2fa90b7fdfc3e25c34a7707b39581c90 Mon Sep 17 00:00:00 2001 From: Orest Divintari Date: Thu, 15 Jan 2026 11:14:31 +0200 Subject: [PATCH 2/4] Refactor shouldSkip logic in ArrayFirstLastRector --- .../ArrayDimFetch/ArrayFirstLastRector.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php index a569be359e7..d012740cbca 100644 --- a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php +++ b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php @@ -108,12 +108,7 @@ private function refactorArrayKeyPattern(ArrayDimFetch $node): ?FuncCall return null; } - $scope = ScopeFetcher::fetch($node->var); - if ($scope->isInExpressionAssign($node)) { - return null; - } - - if ($node->getAttribute(AttributeKey::IS_UNSET_VAR)) { + if ($this->shouldSkip($node)) { return null; } @@ -142,12 +137,7 @@ private function refactorArrayValuesPattern(ArrayDimFetch $node): ?FuncCall return null; } - $scope = ScopeFetcher::fetch($node); - if ($scope->isInExpressionAssign($node)) { - return null; - } - - if ($node->getAttribute(AttributeKey::IS_UNSET_VAR)) { + if ($this->shouldSkip($node)) { return null; } @@ -188,4 +178,14 @@ private function refactorArrayValuesPattern(ArrayDimFetch $node): ?FuncCall return null; } + + private function shouldSkip(ArrayDimFetch $node): bool + { + $scope = ScopeFetcher::fetch($node); + if ($scope->isInExpressionAssign($node)) { + return true; + } + + return (bool) $node->getAttribute(AttributeKey::IS_UNSET_VAR); + } } From 1db562174d5833e91750d3add104728b6bbbaafb Mon Sep 17 00:00:00 2001 From: Orest Divintari Date: Thu, 15 Jan 2026 11:16:16 +0200 Subject: [PATCH 3/4] Replace bool cast with explicit return --- rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php index d012740cbca..1c52331e6ca 100644 --- a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php +++ b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php @@ -186,6 +186,10 @@ private function shouldSkip(ArrayDimFetch $node): bool return true; } - return (bool) $node->getAttribute(AttributeKey::IS_UNSET_VAR); + if ($node->getAttribute(AttributeKey::IS_UNSET_VAR)) { + return true; + } + + return false; } } From ff3a75809262f1ef53ef45dfa3288f44c1623d72 Mon Sep 17 00:00:00 2001 From: Orest Divintari Date: Thu, 15 Jan 2026 11:25:42 +0200 Subject: [PATCH 4/4] Fix scope node --- rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php index 1c52331e6ca..7d4f65b1beb 100644 --- a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php +++ b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php @@ -108,7 +108,7 @@ private function refactorArrayKeyPattern(ArrayDimFetch $node): ?FuncCall return null; } - if ($this->shouldSkip($node)) { + if ($this->shouldSkip($node, $node->var)) { return null; } @@ -137,7 +137,7 @@ private function refactorArrayValuesPattern(ArrayDimFetch $node): ?FuncCall return null; } - if ($this->shouldSkip($node)) { + if ($this->shouldSkip($node, $node)) { return null; } @@ -179,9 +179,9 @@ private function refactorArrayValuesPattern(ArrayDimFetch $node): ?FuncCall return null; } - private function shouldSkip(ArrayDimFetch $node): bool + private function shouldSkip(ArrayDimFetch $node, Node $scopeNode): bool { - $scope = ScopeFetcher::fetch($node); + $scope = ScopeFetcher::fetch($scopeNode); if ($scope->isInExpressionAssign($node)) { return true; }