diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index 3b03f5c5..661868dc 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -1,49 +1,26 @@ -# 19 Rules Overview +# 18 Rules Overview ## AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector -Adds `@return` PHPDoc type to Collection property getter by *ToMany annotation +Adds `@return` PHPDoc type to Collection property getter by *ToMany annotation/attribute - class: [`Rector\Doctrine\CodeQuality\Rector\Class_\AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector`](../rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector.php) ```diff +-use App\Entity\Training; +- /** * @ORM\Entity */ final class Trainer { /** - * @ORM\OneToMany(targetEntity=Training::class, mappedBy="trainer") + * @ORM\OneToMany(targetEntity=Training::class) */ private $trainings; + /** -+ * @return \Doctrine\Common\Collections\Collection -+ */ - public function getTrainings() - { - return $this->trainings; - } - } -``` - -
- -## AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector - -Adds `@return` PHPDoc type to Collection property getter by *ToMany attribute - -- class: [`Rector\Doctrine\CodeQuality\Rector\Class_\AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector`](../rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector.php) - -```diff - #[ORM\Entity] - final class Trainer - { - #[ORM\OneToMany(targetEntity:Training::class, mappedBy:"trainer")] - private $trainings; - -+ /** -+ * @return \Doctrine\Common\Collections\Collection ++ * @return \Doctrine\Common\Collections\Collection + */ public function getTrainings() { diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/ManyToMany/attribute_getter_with_return_type.php b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/ManyToMany/attribute_getter_with_return_type.php new file mode 100644 index 00000000..886d6c75 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/ManyToMany/attribute_getter_with_return_type.php @@ -0,0 +1,21 @@ +trainings; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/ManyToMany/getter_with_return_type.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/ManyToMany/attribute_getter_with_return_type.php.inc similarity index 73% rename from rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/ManyToMany/getter_with_return_type.php.inc rename to rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/ManyToMany/attribute_getter_with_return_type.php.inc index 646da18d..d16fe96b 100644 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/ManyToMany/getter_with_return_type.php.inc +++ b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/ManyToMany/attribute_getter_with_return_type.php.inc @@ -1,10 +1,10 @@ + * @return \Doctrine\Common\Collections\Collection */ public function getTrainings(): Collection { diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/getter_with_return_type.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/OneToMany/attribute_getter_with_return_type.php.inc similarity index 68% rename from rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/getter_with_return_type.php.inc rename to rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/OneToMany/attribute_getter_with_return_type.php.inc index 1365f967..f5479c7c 100644 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/getter_with_return_type.php.inc +++ b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector/Fixture/OneToMany/attribute_getter_with_return_type.php.inc @@ -1,13 +1,13 @@ + * @return \Doctrine\Common\Collections\Collection */ public function getTrainings(): Collection { diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRectorTest.php deleted file mode 100644 index 79c608f4..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRectorTest.php +++ /dev/null @@ -1,28 +0,0 @@ -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/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/association_with_no_target_entity.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/association_with_no_target_entity.php.inc deleted file mode 100644 index b7bb1b0f..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/association_with_no_target_entity.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -trainings; - } -} - -?> ------ -trainings; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/getter_without_return_type.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/getter_without_return_type.php.inc deleted file mode 100644 index ce86311f..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/getter_without_return_type.php.inc +++ /dev/null @@ -1,46 +0,0 @@ -trainings; - } -} - -?> ------ - - */ - public function getTrainings() - { - return $this->trainings; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/multiple_returned_collection_properties.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/multiple_returned_collection_properties.php.inc deleted file mode 100644 index 93521ce3..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/multiple_returned_collection_properties.php.inc +++ /dev/null @@ -1,57 +0,0 @@ -morningTrainings; - } - - return $this->eveningTrainings; - } -} - -?> ------ -morningTrainings; - } - - return $this->eveningTrainings; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/no_collection_methods.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/no_collection_methods.php.inc deleted file mode 100644 index 0c1e6c1f..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/no_collection_methods.php.inc +++ /dev/null @@ -1,39 +0,0 @@ -id; - } -} - -?> ------ -id; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/not_a_collection_property.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/not_a_collection_property.php.inc deleted file mode 100644 index 7228819a..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/not_a_collection_property.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -trainings; - } -} - -?> ------ -trainings; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/not_entity_class.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/not_entity_class.php.inc deleted file mode 100644 index 4755cf63..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/not_entity_class.php.inc +++ /dev/null @@ -1,41 +0,0 @@ -trainings; - } -} - -?> ------ -trainings; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/with_invalid_return_type.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/with_invalid_return_type.php.inc deleted file mode 100644 index 5e78059a..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/with_invalid_return_type.php.inc +++ /dev/null @@ -1,47 +0,0 @@ - - */ - public function getTrainings() - { - return $this->trainings; - } -} - -?> ------ - - */ - public function getTrainings() - { - return $this->trainings; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/with_other_non_collection_methods.php.inc b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/with_other_non_collection_methods.php.inc deleted file mode 100644 index 24bf6d27..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Fixture/OneToMany/with_other_non_collection_methods.php.inc +++ /dev/null @@ -1,76 +0,0 @@ -$name = $value; - } - - public function __get($name) - { - return $this->$name; - } - - public function __isset($name) - { - return isset($this->$name); - } - - public function getTrainings(): Collection - { - return $this->trainings; - } -} - -?> ------ -$name = $value; - } - - public function __get($name) - { - return $this->$name; - } - - public function __isset($name) - { - return isset($this->$name); - } - - /** - * @return \Doctrine\Common\Collections\Collection - */ - public function getTrainings(): Collection - { - return $this->trainings; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Source/Training.php b/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Source/Training.php deleted file mode 100644 index 4438c859..00000000 --- a/rules-tests/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector/Source/Training.php +++ /dev/null @@ -1,10 +0,0 @@ -rule(AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector::class); -}; diff --git a/rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector.php b/rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector.php index 2808dd35..bb4e933b 100644 --- a/rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector.php +++ b/rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector.php @@ -38,16 +38,20 @@ public function __construct( public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Adds @return PHPDoc type to Collection property getter by *ToMany annotation', [ - new CodeSample( - <<<'CODE_SAMPLE' + return new RuleDefinition( + 'Adds @return PHPDoc type to Collection property getter by *ToMany annotation/attribute', + [ + new CodeSample( + <<<'CODE_SAMPLE' +use App\Entity\Training; + /** * @ORM\Entity */ final class Trainer { /** - * @ORM\OneToMany(targetEntity=Training::class, mappedBy="trainer") + * @ORM\OneToMany(targetEntity=Training::class) */ private $trainings; @@ -57,20 +61,20 @@ public function getTrainings() } } CODE_SAMPLE - , - <<<'CODE_SAMPLE' + , + <<<'CODE_SAMPLE' /** * @ORM\Entity */ final class Trainer { /** - * @ORM\OneToMany(targetEntity=Training::class, mappedBy="trainer") + * @ORM\OneToMany(targetEntity=Training::class) */ private $trainings; /** - * @return \Doctrine\Common\Collections\Collection + * @return \Doctrine\Common\Collections\Collection */ public function getTrainings() { @@ -78,8 +82,10 @@ public function getTrainings() } } CODE_SAMPLE - ), - ]); + ), + + ] + ); } /** @@ -111,11 +117,12 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node continue; } - $collectionObjectType = $this->collectionTypeResolver->resolveFromToManyProperties($property); + $collectionObjectType = $this->collectionTypeResolver->resolveFromToManyProperty($property); if (! $collectionObjectType instanceof FullyQualifiedObjectType) { continue; } + // update docblock with known collection type $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); $newVarType = $this->collectionTypeFactory->createType($collectionObjectType); $this->phpDocTypeChanger->changeReturnType($classMethod, $phpDocInfo, $newVarType); diff --git a/rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector.php b/rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector.php index f7c92061..5a2489da 100644 --- a/rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector.php +++ b/rules/CodeQuality/Rector/Class_/AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector.php @@ -5,42 +5,17 @@ namespace Rector\Doctrine\CodeQuality\Rector\Class_; use PhpParser\Node; -use PhpParser\Node\Attribute; -use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Stmt\Class_; -use PhpParser\Node\Stmt\Property; -use PHPStan\Analyser\Scope; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; -use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; -use Rector\Doctrine\CodeQuality\Enum\ToManyMappings; -use Rector\Doctrine\NodeAnalyzer\AttributeFinder; -use Rector\Doctrine\NodeAnalyzer\MethodUniqueReturnedPropertyResolver; -use Rector\Doctrine\NodeAnalyzer\TargetEntityResolver; -use Rector\Doctrine\TypeAnalyzer\CollectionTypeFactory; -use Rector\Rector\AbstractScopeAwareRector; -use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; -use Rector\ValueObject\PhpVersionFeature; -use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard; -use Rector\VersionBonding\Contract\MinPhpVersionInterface; +use Rector\Configuration\Deprecation\Contract\DeprecatedInterface; +use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see \Rector\Doctrine\Tests\CodeQuality\Rector\Class_\AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector\AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRectorTest + * @deprecated This rule is deprecated, as merged into same-feature rule @see \Rector\Doctrine\CodeQuality\Rector\Class_\AddReturnDocBlockToCollectionPropertyGetterByToManyAnnotationRector, use it instead */ -final class AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector extends AbstractScopeAwareRector implements MinPhpVersionInterface +final class AddReturnDocBlockToCollectionPropertyGetterByToManyAttributeRector extends AbstractRector implements DeprecatedInterface { - public function __construct( - private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, - private readonly PhpDocInfoFactory $phpDocInfoFactory, - private readonly PhpDocTypeChanger $phpDocTypeChanger, - private readonly AttributeFinder $attributeFinder, - private readonly TargetEntityResolver $targetEntityResolver, - private readonly CollectionTypeFactory $collectionTypeFactory, - private readonly MethodUniqueReturnedPropertyResolver $methodUniqueReturnedPropertyResolver, - ) { - } - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Adds @return PHPDoc type to Collection property getter by *ToMany attribute', [ @@ -90,73 +65,8 @@ public function getNodeTypes(): array /** * @param Class_ $node */ - public function refactorWithScope(Node $node, Scope $scope): ?Node - { - if (! $this->isDoctrineEntityClass($node)) { - return null; - } - - $hasChanged = false; - - foreach ($node->getMethods() as $classMethod) { - if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($classMethod, $scope)) { - continue; - } - - $property = $this->methodUniqueReturnedPropertyResolver->resolve($node, $classMethod); - - if (! $property instanceof Property) { - continue; - } - - $collectionObjectType = $this->getCollectionObjectTypeFromToManyAttribute($property); - - if (! $collectionObjectType instanceof FullyQualifiedObjectType) { - continue; - } - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - $newVarType = $this->collectionTypeFactory->createType($collectionObjectType); - $this->phpDocTypeChanger->changeReturnType($classMethod, $phpDocInfo, $newVarType); - $hasChanged = true; - } - - return $hasChanged ? $node : null; - } - - public function provideMinPhpVersion(): int - { - return PhpVersionFeature::ATTRIBUTES; - } - - private function getCollectionObjectTypeFromToManyAttribute(Property $property): ?FullyQualifiedObjectType - { - $targetEntityExpr = $this->attributeFinder->findAttributeByClassesArgByName( - $property, - ToManyMappings::TO_MANY_CLASSES, - 'targetEntity' - ); - - if (! $targetEntityExpr instanceof ClassConstFetch) { - return null; - } - - $targetEntityClassName = $this->targetEntityResolver->resolveFromExpr($targetEntityExpr); - - if ($targetEntityClassName === null) { - return null; - } - - return new FullyQualifiedObjectType($targetEntityClassName); - } - - private function isDoctrineEntityClass(Class_ $class): bool + public function refactor(Node $node): ?Node { - $entityAttribute = $this->attributeFinder->findAttributeByClasses( - $class, - ['Doctrine\ORM\Mapping\Entity', 'Doctrine\ORM\Mapping\Embeddable'], - ); - - return $entityAttribute instanceof Attribute; + return null; } } diff --git a/rules/CodeQuality/Rector/Property/ImproveDoctrineCollectionDocTypeInEntityRector.php b/rules/CodeQuality/Rector/Property/ImproveDoctrineCollectionDocTypeInEntityRector.php index 94568faa..fffc84c1 100644 --- a/rules/CodeQuality/Rector/Property/ImproveDoctrineCollectionDocTypeInEntityRector.php +++ b/rules/CodeQuality/Rector/Property/ImproveDoctrineCollectionDocTypeInEntityRector.php @@ -226,7 +226,7 @@ private function refactorPropertyPhpDocInfo(Property $property, PhpDocInfo $phpD $newVarType = $this->collectionTypeFactory->createType($collectionObjectType); $this->phpDocTypeChanger->changeVarType($property, $phpDocInfo, $newVarType); } else { - $collectionObjectType = $this->collectionTypeResolver->resolveFromToManyProperties($property); + $collectionObjectType = $this->collectionTypeResolver->resolveFromToManyProperty($property); if (! $collectionObjectType instanceof FullyQualifiedObjectType) { return null; } diff --git a/src/NodeAnalyzer/AttrinationFinder.php b/src/NodeAnalyzer/AttrinationFinder.php index 0fbe2472..25ef790e 100644 --- a/src/NodeAnalyzer/AttrinationFinder.php +++ b/src/NodeAnalyzer/AttrinationFinder.php @@ -50,7 +50,7 @@ public function hasByOne(Property|Class_|ClassMethod|Param $node, string $name): /** * @param string[] $classNames */ - public function hasByMany(Property $property, array $classNames): bool + public function hasByMany(Class_ | Property $property, array $classNames): bool { $phpDocInfo = $this->phpDocInfoFactory->createFromNode($property); if ($phpDocInfo instanceof PhpDocInfo && $phpDocInfo->hasByAnnotationClasses($classNames)) { @@ -60,4 +60,22 @@ public function hasByMany(Property $property, array $classNames): bool $attribute = $this->attributeFinder->findAttributeByClasses($property, $classNames); return $attribute instanceof Attribute; } + + /** + * @param string[] $classNames + */ + public function getByMany( + Class_ | Property $property, + array $classNames + ): DoctrineAnnotationTagValueNode|Attribute|null { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($property); + if ($phpDocInfo instanceof PhpDocInfo) { + $foundDoctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClasses($classNames); + if ($foundDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { + return $foundDoctrineAnnotationTagValueNode; + } + } + + return $this->attributeFinder->findAttributeByClasses($property, $classNames); + } } diff --git a/src/PhpDocParser/DoctrineDocBlockResolver.php b/src/PhpDocParser/DoctrineDocBlockResolver.php index 24ef0035..1b513bf1 100644 --- a/src/PhpDocParser/DoctrineDocBlockResolver.php +++ b/src/PhpDocParser/DoctrineDocBlockResolver.php @@ -5,23 +5,21 @@ namespace Rector\Doctrine\PhpDocParser; use PhpParser\Node\Stmt\Class_; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Doctrine\Enum\MappingClass; use Rector\Doctrine\Enum\OdmMappingClass; +use Rector\Doctrine\NodeAnalyzer\AttrinationFinder; final readonly class DoctrineDocBlockResolver { public function __construct( - private PhpDocInfoFactory $phpDocInfoFactory + private AttrinationFinder $attrinationFinder, ) { } public function isDoctrineEntityClass(Class_ $class): bool { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($class); - - return $phpDocInfo->hasByAnnotationClasses( - [MappingClass::ENTITY, MappingClass::EMBEDDABLE, OdmMappingClass::DOCUMENT] - ); + return $this->attrinationFinder->hasByMany($class, [ + MappingClass::ENTITY, MappingClass::EMBEDDABLE, OdmMappingClass::DOCUMENT, + ]); } } diff --git a/src/TypeAnalyzer/CollectionTypeResolver.php b/src/TypeAnalyzer/CollectionTypeResolver.php index 57419d8d..c200a25b 100644 --- a/src/TypeAnalyzer/CollectionTypeResolver.php +++ b/src/TypeAnalyzer/CollectionTypeResolver.php @@ -5,6 +5,11 @@ namespace Rector\Doctrine\TypeAnalyzer; use PhpParser\Node; +use PhpParser\Node\Arg; +use PhpParser\Node\Attribute; +use PhpParser\Node\Expr; +use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Identifier; use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; @@ -13,18 +18,30 @@ use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode; use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; use Rector\BetterPhpDocParser\PhpDoc\StringNode; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Doctrine\CodeQuality\Enum\ToManyMappings; +use Rector\Doctrine\NodeAnalyzer\AttrinationFinder; +use Rector\Doctrine\NodeAnalyzer\TargetEntityResolver; use Rector\Doctrine\PhpDoc\ShortClassExpander; use Rector\StaticTypeMapper\Naming\NameScopeFactory; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; final readonly class CollectionTypeResolver { + /** + * @var string + */ + private const TARGET_ENTITY = 'targetEntity'; + + /** + * @var string + */ + private const TARGET_DOCUMENT = 'targetDocument'; + public function __construct( private NameScopeFactory $nameScopeFactory, - private PhpDocInfoFactory $phpDocInfoFactory, - private ShortClassExpander $shortClassExpander + private ShortClassExpander $shortClassExpander, + private AttrinationFinder $attrinationFinder, + private TargetEntityResolver $targetEntityResolver, ) { } @@ -48,18 +65,49 @@ public function resolveFromTypeNode(TypeNode $typeNode, Node $node): ?FullyQuali return null; } - public function resolveFromToManyProperties(Property $property): ?FullyQualifiedObjectType + public function resolveFromToManyProperty(Property $property): ?FullyQualifiedObjectType { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + $doctrineAnnotationTagValueNodeOrAttribute = $this->attrinationFinder->getByMany( + $property, + ToManyMappings::TO_MANY_CLASSES + ); - $doctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClasses(ToManyMappings::TO_MANY_CLASSES); - if (! $doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) { - return null; + if ($doctrineAnnotationTagValueNodeOrAttribute instanceof DoctrineAnnotationTagValueNode) { + return $this->resolveFromDoctrineAnnotationTagValueNode( + $doctrineAnnotationTagValueNodeOrAttribute, + $property + ); } - $targetEntityArrayItemNode = $doctrineAnnotationTagValueNode->getValue('targetEntity'); - // in case of odm - $targetDocumentArrayItemNode = $doctrineAnnotationTagValueNode->getValue('targetDocument'); + if ($doctrineAnnotationTagValueNodeOrAttribute instanceof Attribute) { + $targetEntityExpr = $this->findExprByArgNames( + $doctrineAnnotationTagValueNodeOrAttribute->args, + [self::TARGET_ENTITY, self::TARGET_DOCUMENT] + ); + + if (! $targetEntityExpr instanceof ClassConstFetch) { + return null; + } + + $targetEntityClassName = $this->targetEntityResolver->resolveFromExpr($targetEntityExpr); + if ($targetEntityClassName === null) { + return null; + } + + return new FullyQualifiedObjectType($targetEntityClassName); + } + + return null; + } + + private function resolveFromDoctrineAnnotationTagValueNode( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, + Property $property + ): ?FullyQualifiedObjectType { + $targetEntityArrayItemNode = $doctrineAnnotationTagValueNode->getValue(self::TARGET_ENTITY); + + // in case of ODM + $targetDocumentArrayItemNode = $doctrineAnnotationTagValueNode->getValue(self::TARGET_DOCUMENT); $targetArrayItemNode = $targetEntityArrayItemNode ?: $targetDocumentArrayItemNode; if (! $targetArrayItemNode instanceof ArrayItemNode) { @@ -67,7 +115,6 @@ public function resolveFromToManyProperties(Property $property): ?FullyQualified } $targetEntityClass = $targetArrayItemNode->value; - if ($targetEntityClass instanceof StringNode) { $targetEntityClass = $targetEntityClass->value; } @@ -83,4 +130,24 @@ public function resolveFromToManyProperties(Property $property): ?FullyQualified return new FullyQualifiedObjectType($fullyQualifiedTargetEntity); } + + /** + * @param Arg[] $args + * @param string[] $names + */ + private function findExprByArgNames(array $args, array $names): ?Expr + { + foreach ($args as $arg) { + if (! $arg->name instanceof Identifier) { + continue; + } + + if (in_array($arg->name->toString(), $names, true)) { + return $arg->value; + } + } + + return null; + + } }