diff --git a/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php b/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php index 1fa9153bcbe..8b45974e0f0 100644 --- a/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php +++ b/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php @@ -5,18 +5,17 @@ namespace Rector\Php82\NodeManipulator; use PhpParser\Node; +use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; -use PHPStan\Analyser\Scope; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionProperty; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ReflectionProvider; use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; use Rector\Php81\Enum\AttributeName; -use Rector\PHPStan\ScopeFetcher; use Rector\Privatization\NodeManipulator\VisibilityManipulator; use Rector\ValueObject\MethodName; use Rector\ValueObject\Visibility; @@ -32,8 +31,9 @@ public function __construct( public function process(Class_ $class): Class_|null { - $scope = ScopeFetcher::fetch($class); - if ($this->shouldSkip($class, $scope)) { + $parentClassReflections = $this->resolveParentClassReflections($class); + + if ($this->shouldSkip($class, $parentClassReflections)) { return null; } @@ -55,16 +55,24 @@ public function process(Class_ $class): Class_|null } /** + * Resolve parents from the extends name, so anonymous classes (no name) work too. + * * @return ClassReflection[] */ - private function resolveParentClassReflections(Scope $scope): array + private function resolveParentClassReflections(Class_ $class): array { - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { + if (! $class->extends instanceof Name) { + return []; + } + + $parentName = $class->extends->toString(); + if (! $this->reflectionProvider->hasClass($parentName)) { return []; } - return $classReflection->getParents(); + $parentClassReflection = $this->reflectionProvider->getClass($parentName); + + return [$parentClassReflection, ...$parentClassReflection->getParents()]; } /** @@ -75,25 +83,21 @@ private function hasNonTypedProperty(array $properties): bool return array_any($properties, fn (Property $property): bool => ! $property->type instanceof Node); } - private function shouldSkip(Class_ $class, Scope $scope): bool + /** + * @param ClassReflection[] $parentClassReflections + */ + private function shouldSkip(Class_ $class, array $parentClassReflections): bool { - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return true; - } - if ($this->shouldSkipClass($class)) { return true; } - $parents = $this->resolveParentClassReflections($scope); - if (! $class->isAnonymous() && ! $class->isFinal()) { - return ! $this->isExtendsReadonlyClass($parents); + return ! $this->isExtendsReadonlyClass($parentClassReflections); } - foreach ($parents as $parent) { - if (! $parent->isReadOnly()) { + foreach ($parentClassReflections as $parentClassReflection) { + if (! $parentClassReflection->isReadOnly()) { return true; } }