From 56afd543e23838574f35bd3fa9179d44b42e5570 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 5 Jan 2025 17:38:39 +0100 Subject: [PATCH] Use autowired method if exists in adding new dependency --- .../config/configured_rule.php | 2 +- ...AutowiredClassMethodOrPropertyAnalyzer.php | 22 +++++++ .../ClassDependencyManipulator.php | 36 +++++++++--- .../AddClassDependencyTest.php | 28 +++++++++ .../Fixture/fixture.php.inc | 57 +++++++++++++++++++ .../Source/SomeAutowiredService.php | 9 +++ .../config/configured_rule.php | 11 ++++ 7 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 tests/Issues/AddClassDependency/AddClassDependencyTest.php create mode 100644 tests/Issues/AddClassDependency/Fixture/fixture.php.inc create mode 100644 tests/Issues/AddClassDependency/Source/SomeAutowiredService.php create mode 100644 tests/Issues/AddClassDependency/config/configured_rule.php diff --git a/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php b/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php index bdd2364c8e0..3acbdab1679 100644 --- a/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php +++ b/rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php @@ -2,11 +2,11 @@ declare(strict_types=1); -use Rector\ValueObject\PhpVersion; use Rector\Config\RectorConfig; use Rector\Tests\Transform\Rector\FuncCall\FuncCallToMethodCallRector\Source\SomeTranslator; use Rector\Transform\Rector\FuncCall\FuncCallToMethodCallRector; use Rector\Transform\ValueObject\FuncCallToMethodCall; +use Rector\ValueObject\PhpVersion; return static function (RectorConfig $rectorConfig): void { $rectorConfig->phpVersion(PhpVersion::PHP_80); diff --git a/rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php index 80080786a25..ab6391ecf28 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php +++ b/rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php @@ -5,6 +5,7 @@ namespace Rector\TypeDeclaration\NodeAnalyzer; use PhpParser\Node\Param; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Property; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; @@ -18,6 +19,27 @@ public function __construct( ) { } + public function matchAutowiredMethodInClass(Class_ $class): ?ClassMethod + { + foreach ($class->getMethods() as $classMethod) { + if (! $classMethod->isPublic()) { + continue; + } + + if ($classMethod->isMagic()) { + continue; + } + + if (! $this->detect($classMethod)) { + continue; + } + + return $classMethod; + } + + return null; + } + public function detect(ClassMethod | Param | Property $node): bool { $nodePhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); diff --git a/src/NodeManipulator/ClassDependencyManipulator.php b/src/NodeManipulator/ClassDependencyManipulator.php index 218a3b74a14..b6a3dfde5d4 100644 --- a/src/NodeManipulator/ClassDependencyManipulator.php +++ b/src/NodeManipulator/ClassDependencyManipulator.php @@ -39,17 +39,23 @@ public function __construct( private PropertyPresenceChecker $propertyPresenceChecker, private NodeNameResolver $nodeNameResolver, private AutowiredClassMethodOrPropertyAnalyzer $autowiredClassMethodOrPropertyAnalyzer, - private ReflectionResolver $reflectionResolver + private ReflectionResolver $reflectionResolver, ) { } public function addConstructorDependency(Class_ $class, PropertyMetadata $propertyMetadata): void { + // already has property as dependency? skip it if ($this->hasClassPropertyAndDependency($class, $propertyMetadata)) { return; } - if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) { + // special case for Symfony @required + $autowireClassMethod = $this->autowiredClassMethodOrPropertyAnalyzer->matchAutowiredMethodInClass($class); + + if (! $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::PROPERTY_PROMOTION + ) || $autowireClassMethod instanceof ClassMethod) { $this->classInsertManipulator->addPropertyToClass( $class, $propertyMetadata->getName(), @@ -57,18 +63,34 @@ public function addConstructorDependency(Class_ $class, PropertyMetadata $proper ); } - if ($this->shouldAddPromotedProperty($class, $propertyMetadata)) { - $this->addPromotedProperty($class, $propertyMetadata); - } else { + // in case of existing autowire method, re-use it + if ($autowireClassMethod instanceof ClassMethod) { $assign = $this->nodeFactory->createPropertyAssignment($propertyMetadata->getName()); - $this->addConstructorDependencyWithCustomAssign( - $class, + $this->classMethodAssignManipulator->addParameterAndAssignToMethod( + $autowireClassMethod, $propertyMetadata->getName(), $propertyMetadata->getType(), $assign ); + return; + + } + + // add PHP 8.0 promoted property + if ($this->shouldAddPromotedProperty($class, $propertyMetadata)) { + $this->addPromotedProperty($class, $propertyMetadata); + return; } + + $assign = $this->nodeFactory->createPropertyAssignment($propertyMetadata->getName()); + + $this->addConstructorDependencyWithCustomAssign( + $class, + $propertyMetadata->getName(), + $propertyMetadata->getType(), + $assign + ); } /** diff --git a/tests/Issues/AddClassDependency/AddClassDependencyTest.php b/tests/Issues/AddClassDependency/AddClassDependencyTest.php new file mode 100644 index 00000000000..6cc977287ad --- /dev/null +++ b/tests/Issues/AddClassDependency/AddClassDependencyTest.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/AddClassDependency/Fixture/fixture.php.inc b/tests/Issues/AddClassDependency/Fixture/fixture.php.inc new file mode 100644 index 00000000000..a57efc90db8 --- /dev/null +++ b/tests/Issues/AddClassDependency/Fixture/fixture.php.inc @@ -0,0 +1,57 @@ +someAutowiredService = $someAutowiredService; + } + + public function configure() + { + $someType = $this->get('validator'); + } +} + +?> +----- +someAutowiredService = $someAutowiredService; + $this->validator = $validator; + } + + public function configure() + { + $someType = $this->validator; + } +} + +?> diff --git a/tests/Issues/AddClassDependency/Source/SomeAutowiredService.php b/tests/Issues/AddClassDependency/Source/SomeAutowiredService.php new file mode 100644 index 00000000000..8c4ca1776e4 --- /dev/null +++ b/tests/Issues/AddClassDependency/Source/SomeAutowiredService.php @@ -0,0 +1,9 @@ +withRules([ + GetBySymfonyStringToConstructorInjectionRector::class, + ]);