[Doctrine] Add constructor getRepository to service (#3954)

This commit is contained in:
Tomas Votruba 2020-08-13 01:04:23 +02:00 committed by GitHub
parent 4fce95c509
commit 33d992497a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 489 additions and 571 deletions

View File

@ -6,13 +6,12 @@ use Rector\Architecture\Rector\Class_\MoveRepositoryFromParentToConstructorRecto
use Rector\Architecture\Rector\MethodCall\ReplaceParentRepositoryCallsByRepositoryPropertyRector;
use Rector\Architecture\Rector\MethodCall\ServiceLocatorToDIRector;
use Rector\Doctrine\Rector\Class_\RemoveRepositoryFromEntityAnnotationRector;
use Rector\Doctrine\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector;
use Rector\Generic\Rector\Class_\AddPropertyByParentRector;
use Rector\Generic\Rector\Class_\RemoveParentRector;
use Rector\Generic\Rector\ClassLike\RemoveAnnotationRector;
use Rector\Generic\Rector\ClassMethod\RemoveConstructorDependencyByParentRector;
use Rector\Generic\Rector\MethodCall\MethodCallToPropertyFetchRector;
use Rector\Generic\Rector\MethodCall\ReplaceParentCallByPropertyCallRector;
use Rector\Generic\Rector\StaticCall\RemoveParentCallByParentRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
/**
@ -32,6 +31,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
// covers "extends ServiceEntityRepository"
// @see https://github.com/doctrine/DoctrineBundle/pull/727/files
$services->set(ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector::class);
$services->set(RemoveAnnotationRector::class)
->call('configure', [[
RemoveAnnotationRector::ANNOTATIONS_TO_REMOVE => ['method'],
@ -71,23 +72,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
],
]]);
$services->set(RemoveParentCallByParentRector::class)
->call('configure', [[
RemoveParentCallByParentRector::PARENT_CLASSES => [
'Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository',
],
]]);
$services->set(RemoveConstructorDependencyByParentRector::class)
->call('configure', [[
RemoveConstructorDependencyByParentRector::PARENT_TYPE_TO_PARAM_TYPES_TO_REMOVE => [
'Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository' => [
'Doctrine\Common\Persistence\ManagerRegistry',
'Doctrine\Persistence\ManagerRegistry',
],
],
]]);
$services->set(RemoveParentRector::class)
->call('configure', [[
RemoveParentRector::PARENT_TYPES_TO_REMOVE => [

View File

@ -1,4 +1,4 @@
# All 558 Rectors Overview
# All 557 Rectors Overview
- [Projects](#projects)
---
@ -13,13 +13,13 @@
- [DeadCode](#deadcode) (40)
- [Decomplex](#decomplex) (1)
- [Decouple](#decouple) (1)
- [Doctrine](#doctrine) (16)
- [Doctrine](#doctrine) (17)
- [DoctrineCodeQuality](#doctrinecodequality) (2)
- [DoctrineGedmoToKnplabs](#doctrinegedmotoknplabs) (7)
- [Downgrade](#downgrade) (1)
- [DynamicTypeAnalysis](#dynamictypeanalysis) (3)
- [FileSystemRector](#filesystemrector) (1)
- [Generic](#generic) (49)
- [Generic](#generic) (47)
- [Guzzle](#guzzle) (1)
- [Injection](#injection) (1)
- [JMS](#jms) (2)
@ -3876,6 +3876,41 @@ Remove temporary *Uuid relation properties
<br><br>
### `ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector`
- class: [`Rector\Doctrine\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector`](/../master/rules/doctrine/src/Rector/ClassMethod/ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector.php)
- [test fixtures](/../master/rules/doctrine/tests/Rector/ClassMethod/ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector/Fixture)
Change ServiceEntityRepository to dependency injection, with repository property
```diff
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
final class ProjectRepository extends ServiceEntityRepository
{
- public function __construct(ManagerRegistry $registry)
+ /**
+ * @var \Doctrine\ORM\EntityManagerInterface
+ */
+ private $entityManager;
+
+ /**
+ * @var \Doctrine\ORM\EntityRepository<Project>
+ */
+ private $repository;
+
+ public function __construct(\Doctrine\ORM\EntityManagerInterface $entityManager)
{
- parent::__construct($registry, Project::class);
+ $this->repository = $entityManager->getRepository(Project::class);
+ $this->entityManager = $entityManager;
}
}
```
<br><br>
## DoctrineCodeQuality
### `ChangeQuerySetParametersMethodParameterFromArrayToArrayCollectionRector`
@ -5491,40 +5526,6 @@ return function (ContainerConfigurator $containerConfigurator) : void {
<br><br>
### `RemoveConstructorDependencyByParentRector`
- class: [`Rector\Generic\Rector\ClassMethod\RemoveConstructorDependencyByParentRector`](/../master/rules/generic/src/Rector/ClassMethod/RemoveConstructorDependencyByParentRector.php)
- [test fixtures](/../master/rules/generic/tests/Rector/ClassMethod/RemoveConstructorDependencyByParentRector/Fixture)
Removes params in constructor by parent type and param names
```php
<?php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Rector\Generic\Rector\ClassMethod\RemoveConstructorDependencyByParentRector;
return function (ContainerConfigurator $containerConfigurator) : void {
$services = $containerConfigurator->services();
$services->set(RemoveConstructorDependencyByParentRector::class)
->call('configure', [[RemoveConstructorDependencyByParentRector::PARENT_TYPE_TO_PARAM_TYPES_TO_REMOVE, ['SomeParentClass' => ['someType']]]]);
};
```
```diff
class SomeClass extends SomeParentClass
{
- public function __construct(SomeType $someType)
+ public function __construct()
{
}
}
```
<br><br>
### `RemoveFuncCallArgRector`
- class: [`Rector\Generic\Rector\FuncCall\RemoveFuncCallArgRector`](/../master/rules/generic/src/Rector/FuncCall/RemoveFuncCallArgRector.php)
@ -5614,40 +5615,6 @@ return function (ContainerConfigurator $containerConfigurator) : void {
<br><br>
### `RemoveParentCallByParentRector`
- class: [`Rector\Generic\Rector\StaticCall\RemoveParentCallByParentRector`](/../master/rules/generic/src/Rector/StaticCall/RemoveParentCallByParentRector.php)
- [test fixtures](/../master/rules/generic/tests/Rector/StaticCall/RemoveParentCallByParentRector/Fixture)
Remove parent call by parent class
```php
<?php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Rector\Generic\Rector\StaticCall\RemoveParentCallByParentRector;
return function (ContainerConfigurator $containerConfigurator) : void {
$services = $containerConfigurator->services();
$services->set(RemoveParentCallByParentRector::class)
->call('configure', [[RemoveParentCallByParentRector::PARENT_CLASSES, ['SomeParentClass']]]);
};
```
```diff
final class SomeClass extends SomeParentClass
{
public function run()
{
- parent::someCall();
}
}
```
<br><br>
### `RemoveParentRector`
- class: [`Rector\Generic\Rector\Class_\RemoveParentRector`](/../master/rules/generic/src/Rector/Class_/RemoveParentRector.php)
@ -11590,8 +11557,8 @@ Rename "*Spec.php" file to "*Test.php" file
### `UnwrapFutureCompatibleIfFunctionExistsRector`
- class: [`Rector\Polyfill\Rector\If_\UnwrapFutureCompatibleIfFunctionExistsRector`](/../master/packages/polyfill/src/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector.php)
- [test fixtures](/../master/packages/polyfill/tests/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/Fixture)
- class: [`Rector\Polyfill\Rector\If_\UnwrapFutureCompatibleIfFunctionExistsRector`](/../master/rules/polyfill/src/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector.php)
- [test fixtures](/../master/rules/polyfill/tests/Rector/If_/UnwrapFutureCompatibleIfFunctionExistsRector/Fixture)
Remove functions exists if with else for always existing
@ -11615,8 +11582,8 @@ Remove functions exists if with else for always existing
### `UnwrapFutureCompatibleIfPhpVersionRector`
- class: [`Rector\Polyfill\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector`](/../master/packages/polyfill/src/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php)
- [test fixtures](/../master/packages/polyfill/tests/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/Fixture)
- class: [`Rector\Polyfill\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector`](/../master/rules/polyfill/src/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector.php)
- [test fixtures](/../master/rules/polyfill/tests/Rector/If_/UnwrapFutureCompatibleIfPhpVersionRector/Fixture)
Remove php version checks if they are passed

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Rector\PHPStanStaticTypeMapper\Contract;
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
interface PHPStanStaticTypeMapperAwareInterface
{
public function setPHPStanStaticTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void;
}

View File

@ -11,6 +11,7 @@ use PhpParser\Node\UnionType as PhpParserUnionType;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Type;
use Rector\Core\Exception\NotImplementedException;
use Rector\PHPStanStaticTypeMapper\Contract\PHPStanStaticTypeMapperAwareInterface;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
final class PHPStanStaticTypeMapper
@ -35,6 +36,12 @@ final class PHPStanStaticTypeMapper
*/
public function __construct(array $typeMappers)
{
foreach ($typeMappers as $typeMapper) {
if ($typeMapper instanceof PHPStanStaticTypeMapperAwareInterface) {
$typeMapper->setPHPStanStaticTypeMapper($this);
}
}
$this->typeMappers = $typeMappers;
}

View File

@ -7,6 +7,7 @@ namespace Rector\PHPStanStaticTypeMapper\TypeMapper;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Generic\GenericObjectType;
@ -18,10 +19,17 @@ use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PHPStan\Type\SelfObjectType;
use Rector\PHPStan\Type\ShortenedObjectType;
use Rector\PHPStanStaticTypeMapper\Contract\PHPStanStaticTypeMapperAwareInterface;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
final class ObjectTypeMapper implements TypeMapperInterface
final class ObjectTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMapperAwareInterface
{
/**
* @var PHPStanStaticTypeMapper
*/
private $phpStanStaticTypeMapper;
public function getNodeClass(): string
{
return ObjectType::class;
@ -40,6 +48,18 @@ final class ObjectTypeMapper implements TypeMapperInterface
return new IdentifierTypeNode($type->getClassName());
}
if ($type instanceof GenericObjectType) {
$identifierTypeNode = new IdentifierTypeNode('\\' . $type->getClassName());
$genericTypeNodes = [];
foreach ($type->getTypes() as $genericType) {
$typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($genericType);
$genericTypeNodes[] = $typeNode;
}
return new GenericTypeNode($identifierTypeNode, $genericTypeNodes);
}
return new IdentifierTypeNode('\\' . $type->getClassName());
}
@ -98,4 +118,9 @@ final class ObjectTypeMapper implements TypeMapperInterface
return $type->getClassName();
}
public function setPHPStanStaticTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTypeMapper): void
{
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
}
}

View File

@ -14,6 +14,7 @@ use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\ChangesReporting\Collector\RectorChangeCollector;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
@ -59,6 +60,11 @@ trait NodeCommandersTrait
*/
private $rectorChangeCollector;
/**
* @var PropertyNaming
*/
private $propertyNaming;
/**
* @required
*/
@ -68,7 +74,8 @@ trait NodeCommandersTrait
UseNodesToAddCollector $useNodesToAddCollector,
NodesToAddCollector $nodesToAddCollector,
NodesToReplaceCollector $nodesToReplaceCollector,
RectorChangeCollector $rectorChangeCollector
RectorChangeCollector $rectorChangeCollector,
PropertyNaming $propertyNaming
): void {
$this->nodesToRemoveCollector = $nodesToRemoveCollector;
$this->propertyToAddCollector = $propertyToAddCollector;
@ -76,6 +83,7 @@ trait NodeCommandersTrait
$this->nodesToReplaceCollector = $nodesToReplaceCollector;
$this->nodesToAddCollector = $nodesToAddCollector;
$this->rectorChangeCollector = $rectorChangeCollector;
$this->propertyNaming = $propertyNaming;
}
/**
@ -136,10 +144,18 @@ trait NodeCommandersTrait
/** @var string $propertyName */
$propertyName = $this->getName($property);
$this->addPropertyToClass($classNode, $propertyType, $propertyName);
$this->addConstructorDependencyToClass($classNode, $propertyType, $propertyName);
}
protected function addPropertyToClass(Class_ $class, ?Type $propertyType, string $propertyName): void
protected function addServiceConstructorDependencyToClass(Class_ $class, string $className): void
{
$serviceObjectType = new ObjectType($className);
$propertyName = $this->propertyNaming->fqnToVariableName($serviceObjectType);
$this->addConstructorDependencyToClass($class, $serviceObjectType, $propertyName);
}
protected function addConstructorDependencyToClass(Class_ $class, ?Type $propertyType, string $propertyName): void
{
$this->propertyToAddCollector->addPropertyToClass($propertyName, $propertyType, $class);
$this->rectorChangeCollector->notifyNodeFileInfo($class);
@ -151,11 +167,8 @@ trait NodeCommandersTrait
$this->rectorChangeCollector->notifyNodeFileInfo($class);
}
protected function addPropertyWithoutConstructorToClass(
Class_ $class,
?Type $propertyType,
string $propertyName
): void {
protected function addPropertyToClass(Class_ $class, ?Type $propertyType, string $propertyName): void
{
$this->propertyToAddCollector->addPropertyWithoutConstructorToClass($propertyName, $propertyType, $class);
$this->rectorChangeCollector->notifyNodeFileInfo($class);
}

View File

@ -144,7 +144,7 @@ PHP
$repositoryObjectType = new ObjectType($repositoryFqn);
$this->addPropertyToClass(
$this->addConstructorDependencyToClass(
$classLike,
$repositoryObjectType,
$this->propertyNaming->fqnToVariableName($repositoryObjectType)

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Rector\Doctrine\NodeFactory;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Expression;
final class RepositoryNodeFactory
{
public function createRepositoryAssign(Expr $entityReferenceExpr): Expression
{
$propertyFetch = new PropertyFetch(new Variable('this'), new Identifier('repository'));
$assign = new Assign($propertyFetch, $this->createGetRepositoryMethodCall($entityReferenceExpr));
return new Expression($assign);
}
private function createGetRepositoryMethodCall(Expr $entityReferenceExpr): MethodCall
{
$args = [new Arg($entityReferenceExpr)];
return new MethodCall(new Variable('entityManager'), 'getRepository', $args);
}
}

View File

@ -0,0 +1,187 @@
<?php
declare(strict_types=1);
namespace Rector\Doctrine\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Doctrine\NodeFactory\RepositoryNodeFactory;
use Rector\Doctrine\Type\RepositoryTypeFactory;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* @sponsor Thanks https://www.luzanky.cz/ for sponsoring this rule
*
* @see https://tomasvotruba.com/blog/2017/10/16/how-to-use-repository-with-doctrine-as-service-in-symfony/
*
* @see \Rector\Doctrine\Tests\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRectorTest
*/
final class ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector extends AbstractRector
{
/**
* @var string
*/
private const SERVICE_ENTITY_REPOSITORY_CLASS = 'Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository';
/**
* @var RepositoryNodeFactory
*/
private $repositoryNodeFactory;
/**
* @var RepositoryTypeFactory
*/
private $repositoryTypeFactory;
public function __construct(
RepositoryNodeFactory $repositoryNodeFactory,
RepositoryTypeFactory $repositoryTypeFactory
) {
$this->repositoryNodeFactory = $repositoryNodeFactory;
$this->repositoryTypeFactory = $repositoryTypeFactory;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Change ServiceEntityRepository to dependency injection, with repository property',
[
new CodeSample(
<<<'PHP'
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
final class ProjectRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Project::class);
}
}
PHP
,
<<<'PHP'
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
final class ProjectRepository extends ServiceEntityRepository
{
/**
* @var \Doctrine\ORM\EntityManagerInterface
*/
private $entityManager;
/**
* @var \Doctrine\ORM\EntityRepository<Project>
*/
private $repository;
public function __construct(\Doctrine\ORM\EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Project::class);
$this->entityManager = $entityManager;
}
}
PHP
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [ClassMethod::class];
}
/**
* @param ClassMethod $node
*
* For reference, possible manager registry param types:
* - Doctrine\Common\Persistence\ManagerRegistry
* - Doctrine\Persistence\ManagerRegistry
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
return null;
}
/** @var ClassLike|null $classLike */
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return null;
}
// 1. remove params
$node->params = [];
// 2. remove parent::__construct()
$entityReferenceExpr = $this->removeParentConstructAndCollectEntityReference($node);
// 3. add $entityManager->getRepository() fetch assign
$node->stmts[] = $this->repositoryNodeFactory->createRepositoryAssign($entityReferenceExpr);
// 4. add $repository property
$this->addRepositoryProperty($classLike, $entityReferenceExpr);
// 5. add param + add property, dependency
$this->addServiceConstructorDependencyToClass($classLike, 'Doctrine\ORM\EntityManagerInterface');
return $node;
}
private function shouldSkip(ClassMethod $classMethod): bool
{
/** @var string|null $parentClassName */
$parentClassName = $classMethod->getAttribute(AttributeKey::PARENT_CLASS_NAME);
if ($parentClassName === null) {
return true;
}
return $parentClassName !== self::SERVICE_ENTITY_REPOSITORY_CLASS;
}
private function removeParentConstructAndCollectEntityReference(ClassMethod $classMethod): Expr
{
$entityReferenceExpr = null;
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
&$entityReferenceExpr
) {
if (! $node instanceof StaticCall) {
return null;
}
if (! $this->isName($node->class, 'parent')) {
return null;
}
$entityReferenceExpr = $node->args[1]->value;
$this->removeNode($node);
});
if ($entityReferenceExpr === null) {
throw new ShouldNotHappenException();
}
return $entityReferenceExpr;
}
private function addRepositoryProperty(Class_ $class, Expr $entityReferenceExpr): void
{
$repositoryPropertyType = $this->repositoryTypeFactory->createRepositoryPropertyType($entityReferenceExpr);
$this->addPropertyToClass($class, $repositoryPropertyType, 'repository');
}
}

View File

@ -252,7 +252,7 @@ PHP
$assign = $this->nodeFactory->createPropertyAssignment($name);
$classMethod->stmts[] = new Expression($assign);
$this->addPropertyToClass($class, $objectType, $name);
$this->addConstructorDependencyToClass($class, $objectType, $name);
}
private function createEntityManagerParam(): Param

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Rector\Doctrine\Type;
use PhpParser\Node\Expr;
use PHPStan\Type\Generic\GenericObjectType;
use Rector\Core\Exception\NotImplementedYetException;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
final class RepositoryTypeFactory
{
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
public function __construct(NodeNameResolver $nodeNameResolver)
{
$this->nodeNameResolver = $nodeNameResolver;
}
public function createRepositoryPropertyType(Expr $entityReferenceExpr): GenericObjectType
{
if (! $entityReferenceExpr instanceof Expr\ClassConstFetch) {
throw new NotImplementedYetException();
}
/** @var string $className */
$className = $this->nodeNameResolver->getName($entityReferenceExpr->class);
return new GenericObjectType('Doctrine\ORM\EntityRepository', [new FullyQualifiedObjectType($className)]);
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Rector\Doctrine\Tests\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector\Fixture;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Rector\Doctrine\Tests\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector\Source\Project;
final class ProjectRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Project::class);
}
}
?>
-----
<?php
namespace Rector\Doctrine\Tests\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector\Fixture;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Rector\Doctrine\Tests\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector\Source\Project;
final class ProjectRepository extends ServiceEntityRepository
{
private \Doctrine\ORM\EntityManagerInterface $entityManager;
/**
* @var \Doctrine\ORM\EntityRepository<\Rector\Doctrine\Tests\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector\Source\Project>
*/
private \Doctrine\ORM\EntityRepository $repository;
public function __construct(\Doctrine\ORM\EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Project::class);
$this->entityManager = $entityManager;
}
}
?>

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Rector\Doctrine\Tests\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Doctrine\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector;
use Symplify\SmartFileSystem\SmartFileInfo;
final class ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector::class;
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\Doctrine\Tests\Rector\ClassMethod\ServiceEntityRepositoryConstructorToDependencyInjectionWithRepositoryPropertyRector\Source;
class Project
{
}

View File

@ -82,7 +82,7 @@ abstract class AbstractToMethodCallRector extends AbstractRector implements Conf
{
$fullyQualifiedObjectType = new FullyQualifiedObjectType($type);
$propertyName = $this->propertyNaming->fqnToVariableName($fullyQualifiedObjectType);
$this->addPropertyToClass($class, $fullyQualifiedObjectType, $propertyName);
$this->addConstructorDependencyToClass($class, $fullyQualifiedObjectType, $propertyName);
}
private function createPropertyFetchFromClass(string $type): PropertyFetch

View File

@ -113,7 +113,7 @@ PHP
/** @var string $paramName */
$paramName = $this->getName($paramNode->var);
$this->addPropertyToClass($class, $paramNodeType, $paramName);
$this->addConstructorDependencyToClass($class, $paramNodeType, $paramName);
$this->removeParam($classMethod, $key);

View File

@ -107,7 +107,7 @@ PHP
$factoryObjectType = new ObjectType($factoryClass);
$this->addPropertyToClass($classNode, $factoryObjectType, $propertyName);
$this->addConstructorDependencyToClass($classNode, $factoryObjectType, $propertyName);
}
return new MethodCall(

View File

@ -1,120 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Core\ValueObject\MethodName;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* @see \Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\RemoveConstructorDependencyByParentRectorTest
*/
final class RemoveConstructorDependencyByParentRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var string
* @api
*/
public const PARENT_TYPE_TO_PARAM_TYPES_TO_REMOVE = '$parentsDependenciesToRemove';
/**
* @var array<string, string[]>
*/
private $parentsDependenciesToRemove = [];
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Removes params in constructor by parent type and param names', [
new ConfiguredCodeSample(
<<<'PHP'
class SomeClass extends SomeParentClass
{
public function __construct(SomeType $someType)
{
}
}
PHP
,
<<<'PHP'
class SomeClass extends SomeParentClass
{
public function __construct()
{
}
}
PHP
, [
self::PARENT_TYPE_TO_PARAM_TYPES_TO_REMOVE => [
'SomeParentClass' => ['someType'],
],
]
),
]);
}
/**
* @return class-string[]
*/
public function getNodeTypes(): array
{
return [ClassMethod::class];
}
/**
* @param ClassMethod $node
*/
public function refactor(Node $node): ?Node
{
$parentClassName = $node->getAttribute(AttributeKey::PARENT_CLASS_NAME);
if ($parentClassName === null) {
return null;
}
if (! $this->isName($node, MethodName::CONSTRUCT)) {
return null;
}
foreach ($this->parentsDependenciesToRemove as $parentClass => $paramsToRemove) {
$parentClassName = $node->getAttribute(AttributeKey::PARENT_CLASS_NAME);
if ($parentClassName !== $parentClass) {
continue;
}
$this->removeClassMethodParameterByName($node, $paramsToRemove);
}
return $node;
}
public function configure(array $configuration): void
{
$this->parentsDependenciesToRemove = $configuration[self::PARENT_TYPE_TO_PARAM_TYPES_TO_REMOVE] ?? [];
}
/**
* @param string[] $paramNamesToRemove
*/
private function removeClassMethodParameterByName(ClassMethod $classMethod, array $paramNamesToRemove): void
{
foreach ($paramNamesToRemove as $paramNameToRemove) {
foreach ($classMethod->params as $param) {
if ($param->type === null) {
continue;
}
if (! $this->isName($param->type, $paramNameToRemove)) {
continue;
}
$this->removeNode($param);
}
}
}
}

View File

@ -101,7 +101,7 @@ CODE_SAMPLE
$propertyType = new ObjectType($typeToAdd);
/** @var string $propertyName */
$propertyName = $this->propertyNaming->getExpectedNameFromType($propertyType);
$this->addPropertyToClass($node, $propertyType, $propertyName);
$this->addConstructorDependencyToClass($node, $propertyType, $propertyName);
}
}

View File

@ -158,7 +158,7 @@ PHP
$propertyName = $this->propertyNaming->fqnToVariableName($serviceObjectType);
/** @var Class_ $classLike */
$this->addPropertyToClass($classLike, $serviceObjectType, $propertyName);
$this->addConstructorDependencyToClass($classLike, $serviceObjectType, $propertyName);
return new PropertyFetch(new Variable('this'), new Identifier($propertyName));
}

View File

@ -224,7 +224,7 @@ PHP
throw new ShouldNotHappenException();
}
$this->addPropertyToClass($classLike, $type, $name);
$this->addConstructorDependencyToClass($classLike, $type, $name);
return $property;
}

View File

@ -1,102 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Rector\StaticCall;
use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\Class_;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* @see \Rector\Generic\Tests\Rector\StaticCall\RemoveParentCallByParentRector\RemoveParentCallByParentRectorTest
*/
final class RemoveParentCallByParentRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @api
* @var string
*/
public const PARENT_CLASSES = 'parent_classes';
/**
* @var string[]
*/
private $parentClasses = [];
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Remove parent call by parent class', [
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
final class SomeClass extends SomeParentClass
{
public function run()
{
parent::someCall();
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
final class SomeClass extends SomeParentClass
{
public function run()
{
}
}
CODE_SAMPLE
,
[
self::PARENT_CLASSES => ['SomeParentClass'],
]
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [StaticCall::class];
}
/**
* @param StaticCall $node
*/
public function refactor(Node $node): ?Node
{
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return null;
}
if (! $this->isName($node->class, 'parent')) {
return null;
}
$parentClassName = $node->getAttribute(AttributeKey::PARENT_CLASS_NAME);
foreach ($this->parentClasses as $parentClass) {
if ($parentClassName !== $parentClass) {
continue;
}
$this->removeNode($node);
return null;
}
return null;
}
public function configure(array $configuration): void
{
$this->parentClasses = $configuration[self::PARENT_CLASSES] ?? [];
}
}

View File

@ -1,43 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Fixture;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\ParentClassToRemoveConstructorParamsBy;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\SomeDependencyToBeRemoved;
final class ClassWithExternalConstant extends ParentClassToRemoveConstructorParamsBy
{
/**
* @var SomeDependencyToBeRemoved
*/
private $someDependencyToBeRemoved;
public function __construct(SomeDependencyToBeRemoved $someDependencyToBeRemoved)
{
$this->someDependencyToBeRemoved = $someDependencyToBeRemoved;
}
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Fixture;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\ParentClassToRemoveConstructorParamsBy;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\SomeDependencyToBeRemoved;
final class ClassWithExternalConstant extends ParentClassToRemoveConstructorParamsBy
{
/**
* @var SomeDependencyToBeRemoved
*/
private $someDependencyToBeRemoved;
public function __construct()
{
$this->someDependencyToBeRemoved = $someDependencyToBeRemoved;
}
}
?>

View File

@ -1,43 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Fixture;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\ParentClassToRemoveConstructorParamsBy;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\SomeDependencyToBeRemoved;
final class TypeFirst extends ParentClassToRemoveConstructorParamsBy
{
/**
* @var SomeDependencyToBeRemoved
*/
private $whatever;
public function __construct(SomeDependencyToBeRemoved $whatever)
{
$this->whatever = $whatever;
}
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Fixture;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\ParentClassToRemoveConstructorParamsBy;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\SomeDependencyToBeRemoved;
final class TypeFirst extends ParentClassToRemoveConstructorParamsBy
{
/**
* @var SomeDependencyToBeRemoved
*/
private $whatever;
public function __construct()
{
$this->whatever = $whatever;
}
}
?>

View File

@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Generic\Rector\ClassMethod\RemoveConstructorDependencyByParentRector;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\ParentClassToRemoveConstructorParamsBy;
use Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source\SomeDependencyToBeRemoved;
use Symplify\SmartFileSystem\SmartFileInfo;
final class RemoveConstructorDependencyByParentRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
/**
* @return mixed[]
*/
protected function getRectorsWithConfiguration(): array
{
return [
RemoveConstructorDependencyByParentRector::class => [
RemoveConstructorDependencyByParentRector::PARENT_TYPE_TO_PARAM_TYPES_TO_REMOVE => [
ParentClassToRemoveConstructorParamsBy::class => [SomeDependencyToBeRemoved::class],
],
],
];
}
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source;
abstract class ParentClassToRemoveConstructorParamsBy
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\ClassMethod\RemoveConstructorDependencyByParentRector\Source;
final class SomeDependencyToBeRemoved
{
}

View File

@ -1,30 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\StaticCall\RemoveParentCallByParentRector\Fixture;
use Rector\Generic\Tests\Rector\StaticCall\RemoveParentCallByParentRector\Source\ParentClassToRemoveParentStaticCallBy;
final class SomeClass extends ParentClassToRemoveParentStaticCallBy
{
public function run()
{
parent::run();
}
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\StaticCall\RemoveParentCallByParentRector\Fixture;
use Rector\Generic\Tests\Rector\StaticCall\RemoveParentCallByParentRector\Source\ParentClassToRemoveParentStaticCallBy;
final class SomeClass extends ParentClassToRemoveParentStaticCallBy
{
public function run()
{
}
}
?>

View File

@ -1,39 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\StaticCall\RemoveParentCallByParentRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Generic\Rector\StaticCall\RemoveParentCallByParentRector;
use Rector\Generic\Tests\Rector\StaticCall\RemoveParentCallByParentRector\Source\ParentClassToRemoveParentStaticCallBy;
use Symplify\SmartFileSystem\SmartFileInfo;
final class RemoveParentCallByParentRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
/**
* @return mixed[]
*/
protected function getRectorsWithConfiguration(): array
{
return [
RemoveParentCallByParentRector::class => [
RemoveParentCallByParentRector::PARENT_CLASSES => [ParentClassToRemoveParentStaticCallBy::class],
],
];
}
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\StaticCall\RemoveParentCallByParentRector\Source;
abstract class ParentClassToRemoveParentStaticCallBy
{
}

View File

@ -108,7 +108,7 @@ PHP
}
$fullyQualifiedObjectType = new FullyQualifiedObjectType($functionChange->getClass());
$this->addPropertyToClass($classLike, $fullyQualifiedObjectType, $functionChange->getProperty());
$this->addConstructorDependencyToClass($classLike, $fullyQualifiedObjectType, $functionChange->getProperty());
$propertyFetchNode = $this->createPropertyFetch('this', $functionChange->getProperty());

View File

@ -141,7 +141,7 @@ PHP
$serviceObjectType = new FullyQualifiedObjectType($serviceClass);
$propertyName = $this->propertyNaming->fqnToVariableName($serviceObjectType);
$this->addPropertyToClass($classLike, $serviceObjectType, $propertyName);
$this->addConstructorDependencyToClass($classLike, $serviceObjectType, $propertyName);
$propertyFetchNode = $this->createPropertyFetch('this', $propertyName);
return new MethodCall($propertyFetchNode, $node->name, $node->args);

View File

@ -118,7 +118,7 @@ PHP
/** @var Class_ $classLike */
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
$this->addPropertyToClass(
$this->addConstructorDependencyToClass(
$classLike,
new FullyQualifiedObjectType(EventDispatcherInterface::class),
'eventDispatcher'

View File

@ -185,7 +185,11 @@ PHP
throw new ShouldNotHappenException();
}
$this->addPropertyToClass($classLike, new FullyQualifiedObjectType('Nette\Http\Session'), 'session');
$this->addConstructorDependencyToClass(
$classLike,
new FullyQualifiedObjectType('Nette\Http\Session'),
'session'
);
return $node;
});

View File

@ -89,7 +89,7 @@ PHP
$this->collectGlobalVariableNamesAndRefactorToPropertyFetch($node);
foreach ($this->globalVariableNames as $globalVariableName) {
$this->addPropertyWithoutConstructorToClass($classLike, null, $globalVariableName);
$this->addPropertyToClass($classLike, null, $globalVariableName);
}
return $node;

View File

@ -165,7 +165,7 @@ PHP
$propertyName = $this->propertyNaming->fqnToVariableName($matchedObjectType) . self::FACTORY;
$propertyType = new FullyQualifiedObjectType($matchedObjectType->getClassName() . self::FACTORY);
$this->addPropertyToClass($node, $propertyType, $propertyName);
$this->addConstructorDependencyToClass($node, $propertyType, $propertyName);
}
return $node;

View File

@ -178,7 +178,7 @@ PHP
// add via constructor
foreach ($newPropertyTypes as $newPropertyType) {
$newPropertyName = $this->propertyNaming->fqnToVariableName($newPropertyType);
$this->addPropertyToClass($class, $newPropertyType, $newPropertyName);
$this->addConstructorDependencyToClass($class, $newPropertyType, $newPropertyName);
}
return $class;

View File

@ -200,7 +200,7 @@ PHP
foreach ($staticTypesInClass as $staticType) {
$variableName = $this->propertyNaming->fqnToVariableName($staticType);
$this->addPropertyToClass($class, $staticType, $variableName);
$this->addConstructorDependencyToClass($class, $staticType, $variableName);
// is this an object? create factory for it next to this :)
if ($this->uniqueObjectOrServiceDetector->isUniqueObject()) {

View File

@ -55,7 +55,7 @@ abstract class AbstractToConstructorInjectionRector extends AbstractRector
throw new ShouldNotHappenException();
}
$this->addPropertyToClass($classLike, $serviceType, $propertyName);
$this->addConstructorDependencyToClass($classLike, $serviceType, $propertyName);
return $this->createPropertyFetch('this', $propertyName);
}

View File

@ -102,7 +102,7 @@ PHP
return null;
}
$this->addPropertyToClass($classLike, new StringType(), $propertyName);
$this->addConstructorDependencyToClass($classLike, new StringType(), $propertyName);
return $this->createPropertyFetch('this', $propertyName);
}

View File

@ -36,6 +36,7 @@ use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\UnionType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
@ -234,7 +235,6 @@ final class NodeFactory
$propertyBuilder->makePrivate();
$property = $propertyBuilder->getNode();
$this->addPropertyType($property, $type);
$this->decorateParentPropertyProperty($property);
@ -485,6 +485,11 @@ final class NodeFactory
if ($phpParserType !== null) {
$property->type = $phpParserType;
if ($type instanceof GenericObjectType) {
$phpDocInfo->changeVarType($type);
}
return;
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Doctrine\Bundle\DoctrineBundle\Repository;
if (class_exists('Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository')) {
return;
}
abstract class ServiceEntityRepository
{
}