Skip to content

Commit a4a6c34

Browse files
committed
[DependencyInjection] Add TraitGetByTypeToInjectRector
1 parent 617b3af commit a4a6c34

File tree

13 files changed

+367
-67
lines changed

13 files changed

+367
-67
lines changed

config/sets/symfony/symfony-constructor-injection.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
declare(strict_types=1);
44

55
use Rector\Config\RectorConfig;
6+
use Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector;
7+
use Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector;
68
use Rector\Symfony\Symfony28\Rector\MethodCall\GetToConstructorInjectionRector;
79
use Rector\Symfony\Symfony34\Rector\Closure\ContainerGetNameToTypeInTestsRector;
810
use Rector\Symfony\Symfony42\Rector\MethodCall\ContainerGetToConstructorInjectionRector;
911

1012
return static function (RectorConfig $rectorConfig): void {
1113
$rectorConfig->rules([
1214
// modern step-by-step narrow approach
13-
\Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector::class,
14-
\Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector::class,
15+
ControllerGetByTypeToConstructorInjectionRector::class,
16+
CommandGetByTypeToConstructorInjectionRector::class,
1517

1618
// legacy rules that require container fetch
1719
ContainerGetToConstructorInjectionRector::class,

rules-tests/DependencyInjection/Rector/Class_/CommandGetByTypeToConstructorInjectionRector/config/configured_rule.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
declare(strict_types=1);
44

55
use Rector\Config\RectorConfig;
6+
use Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector;
67

78
return static function (RectorConfig $rectorConfig): void {
8-
$rectorConfig->rule(
9-
\Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector::class
10-
);
9+
$rectorConfig->rule(CommandGetByTypeToConstructorInjectionRector::class);
1110
};

rules-tests/DependencyInjection/Rector/Class_/ControllerGetByTypeToConstructorInjectionRector/config/configured_rule.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
declare(strict_types=1);
44

55
use Rector\Config\RectorConfig;
6+
use Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector;
67

78
return static function (RectorConfig $rectorConfig): void {
8-
$rectorConfig->rule(
9-
\Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector::class
10-
);
9+
$rectorConfig->rule(ControllerGetByTypeToConstructorInjectionRector::class);
1110
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Fixture;
4+
5+
use Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source\SomeService;
6+
7+
trait TraitGetType
8+
{
9+
public function configure()
10+
{
11+
$someType = $this->get(SomeService::class);
12+
}
13+
}
14+
15+
?>
16+
-----
17+
<?php
18+
19+
namespace Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Fixture;
20+
21+
use Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source\SomeService;
22+
23+
trait TraitGetType
24+
{
25+
private \Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source\SomeService $someService;
26+
/**
27+
* @required
28+
*/
29+
public function autowireTraitGetType(\Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source\SomeService $someService): void
30+
{
31+
$this->someService = $someService;
32+
}
33+
public function configure()
34+
{
35+
$someType = $this->someService;
36+
}
37+
}
38+
39+
?>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source;
4+
5+
final class SomeService
6+
{
7+
}
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\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class TraitGetByTypeToInjectRectorTest 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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\Symfony\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->rule(TraitGetByTypeToInjectRector::class);
10+
};

rules/DependencyInjection/NodeDecorator/CommandConstructorDecorator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use Rector\NodeTypeResolver\NodeTypeResolver;
1414
use Rector\ValueObject\MethodName;
1515

16-
final class CommandConstructorDecorator
16+
final readonly class CommandConstructorDecorator
1717
{
1818
public function __construct(
1919
private NodeTypeResolver $nodeTypeResolver
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Symfony\DependencyInjection\NodeFactory;
6+
7+
use PhpParser\Comment\Doc;
8+
use PhpParser\Modifiers;
9+
use PhpParser\Node\Expr\Assign;
10+
use PhpParser\Node\Expr\PropertyFetch;
11+
use PhpParser\Node\Expr\Variable;
12+
use PhpParser\Node\Identifier;
13+
use PhpParser\Node\Name\FullyQualified;
14+
use PhpParser\Node\Param;
15+
use PhpParser\Node\Stmt\ClassMethod;
16+
use PhpParser\Node\Stmt\Expression;
17+
use PhpParser\Node\Stmt\Trait_;
18+
use PHPStan\Type\ObjectType;
19+
use Rector\Exception\ShouldNotHappenException;
20+
use Rector\NodeNameResolver\NodeNameResolver;
21+
use Rector\PostRector\ValueObject\PropertyMetadata;
22+
23+
final class AutowireClassMethodFactory
24+
{
25+
public function __construct(
26+
private NodeNameResolver $nodeNameResolver
27+
) {
28+
29+
}
30+
31+
/**
32+
* @param PropertyMetadata[] $propertyMetadatas
33+
*/
34+
public function create(Trait_ $trait, array $propertyMetadatas): ClassMethod
35+
{
36+
$traitName = $this->nodeNameResolver->getShortName($trait);
37+
38+
$autowireClassMethod = new ClassMethod('autowire' . $traitName);
39+
$autowireClassMethod->flags |= Modifiers::PUBLIC;
40+
$autowireClassMethod->returnType = new Identifier('void');
41+
$autowireClassMethod->setDocComment(new Doc("/**\n * @required\n */"));
42+
43+
foreach ($propertyMetadatas as $propertyMetadata) {
44+
$param = $this->createAutowiredParam($propertyMetadata);
45+
$autowireClassMethod->params[] = $param;
46+
47+
$createPropertyAssign = new Assign(
48+
new PropertyFetch(new Variable('this'), new Identifier($propertyMetadata->getName())),
49+
new Variable($propertyMetadata->getName())
50+
);
51+
$autowireClassMethod->stmts[] = new Expression($createPropertyAssign);
52+
}
53+
54+
return $autowireClassMethod;
55+
}
56+
57+
private function createAutowiredParam(PropertyMetadata $propertyMetadata): Param
58+
{
59+
$param = new Param(new Variable($propertyMetadata->getName()));
60+
$objectType = $propertyMetadata->getType();
61+
if (! $objectType instanceof ObjectType) {
62+
throw new ShouldNotHappenException();
63+
}
64+
65+
$param->type = new FullyQualified($objectType->getClassName());
66+
67+
return $param;
68+
}
69+
}

rules/DependencyInjection/Rector/Class_/CommandGetByTypeToConstructorInjectionRector.php

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Rector\Symfony\DependencyInjection\Rector\Class_;
66

77
use PhpParser\Node;
8-
use PhpParser\Node\Expr\ClassConstFetch;
98
use PhpParser\Node\Expr\MethodCall;
109
use PhpParser\Node\Stmt\Class_;
1110
use PHPStan\Reflection\ClassReflection;
@@ -16,6 +15,7 @@
1615
use Rector\Rector\AbstractRector;
1716
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
1817
use Rector\Symfony\DependencyInjection\NodeDecorator\CommandConstructorDecorator;
18+
use Rector\Symfony\DependencyInjection\ThisGetTypeMatcher;
1919
use Rector\Symfony\Enum\SymfonyClass;
2020
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
2121
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@@ -28,7 +28,8 @@ final class CommandGetByTypeToConstructorInjectionRector extends AbstractRector
2828
public function __construct(
2929
private readonly ClassDependencyManipulator $classDependencyManipulator,
3030
private readonly PropertyNaming $propertyNaming,
31-
private readonly CommandConstructorDecorator $commandConstructorDecorator
31+
private readonly CommandConstructorDecorator $commandConstructorDecorator,
32+
private readonly ThisGetTypeMatcher $thisGetTypeMatcher
3233
) {
3334
}
3435

@@ -94,33 +95,7 @@ public function refactor(Node $node): ?Node
9495
return null;
9596
}
9697

97-
if ($node->isFirstClassCallable()) {
98-
return null;
99-
}
100-
101-
if (! $this->isName($node->name, 'get')) {
102-
return null;
103-
}
104-
105-
if (! $this->isName($node->var, 'this')) {
106-
return null;
107-
}
108-
109-
if (count($node->getArgs()) !== 1) {
110-
return null;
111-
}
112-
113-
$firstArg = $node->getArgs()[0];
114-
if (! $firstArg->value instanceof ClassConstFetch) {
115-
return null;
116-
}
117-
118-
// must be class const fetch
119-
if (! $this->isName($firstArg->value->name, 'class')) {
120-
return null;
121-
}
122-
123-
$className = $this->getName($firstArg->value->class);
98+
$className = $this->thisGetTypeMatcher->match($node);
12499
if (! is_string($className)) {
125100
return null;
126101
}

0 commit comments

Comments
 (0)