From 9621a2ee124e8c9c1ebcd69df801bdfcaac86f7e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 10:38:31 +0700 Subject: [PATCH 01/34] [Php82] Add ReadOnlyClassRector --- .../Rector/Class_/ReadOnlyClassRector.php | 79 +++++++++++++++++++ src/ValueObject/PhpVersionFeature.php | 6 ++ 2 files changed, 85 insertions(+) create mode 100644 rules/Php82/Rector/Class_/ReadOnlyClassRector.php diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php new file mode 100644 index 00000000000..7ec2b2aaedb --- /dev/null +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -0,0 +1,79 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if ($this->classAnalyzer->isAnonymousClass($node)) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::READONLY_CLASS; + } +} diff --git a/src/ValueObject/PhpVersionFeature.php b/src/ValueObject/PhpVersionFeature.php index f1f2de0cd4d..af10c45839a 100644 --- a/src/ValueObject/PhpVersionFeature.php +++ b/src/ValueObject/PhpVersionFeature.php @@ -567,6 +567,12 @@ final class PhpVersionFeature */ public const DEPRECATE_DYNAMIC_PROPERTIES = PhpVersion::PHP_82; + /** + * @see https://wiki.php.net/rfc/readonly_classes + * @var int + */ + public const READONLY_CLASS = PhpVersion::PHP_82; + /** * @see https://wiki.php.net/rfc/mixed_type_v2 * @var int From 9aedda73b45397bf4d97db4e27bef014769d8629 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 10:40:07 +0700 Subject: [PATCH 02/34] skip has non-readonly property --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 7ec2b2aaedb..153504a4e1a 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -69,6 +69,13 @@ public function refactor(Node $node): ?Node return null; } + $properties = $node->getProperties(); + foreach ($properties as $property) { + if (! $property->isReadonly()) { + return null; + } + } + return $node; } From 458e0ab5c6040f259e77d221f0cb43163f09c7e3 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 10:43:13 +0700 Subject: [PATCH 03/34] note --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 153504a4e1a..ac068d07a18 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -76,6 +76,9 @@ public function refactor(Node $node): ?Node } } + // property promotion + // has `#[AllowDynamicProperties]` + return $node; } From d695419d7f8ceae02c658fa7172116c79d0359ce Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 10:46:29 +0700 Subject: [PATCH 04/34] note --- .../Rector/Class_/ReadOnlyClassRector.php | 26 ++++++++++++++++--- .../NodeManipulator/VisibilityManipulator.php | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index ac068d07a18..3d531fd1db1 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -9,6 +9,8 @@ use Rector\Core\NodeAnalyzer\ClassAnalyzer; use Rector\Core\Rector\AbstractRector; use Rector\Core\ValueObject\PhpVersionFeature; +use Rector\Core\ValueObject\Visibility; +use Rector\Privatization\NodeManipulator\VisibilityManipulator; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -20,7 +22,10 @@ */ final class ReadOnlyClassRector extends AbstractRector implements MinPhpVersionInterface { - public function __construct(private readonly ClassAnalyzer $classAnalyzer) + public function __construct( + private readonly ClassAnalyzer $classAnalyzer, + private readonly VisibilityManipulator $visibilityManipulator + ) { } @@ -65,21 +70,34 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($this->classAnalyzer->isAnonymousClass($node)) { + if ($this->shouldSkip($node)) { return null; } + $this->visibilityManipulator->changeNodeVisibility($node, Visibility::READONLY); + + // update all properties, both in defined property or in property promotino to not readonly, as class already readonly + + return $node; + } + + private function shouldSkip(Class_ $node): bool + { + if ($this->classAnalyzer->isAnonymousClass($node)) { + return true; + } + $properties = $node->getProperties(); foreach ($properties as $property) { if (! $property->isReadonly()) { - return null; + return true; } } // property promotion // has `#[AllowDynamicProperties]` - return $node; + return false; } public function provideMinPhpVersion(): int diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index b62f4a5c778..0093a73726f 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -78,7 +78,7 @@ public function removeVisibility(ClassMethod | Property | ClassConst $node): voi } } - public function changeNodeVisibility(ClassMethod | Property | ClassConst $node, int $visibility): void + public function changeNodeVisibility(Class_ | ClassMethod | Property | ClassConst $node, int $visibility): void { Assert::oneOf($visibility, [ Visibility::PUBLIC, From fd38da918b95461ae677b4ac1b186fa1bd0b547c Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 10:46:56 +0700 Subject: [PATCH 05/34] note --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 3d531fd1db1..b4561099758 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -25,8 +25,7 @@ final class ReadOnlyClassRector extends AbstractRector implements MinPhpVersionI public function __construct( private readonly ClassAnalyzer $classAnalyzer, private readonly VisibilityManipulator $visibilityManipulator - ) - { + ) { } public function getRuleDefinition(): RuleDefinition @@ -81,6 +80,11 @@ public function refactor(Node $node): ?Node return $node; } + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::READONLY_CLASS; + } + private function shouldSkip(Class_ $node): bool { if ($this->classAnalyzer->isAnonymousClass($node)) { @@ -99,9 +103,4 @@ private function shouldSkip(Class_ $node): bool return false; } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::READONLY_CLASS; - } } From ea45914d81470a9b6e8f278614ca247fac5d39bf Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 12 May 2022 03:49:53 +0000 Subject: [PATCH 06/34] [ci-review] Rector Rectify --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index b4561099758..600b5f0a494 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -17,8 +17,6 @@ /** * @changelog https://wiki.php.net/rfc/readonly_classes - * - * @see \Rector\Tests\Php82\Rector\Class_\ReadOnlyPropertyRector\ReadOnlyClassRectorTest */ final class ReadOnlyClassRector extends AbstractRector implements MinPhpVersionInterface { @@ -85,13 +83,13 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::READONLY_CLASS; } - private function shouldSkip(Class_ $node): bool + private function shouldSkip(Class_ $class): bool { - if ($this->classAnalyzer->isAnonymousClass($node)) { + if ($this->classAnalyzer->isAnonymousClass($class)) { return true; } - $properties = $node->getProperties(); + $properties = $class->getProperties(); foreach ($properties as $property) { if (! $property->isReadonly()) { return true; From e7ff154e5ce03d3acd3c4ecdbcbb00aebcaf30f5 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 10:52:14 +0700 Subject: [PATCH 07/34] skip has AllowDynamicProperties attribute --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 600b5f0a494..a333a729209 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -10,6 +10,7 @@ use Rector\Core\Rector\AbstractRector; use Rector\Core\ValueObject\PhpVersionFeature; use Rector\Core\ValueObject\Visibility; +use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; use Rector\Privatization\NodeManipulator\VisibilityManipulator; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -20,9 +21,15 @@ */ final class ReadOnlyClassRector extends AbstractRector implements MinPhpVersionInterface { + /** + * @var string + */ + private const ATTRIBUTE = 'AllowDynamicProperties'; + public function __construct( private readonly ClassAnalyzer $classAnalyzer, - private readonly VisibilityManipulator $visibilityManipulator + private readonly VisibilityManipulator $visibilityManipulator, + private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer ) { } @@ -97,7 +104,10 @@ private function shouldSkip(Class_ $class): bool } // property promotion - // has `#[AllowDynamicProperties]` + + if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, self::ATTRIBUTE)) { + return true; + } return false; } From ddaec2f23c8563c2c1a797f14273cba8f73d7814 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 10:53:12 +0700 Subject: [PATCH 08/34] skip already readonly --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 4 ++++ rules/Privatization/NodeManipulator/VisibilityManipulator.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index a333a729209..7062f3c28a4 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -92,6 +92,10 @@ public function provideMinPhpVersion(): int private function shouldSkip(Class_ $class): bool { + if ($this->visibilityManipulator->hasVisibility($class, Visibility::READONLY)) { + return true; + } + if ($this->classAnalyzer->isAnonymousClass($class)) { return true; } diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index 0093a73726f..bfa39997d41 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -17,7 +17,7 @@ */ final class VisibilityManipulator { - public function hasVisibility(ClassMethod | Property | ClassConst | Param $node, int $visibility): bool + public function hasVisibility(Class_ | ClassMethod | Property | ClassConst | Param $node, int $visibility): bool { return (bool) ($node->flags & $visibility); } From 9d6224777ebd70fda40c882284d1d80d3f259dbe Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 12 May 2022 03:56:15 +0000 Subject: [PATCH 09/34] [ci-review] Rector Rectify --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 7062f3c28a4..8ba2c12e7b0 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -106,13 +106,7 @@ private function shouldSkip(Class_ $class): bool return true; } } - // property promotion - - if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, self::ATTRIBUTE)) { - return true; - } - - return false; + return $this->phpAttributeAnalyzer->hasPhpAttribute($class, self::ATTRIBUTE); } } From c3bab830670af737b13191379d57211a308585f0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 11:00:53 +0700 Subject: [PATCH 10/34] skip property promotion not readonly --- .../Rector/Class_/ReadOnlyClassRector.php | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 8ba2c12e7b0..4efc10a43cc 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -6,8 +6,10 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassMethod; use Rector\Core\NodeAnalyzer\ClassAnalyzer; use Rector\Core\Rector\AbstractRector; +use Rector\Core\ValueObject\MethodName; use Rector\Core\ValueObject\PhpVersionFeature; use Rector\Core\ValueObject\Visibility; use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; @@ -100,13 +102,28 @@ private function shouldSkip(Class_ $class): bool return true; } + if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, self::ATTRIBUTE)) { + return true; + } + $properties = $class->getProperties(); foreach ($properties as $property) { if (! $property->isReadonly()) { return true; } } - // property promotion - return $this->phpAttributeAnalyzer->hasPhpAttribute($class, self::ATTRIBUTE); + + $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); + if (! $constructClassMethod instanceof ClassMethod) { + return false; + } + + foreach ($constructClassMethod->getParams() as $param) { + if ($param->flags !== 0) { + return true; + } + } + + return false; } } From 8892d68f5e51d4b3bddd5e324ffeac0624129098 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 11:09:50 +0700 Subject: [PATCH 11/34] no params means no property promotion, skip if no property defined --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 4efc10a43cc..63a6cbcc793 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -115,10 +115,17 @@ private function shouldSkip(Class_ $class): bool $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); if (! $constructClassMethod instanceof ClassMethod) { - return false; + // no __construct means no property promotion, skip if no property + return $properties === []; } - foreach ($constructClassMethod->getParams() as $param) { + $params = $constructClassMethod->getParams(); + if ($params === []) { + // no params means no property promotion, skip if no property + return $properties === []; + } + + foreach ($params as $param) { if ($param->flags !== 0) { return true; } From bb3e69ba2c276bd0b4b5a8def0eb391bd5abee5b Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 11:10:26 +0700 Subject: [PATCH 12/34] note --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 63a6cbcc793..b8c8cfa5896 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -126,6 +126,7 @@ private function shouldSkip(Class_ $class): bool } foreach ($params as $param) { + // has non-property promotion, skip if ($param->flags !== 0) { return true; } From ec62cff576413ad22014c7be0c27729c1e8f43d7 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 11:10:59 +0700 Subject: [PATCH 13/34] note --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index b8c8cfa5896..5b5e6de78e5 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -115,13 +115,13 @@ private function shouldSkip(Class_ $class): bool $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); if (! $constructClassMethod instanceof ClassMethod) { - // no __construct means no property promotion, skip if no property + // no __construct means no property promotion, skip if class has no property defined return $properties === []; } $params = $constructClassMethod->getParams(); if ($params === []) { - // no params means no property promotion, skip if no property + // no params means no property promotion, skip if class has no property defined return $properties === []; } From d110c9decdcbaeaf1d146386b962692810849c47 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 12:04:52 +0700 Subject: [PATCH 14/34] skip final class, possibly extendable --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 5b5e6de78e5..253677ec4e5 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -102,6 +102,10 @@ private function shouldSkip(Class_ $class): bool return true; } + if (! $class->isFinal()) { + return true; + } + if ($this->phpAttributeAnalyzer->hasPhpAttribute($class, self::ATTRIBUTE)) { return true; } From f4cae77ad276f8cb3652dc2f2ff00c88e2d2441f Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 12:13:44 +0700 Subject: [PATCH 15/34] add fixture --- config/set/php82.php | 10 ++++++ packages/Set/ValueObject/SetList.php | 5 +++ .../Fixture/skip_already_readonly.php.inc | 9 +++++ .../ReadOnlyClassRectorTest.php | 33 +++++++++++++++++++ .../config/configured_rule.php | 10 ++++++ .../Rector/Class_/ReadOnlyClassRector.php | 21 +++++++++--- 6 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 config/set/php82.php create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/ReadOnlyClassRectorTest.php create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/config/configured_rule.php diff --git a/config/set/php82.php b/config/set/php82.php new file mode 100644 index 00000000000..ea138b88af9 --- /dev/null +++ b/config/set/php82.php @@ -0,0 +1,10 @@ +rule(ReadOnlyClassRector::class); +}; diff --git a/packages/Set/ValueObject/SetList.php b/packages/Set/ValueObject/SetList.php index b23f4b362b1..37359fbc2bf 100644 --- a/packages/Set/ValueObject/SetList.php +++ b/packages/Set/ValueObject/SetList.php @@ -128,6 +128,11 @@ final class SetList implements SetListInterface */ public const PHP_81 = __DIR__ . '/../../../config/set/php81.php'; + /** + * @var string + */ + public const PHP_82 = __DIR__ . '/../../../config/set/php82.php'; + /** * @var string */ diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc new file mode 100644 index 00000000000..48792dc3c3a --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc @@ -0,0 +1,9 @@ +doTestFileInfo($fileInfo); + } + + /** + * @return Iterator + */ + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/config/configured_rule.php b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/config/configured_rule.php new file mode 100644 index 00000000000..ea138b88af9 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ReadOnlyClassRector::class); +}; diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 253677ec4e5..55ec68d5716 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Property; use Rector\Core\NodeAnalyzer\ClassAnalyzer; use Rector\Core\Rector\AbstractRector; use Rector\Core\ValueObject\MethodName; @@ -111,10 +112,8 @@ private function shouldSkip(Class_ $class): bool } $properties = $class->getProperties(); - foreach ($properties as $property) { - if (! $property->isReadonly()) { - return true; - } + if ($this->hasWritableProperty($properties)) { + return true; } $constructClassMethod = $class->getMethod(MethodName::CONSTRUCT); @@ -138,4 +137,18 @@ private function shouldSkip(Class_ $class): bool return false; } + + /** + * @param Property[] $properties + */ + private function hasWritableProperty(array $properties): bool + { + foreach ($properties as $property) { + if (! $property->isReadonly()) { + return true; + } + } + + return false; + } } From 2252bb1ecfb7ef34d33e9e87c271cc24211b1ea0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 12:14:18 +0700 Subject: [PATCH 16/34] add @see --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 55ec68d5716..52b7eb1e412 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -21,6 +21,8 @@ /** * @changelog https://wiki.php.net/rfc/readonly_classes + * + * @see \Rector\Tests\Php82\Rector\Class_\ReadOnlyClassRector\ReadOnlyClassRectorTest */ final class ReadOnlyClassRector extends AbstractRector implements MinPhpVersionInterface { From c718d39ce7c74133bef7460586c1e6099a157126 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 12:30:58 +0700 Subject: [PATCH 17/34] visibility union ndoe rules/Privatization/NodeManipulator/VisibilityManipulator.php --- .../Fixture/skip_already_readonly.php.inc | 2 +- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 2 +- .../Privatization/NodeManipulator/VisibilityManipulator.php | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc index 48792dc3c3a..51f884c8372 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc @@ -4,6 +4,6 @@ namespace Rector\Tests\Php82\Rector\Class_\ReadOnlyClassRector\Fixture; use MyCLabs\Enum\Enum; -readonly class SkipAlreadyReadonly +class SkipAlreadyReadonly { } diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 52b7eb1e412..42288692286 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -80,7 +80,7 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { if ($this->shouldSkip($node)) { - return null; + //return null; } $this->visibilityManipulator->changeNodeVisibility($node, Visibility::READONLY); diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index bfa39997d41..ce6c37be1ec 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -58,7 +58,7 @@ public function makeNonFinal(Class_ | ClassMethod $node): void /** * This way "abstract", "static", "final" are kept */ - public function removeVisibility(ClassMethod | Property | ClassConst $node): void + public function removeVisibility(Class_ | ClassMethod | Property | ClassConst $node): void { // no modifier if ($node->flags === 0) { @@ -87,6 +87,7 @@ public function changeNodeVisibility(Class_ | ClassMethod | Property | ClassCons Visibility::STATIC, Visibility::ABSTRACT, Visibility::FINAL, + Visibility::READONLY ]); $this->replaceVisibilityFlag($node, $visibility); @@ -146,7 +147,7 @@ private function removeVisibilityFlag( $node->flags &= ~$visibility; } - private function replaceVisibilityFlag(ClassMethod | Property | ClassConst $node, int $visibility): void + private function replaceVisibilityFlag(Class_ | ClassMethod | Property | ClassConst $node, int $visibility): void { $isStatic = $node instanceof ClassMethod && $node->isStatic(); if ($isStatic) { From 01f8f5de76c5d982b01e5a674267153823adcd83 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 12:31:08 +0700 Subject: [PATCH 18/34] visibility union ndoe rules/Privatization/NodeManipulator/VisibilityManipulator.php --- .../ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc | 2 +- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc index 51f884c8372..48792dc3c3a 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc @@ -4,6 +4,6 @@ namespace Rector\Tests\Php82\Rector\Class_\ReadOnlyClassRector\Fixture; use MyCLabs\Enum\Enum; -class SkipAlreadyReadonly +readonly class SkipAlreadyReadonly { } diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 42288692286..52b7eb1e412 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -80,7 +80,7 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { if ($this->shouldSkip($node)) { - //return null; + return null; } $this->visibilityManipulator->changeNodeVisibility($node, Visibility::READONLY); From 857bf256e0ffdd8889854cc4567f8e8eb1d6c4c6 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 12:33:49 +0700 Subject: [PATCH 19/34] remove already readonly fixture --- .../Fixture/skip_already_readonly.php.inc | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc deleted file mode 100644 index 48792dc3c3a..00000000000 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_already_readonly.php.inc +++ /dev/null @@ -1,9 +0,0 @@ - Date: Thu, 12 May 2022 12:35:05 +0700 Subject: [PATCH 20/34] comment --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 52b7eb1e412..2648a21d541 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -97,6 +97,7 @@ public function provideMinPhpVersion(): int private function shouldSkip(Class_ $class): bool { + // need to have test fixture once feature added to nikic/PHP-Parser if ($this->visibilityManipulator->hasVisibility($class, Visibility::READONLY)) { return true; } From 1e57d1493bf398f84952217a90a0c0c72b2e397e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 12:36:15 +0700 Subject: [PATCH 21/34] skip anonymous class fixture --- .../Fixture/skip_anonymous_class.php.inc | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc new file mode 100644 index 00000000000..d62aa5c0b05 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc @@ -0,0 +1,13 @@ + Date: Thu, 12 May 2022 12:37:02 +0700 Subject: [PATCH 22/34] skip non-final class fixture --- .../Fixture/skip_non_final_class.php.inc | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc new file mode 100644 index 00000000000..27e4887fad4 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc @@ -0,0 +1,8 @@ + Date: Thu, 12 May 2022 12:39:36 +0700 Subject: [PATCH 23/34] skip allow dynamic fixture --- .../Fixture/skip_allow_dynamic.php.inc | 9 ++++++++ .../NodeManipulator/VisibilityManipulator.php | 22 ++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc new file mode 100644 index 00000000000..6dd3a55cda6 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc @@ -0,0 +1,9 @@ +flags === 0) { + if ($node instanceof Class_) { + $node->flags -= Class_::MODIFIER_READONLY; + } + return; } - if ($node->isPublic()) { - $node->flags -= Class_::MODIFIER_PUBLIC; - } + if (! $node instanceof Class_) { + if ($node->isPublic()) { + $node->flags -= Class_::MODIFIER_PUBLIC; + } - if ($node->isProtected()) { - $node->flags -= Class_::MODIFIER_PROTECTED; - } + if ($node->isProtected()) { + $node->flags -= Class_::MODIFIER_PROTECTED; + } - if ($node->isPrivate()) { - $node->flags -= Class_::MODIFIER_PRIVATE; + if ($node->isPrivate()) { + $node->flags -= Class_::MODIFIER_PRIVATE; + } } } From 43f765ac6e4ddc062af9fbb2cc9d74d2070135b3 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 15:15:48 +0700 Subject: [PATCH 24/34] class check --- .../NodeManipulator/VisibilityManipulator.php | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index 3fde4178297..d68ba358352 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -60,27 +60,26 @@ public function makeNonFinal(Class_ | ClassMethod $node): void */ public function removeVisibility(Class_ | ClassMethod | Property | ClassConst $node): void { + if ($node instanceof Class_ && $node->flags === 0) { + $node->flags -= Class_::MODIFIER_READONLY; + return; + } + // no modifier if ($node->flags === 0) { - if ($node instanceof Class_) { - $node->flags -= Class_::MODIFIER_READONLY; - } - return; } - if (! $node instanceof Class_) { - if ($node->isPublic()) { - $node->flags -= Class_::MODIFIER_PUBLIC; - } + if ($node->isPublic()) { + $node->flags -= Class_::MODIFIER_PUBLIC; + } - if ($node->isProtected()) { - $node->flags -= Class_::MODIFIER_PROTECTED; - } + if ($node->isProtected()) { + $node->flags -= Class_::MODIFIER_PROTECTED; + } - if ($node->isPrivate()) { - $node->flags -= Class_::MODIFIER_PRIVATE; - } + if ($node->isPrivate()) { + $node->flags -= Class_::MODIFIER_PRIVATE; } } From 2636f90ae3888982e299cb43acba62755f040c63 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 15:16:43 +0700 Subject: [PATCH 25/34] skip has writable property fixture --- .../Fixture/skip_has_writable_property.php.inc | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc new file mode 100644 index 00000000000..c9c557bcae4 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc @@ -0,0 +1,8 @@ + Date: Thu, 12 May 2022 15:18:01 +0700 Subject: [PATCH 26/34] skip no properties --- .../Fixture/skip_no_properties.php.inc | 7 +++++++ .../Fixture/skip_no_properties_2.php.inc | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties.php.inc create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties_2.php.inc diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties.php.inc new file mode 100644 index 00000000000..f160c6b35c9 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties.php.inc @@ -0,0 +1,7 @@ + Date: Thu, 12 May 2022 15:19:01 +0700 Subject: [PATCH 27/34] skip property promotion writable --- .../Fixture/skip_property_promotion_writable.php.inc | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc new file mode 100644 index 00000000000..8622874ab3d --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc @@ -0,0 +1,10 @@ + Date: Thu, 12 May 2022 15:28:11 +0700 Subject: [PATCH 28/34] debug --- .../Fixture/only_readonly_property.php.inc | 21 ++++++++++++++++ .../Fixture/only_readonly_property2.php.inc | 25 +++++++++++++++++++ .../Rector/Class_/ReadOnlyClassRector.php | 15 ++++++++--- .../NodeManipulator/VisibilityManipulator.php | 4 +++ 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc create mode 100644 rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc new file mode 100644 index 00000000000..526157f2119 --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc @@ -0,0 +1,21 @@ + +----- + \ No newline at end of file diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc new file mode 100644 index 00000000000..b5eb2a67bfb --- /dev/null +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc @@ -0,0 +1,25 @@ + +----- + \ No newline at end of file diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 2648a21d541..5c65b55e1de 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -9,6 +9,7 @@ use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; use Rector\Core\NodeAnalyzer\ClassAnalyzer; +use Rector\Core\NodeAnalyzer\PromotedPropertyParamCleaner; use Rector\Core\Rector\AbstractRector; use Rector\Core\ValueObject\MethodName; use Rector\Core\ValueObject\PhpVersionFeature; @@ -34,7 +35,8 @@ final class ReadOnlyClassRector extends AbstractRector implements MinPhpVersionI public function __construct( private readonly ClassAnalyzer $classAnalyzer, private readonly VisibilityManipulator $visibilityManipulator, - private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer + private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer, + private readonly PromotedPropertyParamCleaner $promotedPropertyParamCleaner ) { } @@ -85,7 +87,14 @@ public function refactor(Node $node): ?Node $this->visibilityManipulator->changeNodeVisibility($node, Visibility::READONLY); - // update all properties, both in defined property or in property promotino to not readonly, as class already readonly + $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); + if ($constructClassMethod instanceof ClassMethod) {die; + $this->promotedPropertyParamCleaner->cleanFromFlags($constructClassMethod->getParams()); + } + + foreach ($node->getProperties() as $property) { + $this->visibilityManipulator->removeReadonly($property); + } return $node; } @@ -133,7 +142,7 @@ private function shouldSkip(Class_ $class): bool foreach ($params as $param) { // has non-property promotion, skip - if ($param->flags !== 0) { + if (! $this->visibilityManipulator->hasVisibility($param, Visibility::READONLY)) { return true; } } diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index d68ba358352..83897df7e2f 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -65,6 +65,10 @@ public function removeVisibility(Class_ | ClassMethod | Property | ClassConst $n return; } + if ($node instanceof Class_) { + return; + } + // no modifier if ($node->flags === 0) { return; From 605a1beba18f8a348f18032b49f9afe7ed62301e Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 12 May 2022 08:32:03 +0000 Subject: [PATCH 29/34] [ci-review] Rector Rectify --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 3 ++- rules/Privatization/NodeManipulator/VisibilityManipulator.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 5c65b55e1de..cfbc1c89a90 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -88,7 +88,8 @@ public function refactor(Node $node): ?Node $this->visibilityManipulator->changeNodeVisibility($node, Visibility::READONLY); $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); - if ($constructClassMethod instanceof ClassMethod) {die; + if ($constructClassMethod instanceof ClassMethod) { + die; $this->promotedPropertyParamCleaner->cleanFromFlags($constructClassMethod->getParams()); } diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index 83897df7e2f..21e521bba0e 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -96,7 +96,7 @@ public function changeNodeVisibility(Class_ | ClassMethod | Property | ClassCons Visibility::STATIC, Visibility::ABSTRACT, Visibility::FINAL, - Visibility::READONLY + Visibility::READONLY, ]); $this->replaceVisibilityFlag($node, $visibility); From a13123fe506ab6ee19613a297c4557d189cdcfbe Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 15:37:38 +0700 Subject: [PATCH 30/34] fix --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 9 ++++----- .../NodeManipulator/VisibilityManipulator.php | 13 ++----------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index cfbc1c89a90..714fc406c7d 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -9,7 +9,6 @@ use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; use Rector\Core\NodeAnalyzer\ClassAnalyzer; -use Rector\Core\NodeAnalyzer\PromotedPropertyParamCleaner; use Rector\Core\Rector\AbstractRector; use Rector\Core\ValueObject\MethodName; use Rector\Core\ValueObject\PhpVersionFeature; @@ -35,8 +34,7 @@ final class ReadOnlyClassRector extends AbstractRector implements MinPhpVersionI public function __construct( private readonly ClassAnalyzer $classAnalyzer, private readonly VisibilityManipulator $visibilityManipulator, - private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer, - private readonly PromotedPropertyParamCleaner $promotedPropertyParamCleaner + private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer ) { } @@ -89,8 +87,9 @@ public function refactor(Node $node): ?Node $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); if ($constructClassMethod instanceof ClassMethod) { - die; - $this->promotedPropertyParamCleaner->cleanFromFlags($constructClassMethod->getParams()); + foreach ($constructClassMethod->getParams() as $param) { + $this->visibilityManipulator->removeReadonly($param); + } } foreach ($node->getProperties() as $property) { diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index 21e521bba0e..6e36b8d42b5 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -58,17 +58,8 @@ public function makeNonFinal(Class_ | ClassMethod $node): void /** * This way "abstract", "static", "final" are kept */ - public function removeVisibility(Class_ | ClassMethod | Property | ClassConst $node): void + public function removeVisibility(ClassMethod | Property | ClassConst $node): void { - if ($node instanceof Class_ && $node->flags === 0) { - $node->flags -= Class_::MODIFIER_READONLY; - return; - } - - if ($node instanceof Class_) { - return; - } - // no modifier if ($node->flags === 0) { return; @@ -163,7 +154,7 @@ private function replaceVisibilityFlag(Class_ | ClassMethod | Property | ClassCo $this->makeNonStatic($node); } - if ($visibility !== Visibility::STATIC && $visibility !== Visibility::ABSTRACT && $visibility !== Visibility::FINAL) { + if ($visibility !== Visibility::STATIC && $visibility !== Visibility::ABSTRACT && $visibility !== Visibility::FINAL && $visibility !== Visibility::READONLY) { $this->removeVisibility($node); } From 6ff7f94beaa6030b27f6d68515dac910a1b4a237 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 15:38:48 +0700 Subject: [PATCH 31/34] eol --- .../ReadOnlyClassRector/Fixture/only_readonly_property.php.inc | 2 +- .../ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc | 2 +- .../ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc | 2 +- .../ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc | 2 +- .../Fixture/skip_has_writable_property.php.inc | 2 +- .../ReadOnlyClassRector/Fixture/skip_no_properties.php.inc | 2 +- .../ReadOnlyClassRector/Fixture/skip_no_properties_2.php.inc | 2 +- .../ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc | 2 +- .../Fixture/skip_property_promotion_writable.php.inc | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc index 526157f2119..66c29134d1a 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property.php.inc @@ -18,4 +18,4 @@ final readonly class OnlyReadonlyProperty private string $property; } -?> \ No newline at end of file +?> diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc index b5eb2a67bfb..650ad8e847a 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/only_readonly_property2.php.inc @@ -22,4 +22,4 @@ final readonly class OnlyReadonlyProperty2 } } -?> \ No newline at end of file +?> diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc index 6dd3a55cda6..fa6e6368a05 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_allow_dynamic.php.inc @@ -6,4 +6,4 @@ namespace Rector\Tests\Php82\Rector\Class_\ReadOnlyClassRector\Fixture; final class SkipAllowDynamic { private readonly string $property; -} \ No newline at end of file +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc index d62aa5c0b05..be3ad853763 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_anonymous_class.php.inc @@ -10,4 +10,4 @@ class SkipAnonymousClass private readonly string $foo; }; } -} \ No newline at end of file +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc index c9c557bcae4..3d0d13296f8 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_has_writable_property.php.inc @@ -5,4 +5,4 @@ namespace Rector\Tests\Php82\Rector\Class_\ReadOnlyClassRector\Fixture; final class SkipHasWritableProperty { private string $property; -} \ No newline at end of file +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties.php.inc index f160c6b35c9..d594fbcf396 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties.php.inc @@ -4,4 +4,4 @@ namespace Rector\Tests\Php82\Rector\Class_\ReadOnlyClassRector\Fixture; final class SkipNoProperties { -} \ No newline at end of file +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties_2.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties_2.php.inc index 8058bace73b..30ba64bd251 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties_2.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_no_properties_2.php.inc @@ -7,4 +7,4 @@ final class SkipNoProperties2 public function __construct() { } -} \ No newline at end of file +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc index 27e4887fad4..f14c72af9f2 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_non_final_class.php.inc @@ -5,4 +5,4 @@ namespace Rector\Tests\Php82\Rector\Class_\ReadOnlyClassRector\Fixture; class SkipNonFinalClass { private readonly string $property; -} \ No newline at end of file +} diff --git a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc index 8622874ab3d..543d00b9d1d 100644 --- a/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc +++ b/rules-tests/Php82/Rector/Class_/ReadOnlyClassRector/Fixture/skip_property_promotion_writable.php.inc @@ -7,4 +7,4 @@ final class SkipPropertyPromotionWritable public function __construct(private string $data) { } -} \ No newline at end of file +} From 322c13d22d31da64404421f56ee0d8d1dc7ec72e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 15:42:34 +0700 Subject: [PATCH 32/34] fix --- rules/Php82/Rector/Class_/ReadOnlyClassRector.php | 2 +- .../NodeManipulator/VisibilityManipulator.php | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php index 714fc406c7d..074d116d69b 100644 --- a/rules/Php82/Rector/Class_/ReadOnlyClassRector.php +++ b/rules/Php82/Rector/Class_/ReadOnlyClassRector.php @@ -83,7 +83,7 @@ public function refactor(Node $node): ?Node return null; } - $this->visibilityManipulator->changeNodeVisibility($node, Visibility::READONLY); + $this->visibilityManipulator->makeReadonly($node); $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT); if ($constructClassMethod instanceof ClassMethod) { diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index 6e36b8d42b5..b61f9bd3638 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -78,7 +78,7 @@ public function removeVisibility(ClassMethod | Property | ClassConst $node): voi } } - public function changeNodeVisibility(Class_ | ClassMethod | Property | ClassConst $node, int $visibility): void + public function changeNodeVisibility(ClassMethod | Property | ClassConst $node, int $visibility): void { Assert::oneOf($visibility, [ Visibility::PUBLIC, @@ -86,8 +86,7 @@ public function changeNodeVisibility(Class_ | ClassMethod | Property | ClassCons Visibility::PRIVATE, Visibility::STATIC, Visibility::ABSTRACT, - Visibility::FINAL, - Visibility::READONLY, + Visibility::FINAL ]); $this->replaceVisibilityFlag($node, $visibility); @@ -118,7 +117,7 @@ public function removeAbstract(ClassMethod $classMethod): void $classMethod->flags -= Class_::MODIFIER_ABSTRACT; } - public function makeReadonly(Property | Param $node): void + public function makeReadonly(Class_ | Property | Param $node): void { $this->addVisibilityFlag($node, Visibility::READONLY); } @@ -147,14 +146,14 @@ private function removeVisibilityFlag( $node->flags &= ~$visibility; } - private function replaceVisibilityFlag(Class_ | ClassMethod | Property | ClassConst $node, int $visibility): void + private function replaceVisibilityFlag(ClassMethod | Property | ClassConst $node, int $visibility): void { $isStatic = $node instanceof ClassMethod && $node->isStatic(); if ($isStatic) { $this->makeNonStatic($node); } - if ($visibility !== Visibility::STATIC && $visibility !== Visibility::ABSTRACT && $visibility !== Visibility::FINAL && $visibility !== Visibility::READONLY) { + if ($visibility !== Visibility::STATIC && $visibility !== Visibility::ABSTRACT && $visibility !== Visibility::FINAL) { $this->removeVisibility($node); } From a9de1aed8a8448636799a18057f507bf97a1e159 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 12 May 2022 08:43:51 +0000 Subject: [PATCH 33/34] [ci-review] Rector Rectify --- rules/Privatization/NodeManipulator/VisibilityManipulator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/Privatization/NodeManipulator/VisibilityManipulator.php b/rules/Privatization/NodeManipulator/VisibilityManipulator.php index b61f9bd3638..6a947ed66ef 100644 --- a/rules/Privatization/NodeManipulator/VisibilityManipulator.php +++ b/rules/Privatization/NodeManipulator/VisibilityManipulator.php @@ -86,7 +86,7 @@ public function changeNodeVisibility(ClassMethod | Property | ClassConst $node, Visibility::PRIVATE, Visibility::STATIC, Visibility::ABSTRACT, - Visibility::FINAL + Visibility::FINAL, ]); $this->replaceVisibilityFlag($node, $visibility); From 5d36a7381b0da1063ac6c29be2c2ce1bc8157737 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 12 May 2022 16:53:34 +0700 Subject: [PATCH 34/34] final touch: add up-to-php82 level setlist --- config/set/level/up-to-php82.php | 15 +++++++++++++++ packages/Set/ValueObject/LevelSetList.php | 5 +++++ 2 files changed, 20 insertions(+) create mode 100644 config/set/level/up-to-php82.php diff --git a/config/set/level/up-to-php82.php b/config/set/level/up-to-php82.php new file mode 100644 index 00000000000..f0ac46bd709 --- /dev/null +++ b/config/set/level/up-to-php82.php @@ -0,0 +1,15 @@ +sets([SetList::PHP_82, LevelSetList::UP_TO_PHP_81]); + + // parameter must be defined after import, to override imported param version + $rectorConfig->phpVersion(PhpVersion::PHP_82); +}; diff --git a/packages/Set/ValueObject/LevelSetList.php b/packages/Set/ValueObject/LevelSetList.php index 64c417d7e36..0eb19fe0c5a 100644 --- a/packages/Set/ValueObject/LevelSetList.php +++ b/packages/Set/ValueObject/LevelSetList.php @@ -8,6 +8,11 @@ final class LevelSetList implements SetListInterface { + /** + * @var string + */ + public const UP_TO_PHP_82 = __DIR__ . '/../../../config/set/level/up-to-php82.php'; + /** * @var string */