diff --git a/phpstan.neon b/phpstan.neon index 429e9f6e8d5..bcb230046b6 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -219,6 +219,8 @@ parameters: - '#Register "Rector\\Php81\\Rector\\ClassMethod\\NewInInitializerRector" service to "php81\.php" config set#' - '#Register "Rector\\Php80\\Rector\\NotIdentical\\MbStrContainsRector" service to "php80\.php" config set#' + + - '#Register "Rector\\Php85\\Rector\\Class_\\FinalPropertyPromotionRector" service to "php85\.php" config set#' # closure detailed - '#Method Rector\\Config\\RectorConfig\:\:singleton\(\) has parameter \$concrete with no signature specified for Closure#' diff --git a/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/FinalPropertyPromotionRectorTest.php b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/FinalPropertyPromotionRectorTest.php new file mode 100644 index 00000000000..bf8c0f4b02a --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/FinalPropertyPromotionRectorTest.php @@ -0,0 +1,28 @@ +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/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/child_from_parent_class.php.inc b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/child_from_parent_class.php.inc new file mode 100644 index 00000000000..a6c1a295986 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/child_from_parent_class.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/fixture.php.inc b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..bbb21a0bb7c --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/fixture.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/non_typed_args.php.inc b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/non_typed_args.php.inc new file mode 100644 index 00000000000..55cadaee7f4 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/non_typed_args.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/readonly.php.inc b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/readonly.php.inc new file mode 100644 index 00000000000..820b1427098 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/readonly.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_already_final_param.php.inc b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_already_final_param.php.inc new file mode 100644 index 00000000000..4523731292a --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_already_final_param.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_anonymous_class.php.inc new file mode 100644 index 00000000000..164e6e7fcd3 --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_anonymous_class.php.inc @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_final_class.php.inc b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_final_class.php.inc new file mode 100644 index 00000000000..5103a213dfe --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Fixture/skip_final_class.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Source/ParentClass.php b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Source/ParentClass.php new file mode 100644 index 00000000000..d7d9c4c339e --- /dev/null +++ b/rules-tests/Php85/Rector/Class_/FinalPropertyPromotionRector/Source/ParentClass.php @@ -0,0 +1,7 @@ +rule(FinalPropertyPromotionRector::class); + +}; diff --git a/rules/Php85/Rector/Class_/FinalPropertyPromotionRector.php b/rules/Php85/Rector/Class_/FinalPropertyPromotionRector.php new file mode 100644 index 00000000000..16aa2bb0aa6 --- /dev/null +++ b/rules/Php85/Rector/Class_/FinalPropertyPromotionRector.php @@ -0,0 +1,120 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFinal()) { + return null; + } + + if ($node->isAnonymous()) { + return null; + } + + $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + + if (! $constructClassMethod instanceof ClassMethod) { + return null; + } + + $hasChanged = false; + $params = $constructClassMethod->getParams(); + + foreach ($params as $param) { + if (! $param->isPromoted()) { + continue; + } + + if ($this->visibilityManipulator->hasVisibility($param, Visibility::FINAL)) { + continue; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); + + if (! $phpDocInfo->hasByName(self::TAGNAME)) { + continue; + } + $hasChanged = true; + $this->visibilityManipulator->makeFinal($param); + $phpDocInfo->removeByName(self::TAGNAME); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($param); + } + + if($hasChanged){ + return $node; + } + return null; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::FINAL_PROPERTY_PROMOTION; + } +} diff --git a/src/ValueObject/PhpVersionFeature.php b/src/ValueObject/PhpVersionFeature.php index 9747fdb8345..29669baedc5 100644 --- a/src/ValueObject/PhpVersionFeature.php +++ b/src/ValueObject/PhpVersionFeature.php @@ -787,6 +787,12 @@ final class PhpVersionFeature */ public const DEPRECATED_NULL_DEBUG_INFO_RETURN = PhpVersion::PHP_85; + /** + * @see https://wiki.php.net/rfc/attributes-on-constants + * @var int + */ + public const FINAL_PROPERTY_PROMOTION = PhpVersion::PHP_85; + /** * @see https://wiki.php.net/rfc/attributes-on-constants * @var int