From b8549f547ab72234078416e53f62ba16ab8582c6 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 18 Aug 2022 19:50:31 +0200 Subject: [PATCH] [PHP 8.0] Add NestedAnnotationToAttributeRector (#2781) * [PHP 8.0] Add NestedAnnotationToAttributeRector * remove unused class-string * [PHP 8.0] Add NestedAnnotationToAttribute * move JoinColumns to NestedAnnotationToAttributeRector * [ci-review] Rector Rectify Co-authored-by: GitHub Action --- .../PhpAttribute/UseAliasNameMatcherTest.php | 1 - .../NodeFactory/AttributeNameFactory.php | 4 +- .../NodeFactory/PhpAttributeGroupFactory.php | 2 + .../PhpNestedAttributeGroupFactory.php | 141 +++++++++++++ .../RemovableAnnotationAnalyzer.php | 63 ------ packages/PhpAttribute/UseAliasNameMatcher.php | 4 +- .../Fixture/Doctrine/trailing_comma.php.inc | 10 +- .../config/configured_rule.php | 2 - .../doctrine_nested_join_columns.php.inc | 4 +- .../multiple_inversed_join_columns.php.inc | 36 ++++ .../Fixture/some_class.php.inc | 34 ++++ .../NestedAnnotationToAttributeRectorTest.php | 33 +++ .../config/configured_rule.php | 22 ++ .../AnnotationToAttributeInterface.php | 12 ++ .../NodeFactory/NestedAttrGroupsFactory.php | 53 +++++ .../Class_/AnnotationToAttributeRector.php | 15 +- .../NestedAnnotationToAttributeRector.php | 188 ++++++++++++++++++ .../ValueObject/AnnotationToAttribute.php | 20 +- .../NestedAnnotationToAttribute.php | 45 +++++ ...tedDoctrineTagAndAnnotationToAttribute.php | 26 +++ .../ReplaceStringWithClassConstant.php | 6 +- .../ValueObject/GetAndSetToMethodCall.php | 4 +- .../ValueObject/MethodCallToMethodCall.php | 4 - 23 files changed, 620 insertions(+), 109 deletions(-) create mode 100644 packages/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php delete mode 100644 packages/PhpAttribute/RemovableAnnotationAnalyzer.php rename rules-tests/Php80/Rector/{Class_/AnnotationToAttributeRector/Fixture/Doctrine => Property/NestedAnnotationToAttributeRector/Fixture}/doctrine_nested_join_columns.php.inc (76%) create mode 100644 rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/multiple_inversed_join_columns.php.inc create mode 100644 rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/some_class.php.inc create mode 100644 rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/NestedAnnotationToAttributeRectorTest.php create mode 100644 rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/config/configured_rule.php create mode 100644 rules/Php80/Contract/ValueObject/AnnotationToAttributeInterface.php create mode 100644 rules/Php80/NodeFactory/NestedAttrGroupsFactory.php create mode 100644 rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php create mode 100644 rules/Php80/ValueObject/NestedAnnotationToAttribute.php create mode 100644 rules/Php80/ValueObject/NestedDoctrineTagAndAnnotationToAttribute.php diff --git a/packages-tests/PhpAttribute/UseAliasNameMatcherTest.php b/packages-tests/PhpAttribute/UseAliasNameMatcherTest.php index 38108ff27c9..9d953db1c46 100644 --- a/packages-tests/PhpAttribute/UseAliasNameMatcherTest.php +++ b/packages-tests/PhpAttribute/UseAliasNameMatcherTest.php @@ -47,7 +47,6 @@ public function test( // uses $useAliasMetadata = $this->useAliasNameMatcher->match($uses, $shortAnnotationName, $annotationToAttribute); - $this->assertInstanceOf(UseAliasMetadata::class, $useAliasMetadata); // test new use import diff --git a/packages/PhpAttribute/NodeFactory/AttributeNameFactory.php b/packages/PhpAttribute/NodeFactory/AttributeNameFactory.php index 01744dc00d3..99e0b51c42a 100644 --- a/packages/PhpAttribute/NodeFactory/AttributeNameFactory.php +++ b/packages/PhpAttribute/NodeFactory/AttributeNameFactory.php @@ -8,7 +8,7 @@ use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Use_; use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; -use Rector\Php80\ValueObject\AnnotationToAttribute; +use Rector\Php80\Contract\ValueObject\AnnotationToAttributeInterface; use Rector\PhpAttribute\UseAliasNameMatcher; use Rector\PhpAttribute\ValueObject\UseAliasMetadata; @@ -23,7 +23,7 @@ public function __construct( * @param Use_[] $uses */ public function create( - AnnotationToAttribute $annotationToAttribute, + AnnotationToAttributeInterface $annotationToAttribute, DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, array $uses ): FullyQualified|Name { diff --git a/packages/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php b/packages/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php index d82531d6892..c16684f6305 100644 --- a/packages/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php +++ b/packages/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php @@ -104,6 +104,8 @@ public function createArgsFromItems(array $items, string $attributeClass): array } /** + * @todo deprecated + * * @param mixed[] $items * @return mixed[] */ diff --git a/packages/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php b/packages/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php new file mode 100644 index 00000000000..1f6861ee755 --- /dev/null +++ b/packages/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php @@ -0,0 +1,141 @@ +getValuesWithExplicitSilentAndWithoutQuotes(); + + $args = $this->createArgsFromItems($values, $nestedAnnotationToAttribute); + $args = $this->attributeArrayNameInliner->inlineArrayToArgs($args); + + $attributeName = $this->attributeNameFactory->create( + $nestedAnnotationToAttribute, + $doctrineAnnotationTagValueNode, + $uses + ); + + $attribute = new Attribute($attributeName, $args); + return new AttributeGroup([$attribute]); + } + + /** + * @return AttributeGroup[] + */ + public function createNested( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, + NestedAnnotationToAttribute $nestedAnnotationToAttribute, + ): array { + $attributeGroups = []; + + $values = $doctrineAnnotationTagValueNode->getValuesWithExplicitSilentAndWithoutQuotes(); + + foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $itemName => $nestedAttributeClass) { + $nestedValues = $values[$itemName] ?? null; + if ($nestedValues === null) { + continue; + } + + if ($nestedValues instanceof CurlyListNode) { + foreach ($nestedValues->getValues() as $nestedDoctrineAnnotationTagValueNode) { + /** @var DoctrineAnnotationTagValueNode $nestedDoctrineAnnotationTagValueNode */ + $args = $this->createArgsFromItems( + $nestedDoctrineAnnotationTagValueNode->getValuesWithExplicitSilentAndWithoutQuotes(), + $nestedAnnotationToAttribute + ); + + $args = $this->attributeArrayNameInliner->inlineArrayToArgs($args); + + $originalIdentifier = $nestedDoctrineAnnotationTagValueNode->identifierTypeNode->name; + + $attributeName = $this->resolveAliasedAttributeName($originalIdentifier, $nestedAttributeClass); + + $attribute = new Attribute($attributeName, $args); + $attributeGroups[] = new AttributeGroup([$attribute]); + } + } + } + + return $attributeGroups; + } + + /** + * @param mixed[] $items + * @return Arg[] + */ + private function createArgsFromItems(array $items, NestedAnnotationToAttribute $nestedAnnotationToAttribute): array + { + // remove nested items + foreach (array_keys($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses()) as $itemName) { + unset($items[$itemName]); + } + + /** @var Expr[]|Expr\Array_ $items */ + $items = $this->annotationToAttributeMapper->map($items); + + $items = $this->exprParameterReflectionTypeCorrector->correctItemsByAttributeClass( + $items, + $nestedAnnotationToAttribute->getTag() + ); + + return $this->namedArgsFactory->createFromValues($items); + } + + /** + * @todo improve this hardcoded approach later + */ + private function resolveAliasedAttributeName( + string $originalIdentifier, + string $nestedAttributeClass + ): FullyQualified|Name { + $matches = Strings::match($originalIdentifier, self::SHORT_ORM_ALIAS_REGEX); + + if ($matches !== null) { + // or alias + $shortDoctrineAttributeName = Strings::after($nestedAttributeClass, '\\', -1); + return new Name('ORM\\' . $shortDoctrineAttributeName); + } + + return new FullyQualified($nestedAttributeClass); + } +} diff --git a/packages/PhpAttribute/RemovableAnnotationAnalyzer.php b/packages/PhpAttribute/RemovableAnnotationAnalyzer.php deleted file mode 100644 index c34d6fc9268..00000000000 --- a/packages/PhpAttribute/RemovableAnnotationAnalyzer.php +++ /dev/null @@ -1,63 +0,0 @@ -annotationsToAttributes = $annotationsToAttributes; - } - - public function isRemovable(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode): bool - { - $annotationClassName = $doctrineAnnotationTagValueNode->identifierTypeNode->getAttribute( - PhpDocAttributeKey::RESOLVED_CLASS - ); - - $annotationToAttribute = $this->matchAnnotationToAttribute($doctrineAnnotationTagValueNode); - - // the nested annotation should be convertable - if (! $annotationToAttribute instanceof AnnotationToAttribute) { - return false; - } - - return in_array($annotationClassName, self::REMOVABLE_ANNOTATION_CLASSES, true); - } - - private function matchAnnotationToAttribute( - DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode - ): AnnotationToAttribute|null { - foreach ($this->annotationsToAttributes as $annotationToAttribute) { - if (! $doctrineAnnotationTagValueNode->hasClassName($annotationToAttribute->getTag())) { - continue; - } - - return $annotationToAttribute; - } - - return null; - } -} diff --git a/packages/PhpAttribute/UseAliasNameMatcher.php b/packages/PhpAttribute/UseAliasNameMatcher.php index f65d68c906c..60f3f980161 100644 --- a/packages/PhpAttribute/UseAliasNameMatcher.php +++ b/packages/PhpAttribute/UseAliasNameMatcher.php @@ -7,7 +7,7 @@ use PhpParser\Node\Stmt\Use_; use PhpParser\Node\Stmt\UseUse; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\Php80\ValueObject\AnnotationToAttribute; +use Rector\Php80\Contract\ValueObject\AnnotationToAttributeInterface; use Rector\PhpAttribute\ValueObject\UseAliasMetadata; /** @@ -21,7 +21,7 @@ final class UseAliasNameMatcher public function match( array $uses, string $shortAnnotationName, - AnnotationToAttribute $annotationToAttribute + AnnotationToAttributeInterface $annotationToAttribute ): ?UseAliasMetadata { $shortAnnotationName = trim($shortAnnotationName, '@'); diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/trailing_comma.php.inc b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/trailing_comma.php.inc index 8a0aad36349..34ca533962c 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/trailing_comma.php.inc +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/trailing_comma.php.inc @@ -2,13 +2,13 @@ namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\Doctrine; -use Doctrine\ORM\Mapping as ORM; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation; final class TrailingComma { /** - * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true, ) - **/ + * @GenericAnnotation(key="value", ) + */ protected $someColumn; } @@ -18,11 +18,11 @@ final class TrailingComma namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\Doctrine; -use Doctrine\ORM\Mapping as ORM; +use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\GenericAnnotation; final class TrailingComma { - #[ORM\JoinColumn(name: 'parent_id', referencedColumnName: 'id', nullable: true)] + #[GenericAnnotation(key: 'value')] protected $someColumn; } diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/configured_rule.php b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/configured_rule.php index f20692e64d0..f875e6c9cda 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/configured_rule.php +++ b/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/config/configured_rule.php @@ -34,8 +34,6 @@ new AnnotationToAttribute('Doctrine\ORM\Mapping\Table'), new AnnotationToAttribute('Doctrine\ORM\Mapping\Index'), new AnnotationToAttribute('Doctrine\ORM\Mapping\UniqueConstraint'), - new AnnotationToAttribute('Doctrine\ORM\Mapping\JoinColumns'), - new AnnotationToAttribute('Doctrine\ORM\Mapping\JoinColumn'), new AnnotationToAttribute('Doctrine\ORM\Mapping\DiscriminatorMap'), // validation diff --git a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/doctrine_nested_join_columns.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns.php.inc similarity index 76% rename from rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/doctrine_nested_join_columns.php.inc rename to rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns.php.inc index 578d1f80049..745183d13dc 100644 --- a/rules-tests/Php80/Rector/Class_/AnnotationToAttributeRector/Fixture/Doctrine/doctrine_nested_join_columns.php.inc +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/doctrine_nested_join_columns.php.inc @@ -1,6 +1,6 @@ +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/some_class.php.inc b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..b3795e3559b --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/Fixture/some_class.php.inc @@ -0,0 +1,34 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/NestedAnnotationToAttributeRectorTest.php b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/NestedAnnotationToAttributeRectorTest.php new file mode 100644 index 00000000000..8528ef863b8 --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/NestedAnnotationToAttributeRectorTest.php @@ -0,0 +1,33 @@ +doTestFileInfo($fileInfo); + } + + /** + * @return Iterator + */ + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/config/configured_rule.php b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/config/configured_rule.php new file mode 100644 index 00000000000..7dde0251262 --- /dev/null +++ b/rules-tests/Php80/Rector/Property/NestedAnnotationToAttributeRector/config/configured_rule.php @@ -0,0 +1,22 @@ +ruleWithConfiguration(NestedAnnotationToAttributeRector::class, [ + /** @see https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/reference/attributes-reference.html#joincolumn-inversejoincolumn */ + new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\JoinTable', [ + 'joinColumns' => 'Doctrine\ORM\Mapping\JoinColumn', + 'inverseJoinColumns' => 'Doctrine\ORM\Mapping\InverseJoinColumn', + ]), + + /** @see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#joincolumns */ + new NestedAnnotationToAttribute('Doctrine\ORM\Mapping\JoinColumns', [ + 'Doctrine\ORM\Mapping\JoinColumn', + ], true), + ]); +}; diff --git a/rules/Php80/Contract/ValueObject/AnnotationToAttributeInterface.php b/rules/Php80/Contract/ValueObject/AnnotationToAttributeInterface.php new file mode 100644 index 00000000000..f8edae5eae9 --- /dev/null +++ b/rules/Php80/Contract/ValueObject/AnnotationToAttributeInterface.php @@ -0,0 +1,12 @@ +getDoctrineAnnotationTagValueNode(); + + $nestedAnnotationToAttribute = $nestedDoctrineTagAndAnnotationToAttribute->getNestedAnnotationToAttribute(); + + // do not create alternative for the annotation, only unwrap + if (! $nestedAnnotationToAttribute->shouldRemoveOriginal()) { + // add attributes + $attributeGroups[] = $this->phpNestedAttributeGroupFactory->create( + $doctrineAnnotationTagValueNode, + $nestedDoctrineTagAndAnnotationToAttribute->getNestedAnnotationToAttribute(), + $uses + ); + } + + $nestedAttributeGroups = $this->phpNestedAttributeGroupFactory->createNested( + $doctrineAnnotationTagValueNode, + $nestedDoctrineTagAndAnnotationToAttribute->getNestedAnnotationToAttribute(), + ); + + $attributeGroups = array_merge($attributeGroups, $nestedAttributeGroups); + } + + return $attributeGroups; + } +} diff --git a/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php b/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php index 1f50d319bdd..9b4cfc770ab 100644 --- a/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php +++ b/rules/Php80/Rector/Class_/AnnotationToAttributeRector.php @@ -30,7 +30,6 @@ use Rector\Php80\ValueObject\AnnotationToAttribute; use Rector\Php80\ValueObject\DoctrineTagAndAnnotationToAttribute; use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory; -use Rector\PhpAttribute\RemovableAnnotationAnalyzer; use Rector\PhpAttribute\UnwrapableAnnotationAnalyzer; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\Astral\PhpDocParser\PhpDocNodeTraverser; @@ -56,7 +55,6 @@ public function __construct( private readonly PhpDocTagRemover $phpDocTagRemover, private readonly PhpDocNodeFinder $phpDocNodeFinder, private readonly UnwrapableAnnotationAnalyzer $unwrapableAnnotationAnalyzer, - private readonly RemovableAnnotationAnalyzer $removableAnnotationAnalyzer, private readonly AttributeGroupNamedArgumentManipulator $attributeGroupNamedArgumentManipulator, private readonly PhpVersionProvider $phpVersionProvider, private readonly UseImportsResolver $useImportsResolver, @@ -152,7 +150,6 @@ public function configure(array $configuration): void $this->annotationsToAttributes = $configuration; $this->unwrapableAnnotationAnalyzer->configure($configuration); - $this->removableAnnotationAnalyzer->configure($configuration); } public function provideMinPhpVersion(): int @@ -258,14 +255,10 @@ private function processDoctrineAnnotationClasses(PhpDocInfo $phpDocInfo, array $shouldInlinedNested = true; } - if (! $this->removableAnnotationAnalyzer->isRemovable($doctrineTagValueNode)) { - $doctrineTagAndAnnotationToAttributes[] = new DoctrineTagAndAnnotationToAttribute( - $doctrineTagValueNode, - $annotationToAttribute, - ); - } else { - $shouldInlinedNested = true; - } + $doctrineTagAndAnnotationToAttributes[] = new DoctrineTagAndAnnotationToAttribute( + $doctrineTagValueNode, + $annotationToAttribute, + ); if ($shouldInlinedNested) { // inline nested diff --git a/rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php b/rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php new file mode 100644 index 00000000000..3049e2088ac --- /dev/null +++ b/rules/Php80/Rector/Property/NestedAnnotationToAttributeRector.php @@ -0,0 +1,188 @@ + 'Doctrine\ORM\Mapping\JoinColumn', + 'inverseJoinColumns' => 'Doctrine\ORM\Mapping\InverseJoinColumn', + ]), + ], + ] + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Property::class]; + } + + /** + * @param Property $node + */ + public function refactor(Node $node): ?Node + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $phpDocInfo instanceof PhpDocInfo) { + return null; + } + + $uses = $this->useImportsResolver->resolveBareUsesForNode($node); + + $attributeGroups = $this->transformDoctrineAnnotationClassesToAttributeGroups($phpDocInfo, $uses); + if ($attributeGroups === []) { + return null; + } + + $node->attrGroups = $attributeGroups; + + return $node; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsInstanceOf($configuration, NestedAnnotationToAttribute::class); + $this->nestedAnnotationsToAttributes = $configuration; + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_80; + } + + /** + * @param Node\Stmt\Use_[] $uses + * @return AttributeGroup[] + */ + private function transformDoctrineAnnotationClassesToAttributeGroups(PhpDocInfo $phpDocInfo, array $uses): array + { + if ($phpDocInfo->getPhpDocNode()->children === []) { + return []; + } + + $nestedDoctrineTagAndAnnotationToAttributes = []; + + foreach ($phpDocInfo->getPhpDocNode()->children as $phpDocChildNode) { + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if (! $phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) { + continue; + } + + $doctrineTagValueNode = $phpDocChildNode->value; + $nestedAnnotationToAttribute = $this->matchAnnotationToAttribute($doctrineTagValueNode); + if (! $nestedAnnotationToAttribute instanceof NestedAnnotationToAttribute) { + continue; + } + + $this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineTagValueNode); + + $nestedDoctrineTagAndAnnotationToAttributes[] = new NestedDoctrineTagAndAnnotationToAttribute( + $doctrineTagValueNode, + $nestedAnnotationToAttribute, + ); + } + + return $this->nestedAttrGroupsFactory->create($nestedDoctrineTagAndAnnotationToAttributes, $uses); + } + + private function matchAnnotationToAttribute( + DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode + ): NestedAnnotationToAttribute|null { + foreach ($this->nestedAnnotationsToAttributes as $nestedAnnotationToAttribute) { + $doctrineResolvedClass = $doctrineAnnotationTagValueNode->identifierTypeNode->getAttribute( + PhpDocAttributeKey::RESOLVED_CLASS + ); + + if ($doctrineResolvedClass !== $nestedAnnotationToAttribute->getTag()) { + continue; + } + + return $nestedAnnotationToAttribute; + } + + return null; + } +} diff --git a/rules/Php80/ValueObject/AnnotationToAttribute.php b/rules/Php80/ValueObject/AnnotationToAttribute.php index 8507383aec3..ca0c7184008 100644 --- a/rules/Php80/ValueObject/AnnotationToAttribute.php +++ b/rules/Php80/ValueObject/AnnotationToAttribute.php @@ -4,29 +4,27 @@ namespace Rector\Php80\ValueObject; -final class AnnotationToAttribute +use Rector\Core\Validation\RectorAssert; +use Rector\Php80\Contract\ValueObject\AnnotationToAttributeInterface; + +final class AnnotationToAttribute implements AnnotationToAttributeInterface { - /** - * @param class-string|string $tag - * @param class-string|null $attributeClass - */ public function __construct( private readonly string $tag, private readonly ?string $attributeClass = null ) { + RectorAssert::className($tag); + + if (is_string($attributeClass)) { + RectorAssert::className($attributeClass); + } } - /** - * @return class-string|string - */ public function getTag(): string { return $this->tag; } - /** - * @return class-string - */ public function getAttributeClass(): string { if ($this->attributeClass === null) { diff --git a/rules/Php80/ValueObject/NestedAnnotationToAttribute.php b/rules/Php80/ValueObject/NestedAnnotationToAttribute.php new file mode 100644 index 00000000000..44b633dad7a --- /dev/null +++ b/rules/Php80/ValueObject/NestedAnnotationToAttribute.php @@ -0,0 +1,45 @@ +|string[] $annotationPropertiesToAttributeClasses + */ + public function __construct( + private readonly string $tag, + private readonly array $annotationPropertiesToAttributeClasses, + private readonly bool $removeOriginal = false + ) { + RectorAssert::className($tag); + } + + public function getTag(): string + { + return $this->tag; + } + + /** + * @return array|string[] + */ + public function getAnnotationPropertiesToAttributeClasses(): array + { + return $this->annotationPropertiesToAttributeClasses; + } + + public function getAttributeClass(): string + { + return $this->tag; + } + + public function shouldRemoveOriginal(): bool + { + return $this->removeOriginal; + } +} diff --git a/rules/Php80/ValueObject/NestedDoctrineTagAndAnnotationToAttribute.php b/rules/Php80/ValueObject/NestedDoctrineTagAndAnnotationToAttribute.php new file mode 100644 index 00000000000..a8e3c99f19f --- /dev/null +++ b/rules/Php80/ValueObject/NestedDoctrineTagAndAnnotationToAttribute.php @@ -0,0 +1,26 @@ +doctrineAnnotationTagValueNode; + } + + public function getNestedAnnotationToAttribute(): NestedAnnotationToAttribute + { + return $this->nestedAnnotationToAttribute; + } +} diff --git a/rules/Privatization/ValueObject/ReplaceStringWithClassConstant.php b/rules/Privatization/ValueObject/ReplaceStringWithClassConstant.php index 5cf3f7a1050..3c4d6023c33 100644 --- a/rules/Privatization/ValueObject/ReplaceStringWithClassConstant.php +++ b/rules/Privatization/ValueObject/ReplaceStringWithClassConstant.php @@ -9,9 +9,6 @@ final class ReplaceStringWithClassConstant { - /** - * @param class-string $classWithConstants - */ public function __construct( private readonly string $class, private readonly string $method, @@ -20,6 +17,9 @@ public function __construct( private readonly bool $caseInsensitive = false ) { RectorAssert::className($class); + RectorAssert::methodName($method); + + RectorAssert::className($classWithConstants); } public function getObjectType(): ObjectType diff --git a/rules/Transform/ValueObject/GetAndSetToMethodCall.php b/rules/Transform/ValueObject/GetAndSetToMethodCall.php index 0fb87e74f41..8b4afb46340 100644 --- a/rules/Transform/ValueObject/GetAndSetToMethodCall.php +++ b/rules/Transform/ValueObject/GetAndSetToMethodCall.php @@ -9,14 +9,12 @@ final class GetAndSetToMethodCall { - /** - * @param class-string $classType - */ public function __construct( private readonly string $classType, private readonly string $getMethod, private readonly string $setMethod ) { + RectorAssert::className($classType); RectorAssert::methodName($getMethod); RectorAssert::methodName($setMethod); } diff --git a/rules/Transform/ValueObject/MethodCallToMethodCall.php b/rules/Transform/ValueObject/MethodCallToMethodCall.php index 6d06128d70d..b8bfc8e108f 100644 --- a/rules/Transform/ValueObject/MethodCallToMethodCall.php +++ b/rules/Transform/ValueObject/MethodCallToMethodCall.php @@ -8,10 +8,6 @@ final class MethodCallToMethodCall { - /** - * @param class-string $oldType - * @param class-string $newType - */ public function __construct( private readonly string $oldType, private readonly string $oldMethod,