diff --git a/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/re_use_existing_required_method.php.inc b/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/re_use_existing_required_method.php.inc new file mode 100644 index 00000000..63f7f564 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector/Fixture/re_use_existing_required_method.php.inc @@ -0,0 +1,63 @@ +translator = $translator; + } + + #[Route('/example', name: 'example')] + public function example(LoggerInterface $logger) + { + $logger->log('level', $this->translator->trans('value')); + } +} + +?> +----- +translator = $translator; + $this->logger = $logger; + } + + #[Route('/example', name: 'example')] + public function example() + { + $this->logger->log('level', $this->translator->trans('value')); + } +} + +?> diff --git a/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php b/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php index f2f6e226..e7c7ce11 100644 --- a/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php +++ b/rules/CodeQuality/Rector/Class_/ControllerMethodInjectionToConstructorRector.php @@ -456,12 +456,13 @@ private function hasAccessibleParentProperty(Class_ $class, string $propertyName */ private function addRequiredAutowireClassMethod(Class_ $class, array $propertyMetadatas): void { - $autowireMethodName = $this->resolveAutowireMethodName($class); - - $autowireClassMethod = $class->getMethod($autowireMethodName); + // re-use existing #[Required] method, to keep single injection point + $autowireClassMethod = $this->resolveExistingRequiredClassMethod($class); $isNewClassMethod = ! $autowireClassMethod instanceof ClassMethod; if (! $autowireClassMethod instanceof ClassMethod) { + $autowireMethodName = $this->resolveAutowireMethodName($class); + $autowireClassMethod = new ClassMethod(new Identifier($autowireMethodName), [ 'flags' => Modifiers::PUBLIC, 'returnType' => new Identifier('void'), @@ -495,6 +496,45 @@ private function addRequiredAutowireClassMethod(Class_ $class, array $propertyMe } } + /** + * Existing #[Required] setter method, or a method named autowire*(), to re-use as injection point + */ + private function resolveExistingRequiredClassMethod(Class_ $class): ?ClassMethod + { + foreach ($class->getMethods() as $classMethod) { + if (! $classMethod->isPublic()) { + continue; + } + + if ($classMethod->isStatic() || $classMethod->stmts === null) { + continue; + } + + if ($this->hasRequiredAttribute($classMethod)) { + return $classMethod; + } + + if (str_starts_with($classMethod->name->toString(), self::AUTOWIRE_METHOD_NAME_PREFIX)) { + return $classMethod; + } + } + + return null; + } + + private function hasRequiredAttribute(ClassMethod $classMethod): bool + { + foreach ($classMethod->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attribute) { + if ($this->isName($attribute->name, SymfonyAttribute::REQUIRED)) { + return true; + } + } + } + + return false; + } + /** * Suffix with the short class name, to keep the method unique in case of inheritance */