From 6cadc8dbf5ca724ce6e8d6cb6baac5b507057a05 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 4 Aug 2026 00:11:44 +0200 Subject: [PATCH] [CodeQuality] Re-use parent protected property in ControllerMethodInjectionToConstructorRector --- .../re_use_parent_protected_property.php.inc | 41 ++++++++++++++++++ .../ParentControllerWithProtectedProperty.php | 15 +++++++ ...llerMethodInjectionToConstructorRector.php | 42 +++++++++++++++++-- 3 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/re_use_parent_protected_property.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Source/ParentControllerWithProtectedProperty.php diff --git a/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/re_use_parent_protected_property.php.inc b/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/re_use_parent_protected_property.php.inc new file mode 100644 index 00000000..a25c4260 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/re_use_parent_protected_property.php.inc @@ -0,0 +1,41 @@ +log('level', 'value'); + } +} + +?> +----- +logger->log('level', 'value'); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Source/ParentControllerWithProtectedProperty.php b/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Source/ParentControllerWithProtectedProperty.php new file mode 100644 index 00000000..5a1c38bf --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Source/ParentControllerWithProtectedProperty.php @@ -0,0 +1,15 @@ +getName($param->var); $paramsToRemove[] = [$classMethod, $key]; - $propertyMetadatas[$paramName] = new PropertyMetadata($paramName, $paramType); $methodParamNamesToReplace[$classMethod->name->toString()][] = $paramName; $removedMethodArgPositions[$classMethod->name->toString()][] = $key; + + // the parent class already provides the very same service, re-use it instead of adding own one + if ($this->hasAccessibleParentProperty($node, $paramName, $paramType)) { + continue; + } + + $propertyMetadatas[$paramName] = new PropertyMetadata($paramName, $paramType); } } // nothing to move - if ($propertyMetadatas === []) { + if ($paramsToRemove === []) { return null; } @@ -350,6 +359,33 @@ private function hasConflictedParamName( return false; } + /** + * Is there a protected/public property of the same name and compatible type in a parent class? + */ + private function hasAccessibleParentProperty(Class_ $class, string $propertyName, ObjectType $objectType): bool + { + $classReflection = $this->reflectionResolver->resolveClassReflection($class); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + foreach ($classReflection->getParents() as $parentClassReflection) { + if (! $parentClassReflection->hasNativeProperty($propertyName)) { + continue; + } + + $nativePropertyReflection = $parentClassReflection->getNativeProperty($propertyName); + if ($nativePropertyReflection->isPrivate()) { + continue; + } + + return $objectType->isSuperTypeOf($nativePropertyReflection->getReadableType()) + ->yes(); + } + + return false; + } + private function isUsedInStaticClosureUse(ClassMethod $classMethod, string $paramName): bool { if ($classMethod->stmts === null) {