From 67da1fc2dace9cf050a32aa8412fe52d281d58f2 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 7 Sep 2025 17:06:51 +0700 Subject: [PATCH 1/2] [DowngradePhp81] Add @readonly doc on property promotion readonly --- .../Fixture/promoted_property.php.inc | 3 +++ .../readonly_implicit_property_promotion.php.inc | 3 +++ .../Property/DowngradeReadonlyPropertyRector.php | 10 ++++------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/rules-tests/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector/Fixture/promoted_property.php.inc b/rules-tests/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector/Fixture/promoted_property.php.inc index 324f35b8..c5fb35e1 100644 --- a/rules-tests/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector/Fixture/promoted_property.php.inc +++ b/rules-tests/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector/Fixture/promoted_property.php.inc @@ -20,6 +20,9 @@ namespace Rector\Tests\DowngradePhp81\Rector\Property\DowngradeReadonlyPropertyR class SomeClass { public function __construct( + /** + * @readonly + */ public string $foo = 'foo' ) { diff --git a/rules-tests/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector/Fixture/readonly_implicit_property_promotion.php.inc b/rules-tests/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector/Fixture/readonly_implicit_property_promotion.php.inc index fb83bd5c..748d95d7 100644 --- a/rules-tests/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector/Fixture/readonly_implicit_property_promotion.php.inc +++ b/rules-tests/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector/Fixture/readonly_implicit_property_promotion.php.inc @@ -20,6 +20,9 @@ namespace Rector\Tests\DowngradePhp81\Rector\Property\DowngradeReadonlyPropertyR class ReadonlyImplicitPropertyPromotion { public function __construct( + /** + * @readonly + */ public string $foo = 'foo' ) { diff --git a/rules/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector.php b/rules/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector.php index f19ef816..64dcc8c8 100644 --- a/rules/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector.php +++ b/rules/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector.php @@ -89,24 +89,22 @@ public function refactor(Node $node): ?Node return null; } - if ($node instanceof Property) { - $this->addPhpDocTag($node); - } + $this->addPhpDocTag($node); $this->visibilityManipulator->removeReadonly($node); return $node; } - private function addPhpDocTag(Property $property): void + private function addPhpDocTag(Property|Param $node): void { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); if ($phpDocInfo->hasByName(self::TAGNAME)) { return; } $phpDocInfo->addPhpDocTagNode(new PhpDocTagNode('@' . self::TAGNAME, new GenericTagValueNode(''))); - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); } } From 286be0332d95b02ed7406a9ddb98bffc749439ad Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 7 Sep 2025 17:23:53 +0700 Subject: [PATCH 2/2] Fix print --- .../DowngradeReadonlyPropertyRector.php | 47 ++++++++++++++++--- .../Fixture/fixture.php.inc | 7 ++- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/rules/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector.php b/rules/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector.php index 64dcc8c8..3cd42875 100644 --- a/rules/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector.php +++ b/rules/DowngradePhp81/Rector/Property/DowngradeReadonlyPropertyRector.php @@ -6,13 +6,16 @@ use PhpParser\Node; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Comments\NodeDocBlock\DocBlockUpdater; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Privatization\NodeManipulator\VisibilityManipulator; use Rector\Rector\AbstractRector; +use Rector\ValueObject\MethodName; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -40,7 +43,7 @@ public function __construct( */ public function getNodeTypes(): array { - return [Property::class, Param::class]; + return [Property::class, ClassMethod::class]; } public function getRuleDefinition(): RuleDefinition @@ -81,30 +84,60 @@ public function __construct() } /** - * @param Property|Param $node + * @param Property|ClassMethod $node */ public function refactor(Node $node): ?Node { - if (! $this->visibilityManipulator->isReadonly($node)) { + if ($node instanceof Property) { + if (! $this->visibilityManipulator->isReadonly($node)) { + return null; + } + + $this->addPhpDocTag($node); + $this->visibilityManipulator->removeReadonly($node); + return $node; + } + + if (! $this->isName($node, MethodName::CONSTRUCT)) { return null; } - $this->addPhpDocTag($node); + $hasChangedDoc = false; + $hasChanged = false; + foreach ($node->params as $param) { + if (! $this->visibilityManipulator->isReadonly($param)) { + continue; + } + + if ($this->addPhpDocTag($param)) { + $hasChangedDoc = true; + } + + $this->visibilityManipulator->removeReadonly($param); + $hasChanged = true; + } - $this->visibilityManipulator->removeReadonly($node); + if (! $hasChanged) { + return null; + } + + if ($hasChangedDoc) { + $node->setAttribute(AttributeKey::ORIGINAL_NODE, null); + } return $node; } - private function addPhpDocTag(Property|Param $node): void + private function addPhpDocTag(Property|Param $node): bool { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); if ($phpDocInfo->hasByName(self::TAGNAME)) { - return; + return false; } $phpDocInfo->addPhpDocTagNode(new PhpDocTagNode('@' . self::TAGNAME, new GenericTagValueNode(''))); $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + return true; } } diff --git a/tests/Issues/DowngradeReadonlyClassPropertyToPhp80/Fixture/fixture.php.inc b/tests/Issues/DowngradeReadonlyClassPropertyToPhp80/Fixture/fixture.php.inc index c8e14e5f..4d2492d3 100644 --- a/tests/Issues/DowngradeReadonlyClassPropertyToPhp80/Fixture/fixture.php.inc +++ b/tests/Issues/DowngradeReadonlyClassPropertyToPhp80/Fixture/fixture.php.inc @@ -17,7 +17,12 @@ namespace Rector\Tests\Issues\DowngradeReadonlyClassPropertyToPhp80\Fixture; final class Fixture { - public function __construct(public array $property) + public function __construct( + /** + * @readonly + */ + public array $property + ) { } }