Skip to content

Commit 56afd54

Browse files
committed
Use autowired method if exists in adding new dependency
1 parent 8bc80b4 commit 56afd54

File tree

7 files changed

+157
-8
lines changed

7 files changed

+157
-8
lines changed

rules-tests/Transform/Rector/FuncCall/FuncCallToMethodCallRector/config/configured_rule.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
declare(strict_types=1);
44

5-
use Rector\ValueObject\PhpVersion;
65
use Rector\Config\RectorConfig;
76
use Rector\Tests\Transform\Rector\FuncCall\FuncCallToMethodCallRector\Source\SomeTranslator;
87
use Rector\Transform\Rector\FuncCall\FuncCallToMethodCallRector;
98
use Rector\Transform\ValueObject\FuncCallToMethodCall;
9+
use Rector\ValueObject\PhpVersion;
1010

1111
return static function (RectorConfig $rectorConfig): void {
1212
$rectorConfig->phpVersion(PhpVersion::PHP_80);

rules/TypeDeclaration/NodeAnalyzer/AutowiredClassMethodOrPropertyAnalyzer.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Rector\TypeDeclaration\NodeAnalyzer;
66

77
use PhpParser\Node\Param;
8+
use PhpParser\Node\Stmt\Class_;
89
use PhpParser\Node\Stmt\ClassMethod;
910
use PhpParser\Node\Stmt\Property;
1011
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
@@ -18,6 +19,27 @@ public function __construct(
1819
) {
1920
}
2021

22+
public function matchAutowiredMethodInClass(Class_ $class): ?ClassMethod
23+
{
24+
foreach ($class->getMethods() as $classMethod) {
25+
if (! $classMethod->isPublic()) {
26+
continue;
27+
}
28+
29+
if ($classMethod->isMagic()) {
30+
continue;
31+
}
32+
33+
if (! $this->detect($classMethod)) {
34+
continue;
35+
}
36+
37+
return $classMethod;
38+
}
39+
40+
return null;
41+
}
42+
2143
public function detect(ClassMethod | Param | Property $node): bool
2244
{
2345
$nodePhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);

src/NodeManipulator/ClassDependencyManipulator.php

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,36 +39,58 @@ public function __construct(
3939
private PropertyPresenceChecker $propertyPresenceChecker,
4040
private NodeNameResolver $nodeNameResolver,
4141
private AutowiredClassMethodOrPropertyAnalyzer $autowiredClassMethodOrPropertyAnalyzer,
42-
private ReflectionResolver $reflectionResolver
42+
private ReflectionResolver $reflectionResolver,
4343
) {
4444
}
4545

4646
public function addConstructorDependency(Class_ $class, PropertyMetadata $propertyMetadata): void
4747
{
48+
// already has property as dependency? skip it
4849
if ($this->hasClassPropertyAndDependency($class, $propertyMetadata)) {
4950
return;
5051
}
5152

52-
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) {
53+
// special case for Symfony @required
54+
$autowireClassMethod = $this->autowiredClassMethodOrPropertyAnalyzer->matchAutowiredMethodInClass($class);
55+
56+
if (! $this->phpVersionProvider->isAtLeastPhpVersion(
57+
PhpVersionFeature::PROPERTY_PROMOTION
58+
) || $autowireClassMethod instanceof ClassMethod) {
5359
$this->classInsertManipulator->addPropertyToClass(
5460
$class,
5561
$propertyMetadata->getName(),
5662
$propertyMetadata->getType()
5763
);
5864
}
5965

60-
if ($this->shouldAddPromotedProperty($class, $propertyMetadata)) {
61-
$this->addPromotedProperty($class, $propertyMetadata);
62-
} else {
66+
// in case of existing autowire method, re-use it
67+
if ($autowireClassMethod instanceof ClassMethod) {
6368
$assign = $this->nodeFactory->createPropertyAssignment($propertyMetadata->getName());
6469

65-
$this->addConstructorDependencyWithCustomAssign(
66-
$class,
70+
$this->classMethodAssignManipulator->addParameterAndAssignToMethod(
71+
$autowireClassMethod,
6772
$propertyMetadata->getName(),
6873
$propertyMetadata->getType(),
6974
$assign
7075
);
76+
return;
77+
78+
}
79+
80+
// add PHP 8.0 promoted property
81+
if ($this->shouldAddPromotedProperty($class, $propertyMetadata)) {
82+
$this->addPromotedProperty($class, $propertyMetadata);
83+
return;
7184
}
85+
86+
$assign = $this->nodeFactory->createPropertyAssignment($propertyMetadata->getName());
87+
88+
$this->addConstructorDependencyWithCustomAssign(
89+
$class,
90+
$propertyMetadata->getName(),
91+
$propertyMetadata->getType(),
92+
$assign
93+
);
7294
}
7395

7496
/**
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\Issues\AddClassDependency;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class AddClassDependencyTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace Rector\Tests\Issues\AddClassDependency\Fixture;
4+
5+
use Rector\Tests\Issues\AddClassDependency\Source\SomeAutowiredService;
6+
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7+
8+
class PreferRequiredSetter extends Controller
9+
{
10+
private SomeAutowiredService $someAutowiredService;
11+
12+
/**
13+
* @required
14+
*/
15+
public function autowire(
16+
SomeAutowiredService $someAutowiredService,
17+
) {
18+
$this->someAutowiredService = $someAutowiredService;
19+
}
20+
21+
public function configure()
22+
{
23+
$someType = $this->get('validator');
24+
}
25+
}
26+
27+
?>
28+
-----
29+
<?php
30+
31+
namespace Rector\Tests\Issues\AddClassDependency\Fixture;
32+
33+
use Rector\Tests\Issues\AddClassDependency\Source\SomeAutowiredService;
34+
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
35+
36+
class PreferRequiredSetter extends Controller
37+
{
38+
private SomeAutowiredService $someAutowiredService;
39+
private \Symfony\Component\Validator\Validator\ValidatorInterface $validator;
40+
41+
/**
42+
* @required
43+
*/
44+
public function autowire(
45+
SomeAutowiredService $someAutowiredService, \Symfony\Component\Validator\Validator\ValidatorInterface $validator,
46+
) {
47+
$this->someAutowiredService = $someAutowiredService;
48+
$this->validator = $validator;
49+
}
50+
51+
public function configure()
52+
{
53+
$someType = $this->validator;
54+
}
55+
}
56+
57+
?>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\Issues\AddClassDependency\Source;
6+
7+
final class SomeAutowiredService
8+
{
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Symfony\DependencyInjection\Rector\Class_\GetBySymfonyStringToConstructorInjectionRector;
6+
use Rector\Config\RectorConfig;
7+
8+
return RectorConfig::configure()
9+
->withRules([
10+
GetBySymfonyStringToConstructorInjectionRector::class,
11+
]);

0 commit comments

Comments
 (0)