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) {