From 981bbda3e99da1da8fcf9cdbdec45900c4f2269a Mon Sep 17 00:00:00 2001 From: Caleb White Date: Wed, 20 Aug 2025 23:23:35 -0500 Subject: [PATCH] feat: add StaticToSelfStaticPropertyFetchOnFinalClassRector --- ...tStaticPrivateConstantToSelfRectorTest.php | 28 --- .../Fixture/change-in-non-final.php.inc | 27 --- ...inal_class_with_inherited_constant.php.inc | 33 ---- .../Fixture/on_final_class.php.inc | 37 ---- .../private-constant-with-static.php.inc | 31 --- .../replace-in-private-methods.php.inc | 27 --- .../Fixture/skip-already-self.php.inc | 13 -- .../skip-constant-from-other-class.php.inc | 11 -- .../Fixture/skip-other-visibility.php.inc | 16 -- .../Source/ParentClass.php | 9 - .../config/config.php | 9 - .../ConvertStaticToSelfRectorTest.php} | 4 +- .../Fixture/final_class_basic.php.inc | 47 +++++ .../final_class_inherited_members.php.inc | 35 ++++ .../non_final_class_final_members.php.inc | 47 +++++ .../Fixture/non_final_private_members.php.inc | 47 +++++ .../Fixture/skip_abstract_class.php.inc | 21 ++ .../Fixture/skip_already_self.php.inc | 21 ++ .../Fixture/skip_dynamic_members.php.inc | 13 ++ .../Fixture/skip_non_existent_members.php.inc | 13 ++ ...on_final_class_non_private_members.php.inc | 32 +++ .../Fixture/skip_non_static_members.php.inc | 18 ++ .../Fixture/skip_variable_static_call.php.inc | 21 ++ .../Source/BaseClass.php | 15 ++ .../config/configured_rule.php | 9 + .../Fixture/call_statically.php.inc | 37 ---- .../Fixture/parent_static_method.php.inc | 31 --- .../Fixture/skip_already_self.php.inc | 15 -- .../skip_call_non_existence_method.php.inc | 11 -- .../Fixture/skip_dynamic_method.php.inc | 11 -- .../Fixture/skip_from_variable.php.inc | 15 -- .../Fixture/skip_non_final.php.inc | 15 -- .../Fixture/skip_non_static_call.php.inc | 15 -- .../Source/BaseClass.php | 11 -- .../config/configured_rule.php | 9 - ...nvertStaticPrivateConstantToSelfRector.php | 80 +------- .../Class_/ConvertStaticToSelfRector.php | 183 ++++++++++++++++++ ...SelfStaticMethodCallOnFinalClassRector.php | 65 +------ src/Config/Level/CodeQualityLevel.php | 6 +- 39 files changed, 541 insertions(+), 547 deletions(-) delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/ConvertStaticPrivateConstantToSelfRectorTest.php delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/change-in-non-final.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/final_class_with_inherited_constant.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/on_final_class.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/private-constant-with-static.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/replace-in-private-methods.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/skip-already-self.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/skip-constant-from-other-class.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/skip-other-visibility.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Source/ParentClass.php delete mode 100644 rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/config/config.php rename rules-tests/CodeQuality/Rector/Class_/{StaticToSelfStaticMethodCallOnFinalClassRector/StaticToSelfStaticMethodCallOnFinalClassRectorTest.php => ConvertStaticToSelfRector/ConvertStaticToSelfRectorTest.php} (73%) create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_basic.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_inherited_members.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_class_final_members.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_private_members.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_abstract_class.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_already_self.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_dynamic_members.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_non_existent_members.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_non_final_class_non_private_members.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_non_static_members.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_variable_static_call.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Source/BaseClass.php create mode 100644 rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/config/configured_rule.php delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/call_statically.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/parent_static_method.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_already_self.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_call_non_existence_method.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_dynamic_method.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_from_variable.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_non_final.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_non_static_call.php.inc delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Source/BaseClass.php delete mode 100644 rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/config/configured_rule.php create mode 100644 rules/CodeQuality/Rector/Class_/ConvertStaticToSelfRector.php diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/ConvertStaticPrivateConstantToSelfRectorTest.php b/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/ConvertStaticPrivateConstantToSelfRectorTest.php deleted file mode 100644 index 2c3b91892b4..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/ConvertStaticPrivateConstantToSelfRectorTest.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/config.php'; - } -} diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/change-in-non-final.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/change-in-non-final.php.inc deleted file mode 100644 index 2bd3f95dc8f..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/change-in-non-final.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/final_class_with_inherited_constant.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/final_class_with_inherited_constant.php.inc deleted file mode 100644 index e5aa9fda0c8..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/final_class_with_inherited_constant.php.inc +++ /dev/null @@ -1,33 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/on_final_class.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/on_final_class.php.inc deleted file mode 100644 index 2366e5e6f87..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/on_final_class.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/private-constant-with-static.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/private-constant-with-static.php.inc deleted file mode 100644 index 32b95e914ba..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/private-constant-with-static.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/replace-in-private-methods.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/replace-in-private-methods.php.inc deleted file mode 100644 index 3965820af92..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/replace-in-private-methods.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/skip-already-self.php.inc b/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/skip-already-self.php.inc deleted file mode 100644 index 12964320e22..00000000000 --- a/rules-tests/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector/Fixture/skip-already-self.php.inc +++ /dev/null @@ -1,13 +0,0 @@ -withRules([ConvertStaticPrivateConstantToSelfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/StaticToSelfStaticMethodCallOnFinalClassRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/ConvertStaticToSelfRectorTest.php similarity index 73% rename from rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/StaticToSelfStaticMethodCallOnFinalClassRectorTest.php rename to rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/ConvertStaticToSelfRectorTest.php index 1463be7d355..5b306f205af 100644 --- a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/StaticToSelfStaticMethodCallOnFinalClassRectorTest.php +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/ConvertStaticToSelfRectorTest.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Rector\Tests\CodeQuality\Rector\Class_\StaticToSelfStaticMethodCallOnFinalClassRector; +namespace Rector\Tests\CodeQuality\Rector\Class_\ConvertStaticToSelfRector; use Iterator; use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; -final class StaticToSelfStaticMethodCallOnFinalClassRectorTest extends AbstractRectorTestCase +final class ConvertStaticToSelfRectorTest extends AbstractRectorTestCase { #[DataProvider('provideData')] public function test(string $filePath): void diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_basic.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_basic.php.inc new file mode 100644 index 00000000000..e40e5146a49 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_basic.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_inherited_members.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_inherited_members.php.inc new file mode 100644 index 00000000000..5786499fe0a --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/final_class_inherited_members.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_class_final_members.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_class_final_members.php.inc new file mode 100644 index 00000000000..c69bad6bdf6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_class_final_members.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_private_members.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_private_members.php.inc new file mode 100644 index 00000000000..bfa0ec502cc --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/non_final_private_members.php.inc @@ -0,0 +1,47 @@ + +----- + diff --git a/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_abstract_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_abstract_class.php.inc new file mode 100644 index 00000000000..4fc323b9c55 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ConvertStaticToSelfRector/Fixture/skip_abstract_class.php.inc @@ -0,0 +1,21 @@ +withRules([ConvertStaticToSelfRector::class]); diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/call_statically.php.inc b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/call_statically.php.inc deleted file mode 100644 index 8ed69160662..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/call_statically.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/parent_static_method.php.inc b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/parent_static_method.php.inc deleted file mode 100644 index afb0319859d..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/parent_static_method.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_already_self.php.inc b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_already_self.php.inc deleted file mode 100644 index 14a50e10adc..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Fixture/skip_already_self.php.inc +++ /dev/null @@ -1,15 +0,0 @@ -run(); - } - - private function run() - { - } -} diff --git a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Source/BaseClass.php b/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Source/BaseClass.php deleted file mode 100644 index 1f56d0e22b1..00000000000 --- a/rules-tests/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector/Source/BaseClass.php +++ /dev/null @@ -1,11 +0,0 @@ -withRules([StaticToSelfStaticMethodCallOnFinalClassRector::class]); diff --git a/rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php b/rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php index cbe1eb79876..79c4453bae6 100644 --- a/rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php +++ b/rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php @@ -5,23 +5,22 @@ namespace Rector\CodeQuality\Rector\ClassConstFetch; use PhpParser\Node; -use PhpParser\Node\Expr\ClassConstFetch; -use PhpParser\Node\Identifier; -use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; +use Rector\CodeQuality\Rector\Class_\ConvertStaticToSelfRector; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see \Rector\Tests\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector\ConvertStaticPrivateConstantToSelfRectorTest - * - * @see https://3v4l.org/8Y0ba - * @see https://3v4l.org/ZIeA1 - * @see https://phpstan.org/r/11d4c850-1a40-4fae-b665-291f96104d11 + * @deprecated Use ConvertStaticToSelfRector instead */ final class ConvertStaticPrivateConstantToSelfRector extends AbstractRector { + public function __construct( + private readonly ConvertStaticToSelfRector $convertStaticToSelfRector + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -73,69 +72,6 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Class_ { - if ($node->getConstants() === [] && ! $node->isFinal()) { - return null; - } - - $class = $node; - $hasChanged = false; - - $this->traverseNodesWithCallable($class, function (Node $node) use ($class, &$hasChanged): ?Node { - if (! $node instanceof ClassConstFetch) { - return null; - } - - if (! $this->isUsingStatic($node)) { - return null; - } - - if (! $class->isFinal() && ! $this->isPrivateConstant($node, $class)) { - return null; - } - - $hasChanged = true; - - $node->class = new Name('self'); - return $node; - }); - - if ($hasChanged) { - return $node; - } - - return null; - } - - private function isUsingStatic(ClassConstFetch $classConstFetch): bool - { - return $this->isName($classConstFetch->class, 'static'); - } - - private function isPrivateConstant(ClassConstFetch $classConstFetch, Class_ $class): bool - { - $constantName = $this->getConstantName($classConstFetch); - if ($constantName === null) { - return false; - } - - foreach ($class->getConstants() as $constant) { - if (! $this->isName($constant, $constantName)) { - continue; - } - - return $constant->isPrivate(); - } - - return false; - } - - private function getConstantName(ClassConstFetch $classConstFetch): ?string - { - $constantNameIdentifier = $classConstFetch->name; - if (! $constantNameIdentifier instanceof Identifier) { - return null; - } - - return $constantNameIdentifier->toString(); + return $this->convertStaticToSelfRector->refactor($node); } } diff --git a/rules/CodeQuality/Rector/Class_/ConvertStaticToSelfRector.php b/rules/CodeQuality/Rector/Class_/ConvertStaticToSelfRector.php new file mode 100644 index 00000000000..57ac9b1ad89 --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/ConvertStaticToSelfRector.php @@ -0,0 +1,183 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + $hasChanged = false; + $isFinal = $node->isFinal() || FeatureFlags::treatClassesAsFinal($node); + $scope = ScopeFetcher::fetch($node); + $classReflection = $scope->getClassReflection(); + + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $this->traverseNodesWithCallable($node->stmts, function (Node $subNode) use ( + &$hasChanged, + $classReflection, + $isFinal, + $scope, + ): ?Node { + if ( + ! $subNode instanceof StaticPropertyFetch + && ! $subNode instanceof StaticCall + && ! $subNode instanceof ClassConstFetch + ) { + return null; + } + + if ($this->shouldSkip($subNode, $classReflection, $isFinal, $scope)) { + return null; + } + + $hasChanged = true; + $subNode->class = new Name('self'); + return $subNode; + }); + + return $hasChanged ? $node : null; + } + + private function shouldSkip( + StaticPropertyFetch | StaticCall | ClassConstFetch $node, + ClassReflection $classReflection, + bool $isFinal, + Scope $scope, + ): bool { + if (! $node->class instanceof Name) { + return true; + } + + if (! $this->isName($node->class, ObjectReference::STATIC)) { + return true; + } + + if (! $node->name instanceof Identifier) { + return true; + } + + $name = (string) $this->getName($node->name); + + // For final classes, we can safely convert all static:: to self::, even + // it's virtual. For non-final classes, we can only convert private or final + // native members. + $hasMember = match (true) { + $node instanceof StaticPropertyFetch => $isFinal + ? $classReflection->hasProperty($name) + : $classReflection->hasNativeProperty($name), + $node instanceof StaticCall => $isFinal + ? $classReflection->hasMethod($name) + : $classReflection->hasNativeMethod($name), + $node instanceof ClassConstFetch => $classReflection->hasConstant($name), + }; + + if (! $hasMember) { + return true; + } + + $reflection = match (true) { + $node instanceof StaticPropertyFetch => $isFinal + ? $classReflection->getProperty($name, $scope) + : $classReflection->getNativeProperty($name), + $node instanceof StaticCall => $isFinal + ? $classReflection->getMethod($name, $scope) + : $classReflection->getNativeMethod($name), + $node instanceof ClassConstFetch => $classReflection->getConstant($name), + }; + + // avoid overlapped change + if (! $reflection->isStatic()) { + return true; + } + + if (! $isFinal) { + $memberIsFinal = $reflection instanceof ClassConstantReflection + ? $reflection->isFinal() + : $reflection->isFinal()->yes(); + + // Final native members can be safely converted + if ($memberIsFinal) { + return false; + } + + // Otherwise, only convert private native members + return ! $reflection->isPrivate(); + } + + // For final classes, can safely convert all members + return false; + } +} diff --git a/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php b/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php index 9ccd4a269dd..3ddc1df0b35 100644 --- a/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php +++ b/rules/CodeQuality/Rector/Class_/StaticToSelfStaticMethodCallOnFinalClassRector.php @@ -5,24 +5,21 @@ namespace Rector\CodeQuality\Rector\Class_; use PhpParser\Node; -use PhpParser\Node\Expr\StaticCall; -use PhpParser\Node\Identifier; -use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; -use PHPStan\Reflection\ClassReflection; -use Rector\Configuration\Parameter\FeatureFlags; -use Rector\Enum\ObjectReference; -use Rector\PHPStan\ScopeFetcher; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see https://3v4l.org/VbcrN - * @see \Rector\Tests\CodeQuality\Rector\Class_\StaticToSelfStaticMethodCallOnFinalClassRector\StaticToSelfStaticMethodCallOnFinalClassRectorTest + * @deprecated Use ConvertStaticToSelfRector instead */ final class StaticToSelfStaticMethodCallOnFinalClassRector extends AbstractRector { + public function __construct( + private readonly ConvertStaticToSelfRector $convertStaticToSelfRector + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Change `static::methodCall()` to `self::methodCall()` on final class', [ @@ -73,54 +70,6 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Class_ { - if (! $node->isFinal() && FeatureFlags::treatClassesAsFinal($node) === false) { - return null; - } - - $hasChanged = false; - $scope = ScopeFetcher::fetch($node); - $classReflection = $scope->getClassReflection(); - - if (! $classReflection instanceof ClassReflection) { - return null; - } - - $this->traverseNodesWithCallable($node->stmts, function (Node $subNode) use (&$hasChanged, $classReflection): ?StaticCall { - if (! $subNode instanceof StaticCall) { - return null; - } - - if (! $this->isName($subNode->class, ObjectReference::STATIC)) { - return null; - } - - // skip dynamic method - if (! $subNode->name instanceof Identifier) { - return null; - } - - $methodName = (string) $this->getName($subNode->name); - - if (! $classReflection->hasNativeMethod($methodName)) { - return null; - } - - $extendedMethodReflection = $classReflection->getNativeMethod($methodName); - - // avoid overlapped change - if (! $extendedMethodReflection->isStatic()) { - return null; - } - - $hasChanged = true; - $subNode->class = new Name('self'); - return $subNode; - }); - - if ($hasChanged) { - return $node; - } - - return null; + return $this->convertStaticToSelfRector->refactor($node); } } diff --git a/src/Config/Level/CodeQualityLevel.php b/src/Config/Level/CodeQualityLevel.php index f47427c7867..166f12a5607 100644 --- a/src/Config/Level/CodeQualityLevel.php +++ b/src/Config/Level/CodeQualityLevel.php @@ -11,10 +11,9 @@ use Rector\CodeQuality\Rector\BooleanNot\SimplifyDeMorganBinaryRector; use Rector\CodeQuality\Rector\Catch_\ThrowWithPreviousExceptionRector; use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector; +use Rector\CodeQuality\Rector\Class_\ConvertStaticToSelfRector; use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector; use Rector\CodeQuality\Rector\Class_\RemoveReadonlyPropertyVisibilityOnReadonlyClassRector; -use Rector\CodeQuality\Rector\Class_\StaticToSelfStaticMethodCallOnFinalClassRector; -use Rector\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector; use Rector\CodeQuality\Rector\ClassMethod\ExplicitReturnNullRector; use Rector\CodeQuality\Rector\ClassMethod\InlineArrayReturnAssignRector; use Rector\CodeQuality\Rector\ClassMethod\LocallyCalledStaticMethodToNonStaticRector; @@ -167,12 +166,11 @@ final class CodeQualityLevel SwitchTrueToIfRector::class, CleanupUnneededNullsafeOperatorRector::class, DisallowedEmptyRuleFixerRector::class, - ConvertStaticPrivateConstantToSelfRector::class, LocallyCalledStaticMethodToNonStaticRector::class, NumberCompareToMaxFuncCallRector::class, CompleteMissingIfElseBracketRector::class, RemoveUselessIsObjectCheckRector::class, - StaticToSelfStaticMethodCallOnFinalClassRector::class, + ConvertStaticToSelfRector::class, SortNamedParamRector::class, RemoveReadonlyPropertyVisibilityOnReadonlyClassRector::class, ];