From cb24eeb3e8d28c237108e2690266c50a52e49194 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 11 Oct 2026 12:00:14 +0200 Subject: [PATCH 1/2] [CodeQuality] Deprecate InnerFunctionToPrivateMethodRector as niche and behavior-risky, needs human check --- .../Fixture/inner_method.php.inc | 36 ---- .../skip_existing_method_collision.php.inc | 21 --- .../Fixture/skip_no_inner_function.php.inc | 11 -- .../Fixture/skip_string_callable.php.inc | 22 --- .../Fixture/static_method.php.inc | 36 ---- ...InnerFunctionToPrivateMethodRectorTest.php | 28 --- .../config/configured_rule.php | 9 - .../InnerFunctionToPrivateMethodRector.php | 160 +----------------- src/Config/Level/CodeQualityLevel.php | 2 - 9 files changed, 8 insertions(+), 317 deletions(-) delete mode 100644 rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/inner_method.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_existing_method_collision.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_no_inner_function.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_string_callable.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/static_method.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/InnerFunctionToPrivateMethodRectorTest.php delete mode 100644 rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/config/configured_rule.php diff --git a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/inner_method.php.inc b/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/inner_method.php.inc deleted file mode 100644 index 191980c7882..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/inner_method.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ -inner(); - } - private function inner(): void - { - echo 'hello'; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_existing_method_collision.php.inc b/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_existing_method_collision.php.inc deleted file mode 100644 index 5f6a5c81769..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/skip_existing_method_collision.php.inc +++ /dev/null @@ -1,21 +0,0 @@ - 'b', 'label' => 'Bravo'], - ['value' => 'a', 'label' => 'Alpha'], - ]; - - usort($options, 'sortByOrder'); - return $options; - } -} diff --git a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/static_method.php.inc b/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/static_method.php.inc deleted file mode 100644 index 1e28b5afc1d..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/Fixture/static_method.php.inc +++ /dev/null @@ -1,36 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/InnerFunctionToPrivateMethodRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/InnerFunctionToPrivateMethodRectorTest.php deleted file mode 100644 index 5703ab95351..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/InnerFunctionToPrivateMethodRectorTest.php +++ /dev/null @@ -1,28 +0,0 @@ -doTestFile($filePath); - } - - public static function provideData(): Iterator - { - return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/config/configured_rule.php deleted file mode 100644 index ae3c0fbe14a..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector/config/configured_rule.php +++ /dev/null @@ -1,9 +0,0 @@ -withRules([InnerFunctionToPrivateMethodRector::class]); diff --git a/rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php b/rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php index 50629fd9282..b676968f69a 100644 --- a/rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php +++ b/rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php @@ -4,25 +4,18 @@ namespace Rector\CodeQuality\Rector\Class_; -use PhpParser\Modifiers; use PhpParser\Node; -use PhpParser\Node\Expr\FuncCall; -use PhpParser\Node\Expr\MethodCall; -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_; +use Rector\Configuration\Deprecation\Contract\DeprecatedInterface; +use Rector\Exception\ShouldNotHappenException; use Rector\Rector\AbstractRector; use Rector\RuleDoc\CodeSample\CodeSample; use Rector\RuleDoc\RuleDefinition; /** - * @see \Rector\Tests\CodeQuality\Rector\Class_\InnerFunctionToPrivateMethodRector\InnerFunctionToPrivateMethodRectorTest + * @deprecated This rule is deprecated, as moving an inner named function to a private method is a niche change that can alter behavior in undesired ways and needs a human check. */ -final class InnerFunctionToPrivateMethodRector extends AbstractRector +final class InnerFunctionToPrivateMethodRector extends AbstractRector implements DeprecatedInterface { public function getRuleDefinition(): RuleDefinition { @@ -77,146 +70,9 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $existingMethodNames = array_map( - fn (ClassMethod $classMethod): string => $classMethod->name->toString(), - $node->getMethods() - ); - - $newClassMethods = []; - - foreach ($node->getMethods() as $classMethod) { - $innerFunctions = $this->extractInnerFunctions($classMethod, $existingMethodNames); - if ($innerFunctions === []) { - continue; - } - - $isStatic = $classMethod->isStatic(); - $innerFunctionNames = array_map( - fn (Function_ $innerFunction): string => $innerFunction->name->toString(), - $innerFunctions - ); - - foreach ($innerFunctions as $innerFunction) { - $newClassMethod = $this->createPrivateMethod($innerFunction, $isStatic); - $this->rewriteCallsToMethod($newClassMethod, $innerFunctionNames, $isStatic); - $newClassMethods[] = $newClassMethod; - } - - $this->rewriteCallsToMethod($classMethod, $innerFunctionNames, $isStatic); - } - - if ($newClassMethods === []) { - return null; - } - - $node->stmts = array_merge($node->stmts, $newClassMethods); - - return $node; - } - - /** - * @param string[] $existingMethodNames - * @return Function_[] - */ - private function extractInnerFunctions(ClassMethod $classMethod, array &$existingMethodNames): array - { - $innerFunctions = []; - - foreach ($classMethod->stmts ?? [] as $key => $stmt) { - if (! $stmt instanceof Function_) { - continue; - } - - $functionName = $stmt->name->toString(); - - // avoid collision with an already existing method - if (in_array($functionName, $existingMethodNames, true)) { - continue; - } - - // avoid breaking string callables, e.g. usort($items, 'functionName') - if ($this->isReferencedAsStringCallable($classMethod, $functionName)) { - continue; - } - - $existingMethodNames[] = $functionName; - $innerFunctions[] = $stmt; - unset($classMethod->stmts[$key]); - } - - if ($innerFunctions !== []) { - $classMethod->stmts = array_values((array) $classMethod->stmts); - } - - 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; - if ($isStatic) { - $flags |= Modifiers::STATIC; - } - - $classMethod = new ClassMethod($innerFunction->name, [ - 'flags' => $flags, - 'byRef' => $innerFunction->byRef, - 'params' => $innerFunction->params, - 'returnType' => $innerFunction->returnType, - 'stmts' => $innerFunction->stmts, - 'attrGroups' => $innerFunction->attrGroups, - ]); - - $this->mirrorComments($classMethod, $innerFunction); - - return $classMethod; - } - - /** - * @param string[] $innerFunctionNames - */ - private function rewriteCallsToMethod(ClassMethod $classMethod, array $innerFunctionNames, bool $isStatic): void - { - $this->traverseNodesWithCallable($classMethod->stmts ?? [], function (Node $node) use ( - $innerFunctionNames, - $isStatic - ): MethodCall|StaticCall|null { - if (! $node instanceof FuncCall) { - return null; - } - - if (! $node->name instanceof Name) { - return null; - } - - $functionName = $node->name->toString(); - if (! in_array($functionName, $innerFunctionNames, true)) { - return null; - } - - if ($isStatic) { - return new StaticCall(new Name('self'), $functionName, $node->args); - } - - return new MethodCall(new Variable('this'), $functionName, $node->args); - }); + throw new ShouldNotHappenException(sprintf( + '"%s" rule is deprecated, as it is a niche change that can alter behavior in undesired ways and needs a human check', + self::class + )); } } diff --git a/src/Config/Level/CodeQualityLevel.php b/src/Config/Level/CodeQualityLevel.php index f20343b7cf3..7cb806ec8e4 100644 --- a/src/Config/Level/CodeQualityLevel.php +++ b/src/Config/Level/CodeQualityLevel.php @@ -18,7 +18,6 @@ use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector; use Rector\CodeQuality\Rector\Class_\ConvertStaticToSelfRector; use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector; -use Rector\CodeQuality\Rector\Class_\InnerFunctionToPrivateMethodRector; use Rector\CodeQuality\Rector\ClassConstFetch\VariableConstFetchToClassConstFetchRector; use Rector\CodeQuality\Rector\ClassMethod\ExplicitReturnNullRector; use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector; @@ -110,7 +109,6 @@ final class CodeQualityLevel ForeachToInArrayRector::class, RepeatedOrEqualToInArrayRector::class, RepeatedAndNotEqualToNotInArrayRector::class, - InnerFunctionToPrivateMethodRector::class, SimplifyForeachToCoalescingRector::class, SimplifyFuncGetArgsCountRector::class, SimplifyInArrayValuesRector::class, From bc2af6cd0373d74a06ab6f8ccca15596dc2a698c Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 11 Oct 2026 12:02:37 +0200 Subject: [PATCH 2/2] Remove stale PHPStan ignore for deprecated InnerFunctionToPrivateMethodRector --- phpstan.neon | 1 - 1 file changed, 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index 52ba235ce69..c80acc14b90 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -360,7 +360,6 @@ parameters: - rules/Php70/EregToPcreTransformer.php - src/BetterPhpDocParser/PhpDocManipulator/PhpDocClassRenamer.php - src/NodeTypeResolver/PHPStan/Type/TypeFactory.php - - rules/CodeQuality/Rector/Class_/InnerFunctionToPrivateMethodRector.php - rules/CodeQuality/NodeAnalyzer/VariableDimFetchAssignResolver.php - src/Parallel/Experimental/ExperimentalParallelFileProcessor.php