From 396aca417145af18c59e5d36c6b5583ff9b84196 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Oct 2026 21:45:37 +0700 Subject: [PATCH 1/4] Proper fix for ClassReflection refresh (part 2) --- rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php | 7 +++++++ .../Source/AddTraitByReflectionRector.php | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php b/rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php index 5e8723881e4..f96422e6dd0 100644 --- a/rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php +++ b/rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php @@ -64,6 +64,13 @@ public function narrowToFullyQualifiedOrAliasedObjectType( } if ($this->reflectionProvider->hasClass($className)) { + // keep the scope class reflection of the current class, as it is refreshed on class change, + // while the reflection provider one still reads the original file + $classReflection = $scope?->getClassReflection(); + if ($classReflection instanceof ClassReflection && $classReflection->getName() === $className) { + return new FullyQualifiedObjectType($className, null, $classReflection); + } + return new FullyQualifiedObjectType($className); } diff --git a/tests/Issues/ClassReflectionRefresh/Source/AddTraitByReflectionRector.php b/tests/Issues/ClassReflectionRefresh/Source/AddTraitByReflectionRector.php index bad1968199b..cf866a57485 100644 --- a/tests/Issues/ClassReflectionRefresh/Source/AddTraitByReflectionRector.php +++ b/tests/Issues/ClassReflectionRefresh/Source/AddTraitByReflectionRector.php @@ -47,7 +47,16 @@ public function refactor(Node $node): ?Class_ return null; } - if ($classReflection->hasTraitUse(SomeTrait::class)) { + // both the scope and the resolved type must see the refreshed class reflection + $typeClassReflection = $this->getType($node) + ->getObjectClassReflections()[0] ?? null; + + $hasTraitInScope = $classReflection->hasTraitUse(SomeTrait::class); + $hasTraitInType = $typeClassReflection instanceof ClassReflection && $typeClassReflection->hasTraitUse( + SomeTrait::class + ); + + if ($hasTraitInScope && $hasTraitInType) { return null; } From 62bc9e8970e5f1b442b16af04efb942d93aac201 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Oct 2026 21:48:34 +0700 Subject: [PATCH 2/4] update test --- .../Fixture/add_trait_once.php.inc | 2 +- .../add_trait_with_imported_parent.php.inc | 2 +- .../skip_already_used_imported_trait.php.inc | 10 ---- .../Source/AddSomeTraitRector.php | 46 +++++++++++++++++++ ...php => FinalizeOnReflectedTraitRector.php} | 30 ++++++------ .../config/configured_rule.php | 5 +- 6 files changed, 66 insertions(+), 29 deletions(-) delete mode 100644 tests/Issues/ClassReflectionRefresh/Fixture/skip_already_used_imported_trait.php.inc create mode 100644 tests/Issues/ClassReflectionRefresh/Source/AddSomeTraitRector.php rename tests/Issues/ClassReflectionRefresh/Source/{AddTraitByReflectionRector.php => FinalizeOnReflectedTraitRector.php} (53%) diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc index dae3faf52ce..865e684b6f0 100644 --- a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc +++ b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Issues\ClassReflectionRefresh\Fixture; -final class AddTraitOnce +class AddTraitOnce { public function run() { diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc index ea21734913f..aa6ec356add 100644 --- a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc +++ b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc @@ -4,7 +4,7 @@ namespace Rector\Tests\Issues\ClassReflectionRefresh\Fixture; use Rector\Tests\Issues\ClassReflectionRefresh\Source\SomeParentClass; -final class AddTraitWithImportedParent extends SomeParentClass +class AddTraitWithImportedParent extends SomeParentClass { public function run(): SomeParentClass { diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/skip_already_used_imported_trait.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/skip_already_used_imported_trait.php.inc deleted file mode 100644 index 80669bd0bfb..00000000000 --- a/tests/Issues/ClassReflectionRefresh/Fixture/skip_already_used_imported_trait.php.inc +++ /dev/null @@ -1,10 +0,0 @@ -> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + foreach ($node->getTraitUses() as $traitUse) { + foreach ($traitUse->traits as $trait) { + if ($this->isName($trait, SomeTrait::class)) { + return null; + } + } + } + + $node->stmts = array_merge([new TraitUse([new FullyQualified(SomeTrait::class)])], $node->stmts); + + return $node; + } +} diff --git a/tests/Issues/ClassReflectionRefresh/Source/AddTraitByReflectionRector.php b/tests/Issues/ClassReflectionRefresh/Source/FinalizeOnReflectedTraitRector.php similarity index 53% rename from tests/Issues/ClassReflectionRefresh/Source/AddTraitByReflectionRector.php rename to tests/Issues/ClassReflectionRefresh/Source/FinalizeOnReflectedTraitRector.php index cf866a57485..236beed669d 100644 --- a/tests/Issues/ClassReflectionRefresh/Source/AddTraitByReflectionRector.php +++ b/tests/Issues/ClassReflectionRefresh/Source/FinalizeOnReflectedTraitRector.php @@ -4,20 +4,19 @@ namespace Rector\Tests\Issues\ClassReflectionRefresh\Source; +use PhpParser\Modifiers; use PhpParser\Node; -use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\TraitUse; use PHPStan\Reflection\ClassReflection; use Rector\Rector\AbstractRector; use Rector\Reflection\ReflectionResolver; use Rector\RuleDoc\RuleDefinition; /** - * Decides on the class reflection only, so it adds the trait again on every pass - * unless the class reflection reflects the changed class node + * Sees the trait added by AddSomeTraitRector only when the class reflection reflects the changed class node, + * both on the scope and on the resolved type */ -final class AddTraitByReflectionRector extends AbstractRector +final class FinalizeOnReflectedTraitRector extends AbstractRector { public function __construct( private readonly ReflectionResolver $reflectionResolver @@ -42,25 +41,26 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Class_ { - $classReflection = $this->reflectionResolver->resolveClassReflection($node); - if (! $classReflection instanceof ClassReflection) { + if ($node->isFinal()) { + return null; + } + + $scopeClassReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $scopeClassReflection instanceof ClassReflection || ! $scopeClassReflection->hasTraitUse( + SomeTrait::class + )) { return null; } - // both the scope and the resolved type must see the refreshed class reflection $typeClassReflection = $this->getType($node) ->getObjectClassReflections()[0] ?? null; - - $hasTraitInScope = $classReflection->hasTraitUse(SomeTrait::class); - $hasTraitInType = $typeClassReflection instanceof ClassReflection && $typeClassReflection->hasTraitUse( + if (! $typeClassReflection instanceof ClassReflection || ! $typeClassReflection->hasTraitUse( SomeTrait::class - ); - - if ($hasTraitInScope && $hasTraitInType) { + )) { return null; } - $node->stmts = array_merge([new TraitUse([new FullyQualified(SomeTrait::class)])], $node->stmts); + $node->flags |= Modifiers::FINAL; return $node; } diff --git a/tests/Issues/ClassReflectionRefresh/config/configured_rule.php b/tests/Issues/ClassReflectionRefresh/config/configured_rule.php index 5d63201ac51..c7746fa1d45 100644 --- a/tests/Issues/ClassReflectionRefresh/config/configured_rule.php +++ b/tests/Issues/ClassReflectionRefresh/config/configured_rule.php @@ -3,7 +3,8 @@ declare(strict_types=1); use Rector\Config\RectorConfig; -use Rector\Tests\Issues\ClassReflectionRefresh\Source\AddTraitByReflectionRector; +use Rector\Tests\Issues\ClassReflectionRefresh\Source\AddSomeTraitRector; +use Rector\Tests\Issues\ClassReflectionRefresh\Source\FinalizeOnReflectedTraitRector; return RectorConfig::configure() - ->withRules([AddTraitByReflectionRector::class]); + ->withRules([AddSomeTraitRector::class, FinalizeOnReflectedTraitRector::class]); From 5d94a90bfcd2420c241319acd4fc89bb6825993f Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Oct 2026 21:51:42 +0700 Subject: [PATCH 3/4] fix --- .../add_trait_with_imported_parent.php.inc | 32 ------------------- ...hp.inc => refresh_on_class_change.php.inc} | 5 ++- ...ass_change_with_imported_interface.php.inc | 31 ++++++++++++++++++ ...tor.php => FinalizeByReflectionRector.php} | 23 +++++++------ ... => ReadonlyOnFinalByReflectionRector.php} | 20 +++++------- .../{SomeTrait.php => SomeInterface.php} | 2 +- .../Source/SomeParentClass.php | 9 ------ .../config/configured_rule.php | 6 ++-- 8 files changed, 58 insertions(+), 70 deletions(-) delete mode 100644 tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc rename tests/Issues/ClassReflectionRefresh/Fixture/{add_trait_once.php.inc => refresh_on_class_change.php.inc} (67%) create mode 100644 tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change_with_imported_interface.php.inc rename tests/Issues/ClassReflectionRefresh/Source/{AddSomeTraitRector.php => FinalizeByReflectionRector.php} (53%) rename tests/Issues/ClassReflectionRefresh/Source/{FinalizeOnReflectedTraitRector.php => ReadonlyOnFinalByReflectionRector.php} (73%) rename tests/Issues/ClassReflectionRefresh/Source/{SomeTrait.php => SomeInterface.php} (80%) delete mode 100644 tests/Issues/ClassReflectionRefresh/Source/SomeParentClass.php diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc deleted file mode 100644 index aa6ec356add..00000000000 --- a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc +++ /dev/null @@ -1,32 +0,0 @@ - ------ - diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change.php.inc similarity index 67% rename from tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc rename to tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change.php.inc index 865e684b6f0..3164f5893bb 100644 --- a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc +++ b/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Issues\ClassReflectionRefresh\Fixture; -class AddTraitOnce +class RefreshOnClassChange { public function run() { @@ -15,9 +15,8 @@ class AddTraitOnce namespace Rector\Tests\Issues\ClassReflectionRefresh\Fixture; -final class AddTraitOnce +final readonly class RefreshOnClassChange { - use \Rector\Tests\Issues\ClassReflectionRefresh\Source\SomeTrait; public function run() { } diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change_with_imported_interface.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change_with_imported_interface.php.inc new file mode 100644 index 00000000000..15fb3e8c9c2 --- /dev/null +++ b/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change_with_imported_interface.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/tests/Issues/ClassReflectionRefresh/Source/AddSomeTraitRector.php b/tests/Issues/ClassReflectionRefresh/Source/FinalizeByReflectionRector.php similarity index 53% rename from tests/Issues/ClassReflectionRefresh/Source/AddSomeTraitRector.php rename to tests/Issues/ClassReflectionRefresh/Source/FinalizeByReflectionRector.php index 2e60dfd25b2..58fb090c710 100644 --- a/tests/Issues/ClassReflectionRefresh/Source/AddSomeTraitRector.php +++ b/tests/Issues/ClassReflectionRefresh/Source/FinalizeByReflectionRector.php @@ -4,15 +4,21 @@ namespace Rector\Tests\Issues\ClassReflectionRefresh\Source; +use PhpParser\Modifiers; use PhpParser\Node; -use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\TraitUse; +use PHPStan\Reflection\ClassReflection; use Rector\Rector\AbstractRector; +use Rector\Reflection\ReflectionResolver; use Rector\RuleDoc\RuleDefinition; -final class AddSomeTraitRector extends AbstractRector +final class FinalizeByReflectionRector extends AbstractRector { + public function __construct( + private readonly ReflectionResolver $reflectionResolver + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('', []); @@ -31,15 +37,12 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Class_ { - foreach ($node->getTraitUses() as $traitUse) { - foreach ($traitUse->traits as $trait) { - if ($this->isName($trait, SomeTrait::class)) { - return null; - } - } + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection || $classReflection->isFinal()) { + return null; } - $node->stmts = array_merge([new TraitUse([new FullyQualified(SomeTrait::class)])], $node->stmts); + $node->flags |= Modifiers::FINAL; return $node; } diff --git a/tests/Issues/ClassReflectionRefresh/Source/FinalizeOnReflectedTraitRector.php b/tests/Issues/ClassReflectionRefresh/Source/ReadonlyOnFinalByReflectionRector.php similarity index 73% rename from tests/Issues/ClassReflectionRefresh/Source/FinalizeOnReflectedTraitRector.php rename to tests/Issues/ClassReflectionRefresh/Source/ReadonlyOnFinalByReflectionRector.php index 236beed669d..2fd48fad897 100644 --- a/tests/Issues/ClassReflectionRefresh/Source/FinalizeOnReflectedTraitRector.php +++ b/tests/Issues/ClassReflectionRefresh/Source/ReadonlyOnFinalByReflectionRector.php @@ -13,10 +13,10 @@ use Rector\RuleDoc\RuleDefinition; /** - * Sees the trait added by AddSomeTraitRector only when the class reflection reflects the changed class node, - * both on the scope and on the resolved type + * Sees the final added by FinalizeByReflectionRector only when the class reflection is refreshed + * after the class node changed, both on the scope and on the resolved type */ -final class FinalizeOnReflectedTraitRector extends AbstractRector +final class ReadonlyOnFinalByReflectionRector extends AbstractRector { public function __construct( private readonly ReflectionResolver $reflectionResolver @@ -41,26 +41,22 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Class_ { - if ($node->isFinal()) { + $scopeClassReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $scopeClassReflection instanceof ClassReflection || ! $scopeClassReflection->isFinal()) { return null; } - $scopeClassReflection = $this->reflectionResolver->resolveClassReflection($node); - if (! $scopeClassReflection instanceof ClassReflection || ! $scopeClassReflection->hasTraitUse( - SomeTrait::class - )) { + if ($scopeClassReflection->isReadOnly()) { return null; } $typeClassReflection = $this->getType($node) ->getObjectClassReflections()[0] ?? null; - if (! $typeClassReflection instanceof ClassReflection || ! $typeClassReflection->hasTraitUse( - SomeTrait::class - )) { + if (! $typeClassReflection instanceof ClassReflection || ! $typeClassReflection->isFinal()) { return null; } - $node->flags |= Modifiers::FINAL; + $node->flags |= Modifiers::READONLY; return $node; } diff --git a/tests/Issues/ClassReflectionRefresh/Source/SomeTrait.php b/tests/Issues/ClassReflectionRefresh/Source/SomeInterface.php similarity index 80% rename from tests/Issues/ClassReflectionRefresh/Source/SomeTrait.php rename to tests/Issues/ClassReflectionRefresh/Source/SomeInterface.php index c981b880048..9470fd32cfa 100644 --- a/tests/Issues/ClassReflectionRefresh/Source/SomeTrait.php +++ b/tests/Issues/ClassReflectionRefresh/Source/SomeInterface.php @@ -4,6 +4,6 @@ namespace Rector\Tests\Issues\ClassReflectionRefresh\Source; -trait SomeTrait +interface SomeInterface { } diff --git a/tests/Issues/ClassReflectionRefresh/Source/SomeParentClass.php b/tests/Issues/ClassReflectionRefresh/Source/SomeParentClass.php deleted file mode 100644 index 2cef5b3aeba..00000000000 --- a/tests/Issues/ClassReflectionRefresh/Source/SomeParentClass.php +++ /dev/null @@ -1,9 +0,0 @@ -withRules([AddSomeTraitRector::class, FinalizeOnReflectedTraitRector::class]); + ->withRules([FinalizeByReflectionRector::class, ReadonlyOnFinalByReflectionRector::class]); From 7a8297cf19d21776c728d31b4670149fe0b6cfba Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Tue, 20 Oct 2026 21:53:43 +0700 Subject: [PATCH 4/4] restore test --- ..._change.php.inc => add_trait_once.php.inc} | 5 +- .../add_trait_with_imported_parent.php.inc | 32 +++++++++ .../skip_already_used_imported_trait.php.inc | 10 +++ .../Source/AddTraitByReflectionRector.php | 67 +++++++++++++++++++ .../Source/SomeParentClass.php | 9 +++ .../{SomeInterface.php => SomeTrait.php} | 2 +- .../config/configured_rule.php | 5 +- .../ClassReflectionRefreshOnModifierTest.php | 28 ++++++++ .../Fixture/refresh_on_class_change.php.inc | 25 +++++++ ...ass_change_with_imported_interface.php.inc | 8 +-- .../Source/FinalizeByReflectionRector.php | 2 +- .../ReadonlyOnFinalByReflectionRector.php | 2 +- .../Source/SomeInterface.php | 9 +++ .../config/configured_rule.php | 10 +++ 14 files changed, 202 insertions(+), 12 deletions(-) rename tests/Issues/ClassReflectionRefresh/Fixture/{refresh_on_class_change.php.inc => add_trait_once.php.inc} (66%) create mode 100644 tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc create mode 100644 tests/Issues/ClassReflectionRefresh/Fixture/skip_already_used_imported_trait.php.inc create mode 100644 tests/Issues/ClassReflectionRefresh/Source/AddTraitByReflectionRector.php create mode 100644 tests/Issues/ClassReflectionRefresh/Source/SomeParentClass.php rename tests/Issues/ClassReflectionRefresh/Source/{SomeInterface.php => SomeTrait.php} (80%) create mode 100644 tests/Issues/ClassReflectionRefreshOnModifier/ClassReflectionRefreshOnModifierTest.php create mode 100644 tests/Issues/ClassReflectionRefreshOnModifier/Fixture/refresh_on_class_change.php.inc rename tests/Issues/{ClassReflectionRefresh => ClassReflectionRefreshOnModifier}/Fixture/refresh_on_class_change_with_imported_interface.php.inc (53%) rename tests/Issues/{ClassReflectionRefresh => ClassReflectionRefreshOnModifier}/Source/FinalizeByReflectionRector.php (93%) rename tests/Issues/{ClassReflectionRefresh => ClassReflectionRefreshOnModifier}/Source/ReadonlyOnFinalByReflectionRector.php (95%) create mode 100644 tests/Issues/ClassReflectionRefreshOnModifier/Source/SomeInterface.php create mode 100644 tests/Issues/ClassReflectionRefreshOnModifier/config/configured_rule.php diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc similarity index 66% rename from tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change.php.inc rename to tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc index 3164f5893bb..dae3faf52ce 100644 --- a/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change.php.inc +++ b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_once.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\Issues\ClassReflectionRefresh\Fixture; -class RefreshOnClassChange +final class AddTraitOnce { public function run() { @@ -15,8 +15,9 @@ class RefreshOnClassChange namespace Rector\Tests\Issues\ClassReflectionRefresh\Fixture; -final readonly class RefreshOnClassChange +final class AddTraitOnce { + use \Rector\Tests\Issues\ClassReflectionRefresh\Source\SomeTrait; public function run() { } diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc new file mode 100644 index 00000000000..ea21734913f --- /dev/null +++ b/tests/Issues/ClassReflectionRefresh/Fixture/add_trait_with_imported_parent.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/skip_already_used_imported_trait.php.inc b/tests/Issues/ClassReflectionRefresh/Fixture/skip_already_used_imported_trait.php.inc new file mode 100644 index 00000000000..80669bd0bfb --- /dev/null +++ b/tests/Issues/ClassReflectionRefresh/Fixture/skip_already_used_imported_trait.php.inc @@ -0,0 +1,10 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Class_ + { + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + // both the scope and the resolved type must see the refreshed class reflection + $typeClassReflection = $this->getType($node) + ->getObjectClassReflections()[0] ?? null; + + $hasTraitInScope = $classReflection->hasTraitUse(SomeTrait::class); + $hasTraitInType = $typeClassReflection instanceof ClassReflection && $typeClassReflection->hasTraitUse( + SomeTrait::class + ); + + if ($hasTraitInScope && $hasTraitInType) { + return null; + } + + $node->stmts = array_merge([new TraitUse([new FullyQualified(SomeTrait::class)])], $node->stmts); + + return $node; + } +} diff --git a/tests/Issues/ClassReflectionRefresh/Source/SomeParentClass.php b/tests/Issues/ClassReflectionRefresh/Source/SomeParentClass.php new file mode 100644 index 00000000000..2cef5b3aeba --- /dev/null +++ b/tests/Issues/ClassReflectionRefresh/Source/SomeParentClass.php @@ -0,0 +1,9 @@ +withRules([FinalizeByReflectionRector::class, ReadonlyOnFinalByReflectionRector::class]); + ->withRules([AddTraitByReflectionRector::class]); diff --git a/tests/Issues/ClassReflectionRefreshOnModifier/ClassReflectionRefreshOnModifierTest.php b/tests/Issues/ClassReflectionRefreshOnModifier/ClassReflectionRefreshOnModifierTest.php new file mode 100644 index 00000000000..57325fa777c --- /dev/null +++ b/tests/Issues/ClassReflectionRefreshOnModifier/ClassReflectionRefreshOnModifierTest.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/tests/Issues/ClassReflectionRefreshOnModifier/Fixture/refresh_on_class_change.php.inc b/tests/Issues/ClassReflectionRefreshOnModifier/Fixture/refresh_on_class_change.php.inc new file mode 100644 index 00000000000..11dfb92a969 --- /dev/null +++ b/tests/Issues/ClassReflectionRefreshOnModifier/Fixture/refresh_on_class_change.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change_with_imported_interface.php.inc b/tests/Issues/ClassReflectionRefreshOnModifier/Fixture/refresh_on_class_change_with_imported_interface.php.inc similarity index 53% rename from tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change_with_imported_interface.php.inc rename to tests/Issues/ClassReflectionRefreshOnModifier/Fixture/refresh_on_class_change_with_imported_interface.php.inc index 15fb3e8c9c2..ee2eb92f20e 100644 --- a/tests/Issues/ClassReflectionRefresh/Fixture/refresh_on_class_change_with_imported_interface.php.inc +++ b/tests/Issues/ClassReflectionRefreshOnModifier/Fixture/refresh_on_class_change_with_imported_interface.php.inc @@ -1,8 +1,8 @@ withRules([FinalizeByReflectionRector::class, ReadonlyOnFinalByReflectionRector::class]);