From ef47ee4f5043db6416c73e420e1b98738af42834 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 8 Jul 2026 13:00:59 +0200 Subject: [PATCH] [CodeQuality] Skip inner function referenced as string callable in InnerFunctionToPrivateMethodRector --- .../Fixture/skip_string_callable.php.inc | 22 +++++++++++++++++ .../InnerFunctionToPrivateMethodRector.php | 24 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_string_callable.php.inc diff --git a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_string_callable.php.inc b/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_string_callable.php.inc new file mode 100644 index 00000000000..699d21df9a5 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_string_callable.php.inc @@ -0,0 +1,22 @@ + 'b', 'label' => 'Bravo'], + ['value' => 'a', 'label' => 'Alpha'], + ]; + + usort($options, 'sortByOrder'); + return $options; + } +} diff --git a/rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php b/rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php index ab028f6764f..bebf7ca3a00 100644 --- a/rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php +++ b/rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php @@ -11,6 +11,7 @@ use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name; +use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; @@ -133,6 +134,11 @@ private function extractInnerFunctions(ClassMethod $classMethod, array &$existin continue; } + // avoid breaking string callables, e.g. usort($items, 'functionName') + if ($this->isReferencedAsStringCallable($classMethod, $functionName)) { + continue; + } + $existingMethodNames[] = $functionName; $innerFunctions[] = $stmt; unset($classMethod->stmts[$key]); @@ -145,6 +151,24 @@ private function extractInnerFunctions(ClassMethod $classMethod, array &$existin return $innerFunctions; } + private function isReferencedAsStringCallable(ClassMethod $classMethod, string $functionName): bool + { + $isReferenced = false; + + $this->traverseNodesWithCallable($classMethod->stmts ?? [], function (Node $node) use ( + $functionName, + &$isReferenced + ): null { + if ($node instanceof String_ && $node->value === $functionName) { + $isReferenced = true; + } + + return null; + }); + + return $isReferenced; + } + private function createPrivateMethod(Function_ $innerFunction, bool $isStatic): ClassMethod { $flags = Modifiers::PRIVATE;