Skip to content

Commit

Permalink
[DependencyInjection] Add TraitGetByTypeToInjectRector (#687)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba authored Dec 1, 2024
1 parent 617b3af commit da54cd8
Show file tree
Hide file tree
Showing 13 changed files with 367 additions and 67 deletions.
6 changes: 4 additions & 2 deletions config/sets/symfony/symfony-constructor-injection.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector;
use Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector;
use Rector\Symfony\Symfony28\Rector\MethodCall\GetToConstructorInjectionRector;
use Rector\Symfony\Symfony34\Rector\Closure\ContainerGetNameToTypeInTestsRector;
use Rector\Symfony\Symfony42\Rector\MethodCall\ContainerGetToConstructorInjectionRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([
// modern step-by-step narrow approach
\Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector::class,
\Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector::class,
ControllerGetByTypeToConstructorInjectionRector::class,
CommandGetByTypeToConstructorInjectionRector::class,

// legacy rules that require container fetch
ContainerGetToConstructorInjectionRector::class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(
\Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector::class
);
$rectorConfig->rule(CommandGetByTypeToConstructorInjectionRector::class);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(
\Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector::class
);
$rectorConfig->rule(ControllerGetByTypeToConstructorInjectionRector::class);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Fixture;

use Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source\SomeService;

trait TraitGetType
{
public function configure()
{
$someType = $this->get(SomeService::class);
}
}

?>
-----
<?php

namespace Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Fixture;

use Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source\SomeService;

trait TraitGetType
{
private \Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source\SomeService $someService;
/**
* @required
*/
public function autowireTraitGetType(\Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source\SomeService $someService): void
{
$this->someService = $someService;
}
public function configure()
{
$someType = $this->someService;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector\Source;

final class SomeService
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Symfony\Tests\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class TraitGetByTypeToInjectRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Symfony\DependencyInjection\Rector\Trait_\TraitGetByTypeToInjectRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(TraitGetByTypeToInjectRector::class);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\ValueObject\MethodName;

final class CommandConstructorDecorator
final readonly class CommandConstructorDecorator
{
public function __construct(
private NodeTypeResolver $nodeTypeResolver
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace Rector\Symfony\DependencyInjection\NodeFactory;

use PhpParser\Comment\Doc;
use PhpParser\Modifiers;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Trait_;
use PHPStan\Type\ObjectType;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PostRector\ValueObject\PropertyMetadata;

final class AutowireClassMethodFactory
{
public function __construct(
private NodeNameResolver $nodeNameResolver
) {

}

/**
* @param PropertyMetadata[] $propertyMetadatas
*/
public function create(Trait_ $trait, array $propertyMetadatas): ClassMethod
{
$traitName = $this->nodeNameResolver->getShortName($trait);

$autowireClassMethod = new ClassMethod('autowire' . $traitName);
$autowireClassMethod->flags |= Modifiers::PUBLIC;
$autowireClassMethod->returnType = new Identifier('void');
$autowireClassMethod->setDocComment(new Doc("/**\n * @required\n */"));

foreach ($propertyMetadatas as $propertyMetadata) {
$param = $this->createAutowiredParam($propertyMetadata);
$autowireClassMethod->params[] = $param;

$createPropertyAssign = new Assign(
new PropertyFetch(new Variable('this'), new Identifier($propertyMetadata->getName())),
new Variable($propertyMetadata->getName())
);
$autowireClassMethod->stmts[] = new Expression($createPropertyAssign);
}

return $autowireClassMethod;
}

private function createAutowiredParam(PropertyMetadata $propertyMetadata): Param
{
$param = new Param(new Variable($propertyMetadata->getName()));
$objectType = $propertyMetadata->getType();
if (! $objectType instanceof ObjectType) {
throw new ShouldNotHappenException();
}

$param->type = new FullyQualified($objectType->getClassName());

return $param;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Rector\Symfony\DependencyInjection\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ClassReflection;
Expand All @@ -16,6 +15,7 @@
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\Symfony\DependencyInjection\NodeDecorator\CommandConstructorDecorator;
use Rector\Symfony\DependencyInjection\ThisGetTypeMatcher;
use Rector\Symfony\Enum\SymfonyClass;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
Expand All @@ -28,7 +28,8 @@ final class CommandGetByTypeToConstructorInjectionRector extends AbstractRector
public function __construct(
private readonly ClassDependencyManipulator $classDependencyManipulator,
private readonly PropertyNaming $propertyNaming,
private readonly CommandConstructorDecorator $commandConstructorDecorator
private readonly CommandConstructorDecorator $commandConstructorDecorator,
private readonly ThisGetTypeMatcher $thisGetTypeMatcher
) {
}

Expand Down Expand Up @@ -94,33 +95,7 @@ public function refactor(Node $node): ?Node
return null;
}

if ($node->isFirstClassCallable()) {
return null;
}

if (! $this->isName($node->name, 'get')) {
return null;
}

if (! $this->isName($node->var, 'this')) {
return null;
}

if (count($node->getArgs()) !== 1) {
return null;
}

$firstArg = $node->getArgs()[0];
if (! $firstArg->value instanceof ClassConstFetch) {
return null;
}

// must be class const fetch
if (! $this->isName($firstArg->value->name, 'class')) {
return null;
}

$className = $this->getName($firstArg->value->class);
$className = $this->thisGetTypeMatcher->match($node);
if (! is_string($className)) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Rector\Symfony\DependencyInjection\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ClassReflection;
Expand All @@ -15,6 +14,7 @@
use Rector\PostRector\ValueObject\PropertyMetadata;
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\Symfony\DependencyInjection\ThisGetTypeMatcher;
use Rector\Symfony\Enum\SymfonyClass;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
Expand All @@ -26,7 +26,8 @@ final class ControllerGetByTypeToConstructorInjectionRector extends AbstractRect
{
public function __construct(
private readonly ClassDependencyManipulator $classDependencyManipulator,
private readonly PropertyNaming $propertyNaming
private readonly PropertyNaming $propertyNaming,
private readonly ThisGetTypeMatcher $thisGetTypeMatcher
) {
}

Expand Down Expand Up @@ -92,33 +93,7 @@ public function refactor(Node $node): ?Node
return null;
}

if ($node->isFirstClassCallable()) {
return null;
}

if (! $this->isName($node->name, 'get')) {
return null;
}

if (! $this->isName($node->var, 'this')) {
return null;
}

if (count($node->getArgs()) !== 1) {
return null;
}

$firstArg = $node->getArgs()[0];
if (! $firstArg->value instanceof ClassConstFetch) {
return null;
}

// must be class const fetch
if (! $this->isName($firstArg->value->name, 'class')) {
return null;
}

$className = $this->getName($firstArg->value->class);
$className = $this->thisGetTypeMatcher->match($node);
if (! is_string($className)) {
return null;
}
Expand Down
Loading

0 comments on commit da54cd8

Please sign in to comment.