Skip to content

Commit

Permalink
[Php81] Handle nested Fqcn UniqueConstraint on NestedAnnotationToAttr…
Browse files Browse the repository at this point in the history
…ibuteRector (#5614)

* cs

* add failng test case

* update fixture

* fix add attribute

* [ci-review] Rector Rectify

* throw

* Fix

* fix

* Revert "fix"

This reverts commit 4cff142.

* test normal merge

* fix

* fix phpstan

---------

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>
Co-authored-by: GitHub Action <actions@github.com>
  • Loading branch information
3 people committed Feb 13, 2024
1 parent 539cfeb commit 82d1e1b
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 8 deletions.
1 change: 1 addition & 0 deletions phpstan.neon
Expand Up @@ -75,6 +75,7 @@ parameters:
- src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php
- src/Configuration/RectorConfigBuilder.php
- rules/TypeDeclaration/PHPStan/ObjectTypeSpecifier.php
- src/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php

# is nested expr
-
Expand Down
@@ -1,9 +1,9 @@
<?php

declare(strict_types=1);

use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\UniqueConstraint;

use Rector\Config\RectorConfig;
use Rector\Php80\Rector\Class_\AnnotationToAttributeRector;
use Rector\Php80\ValueObject\AnnotationToAttribute;
Expand Down
@@ -0,0 +1,28 @@
<?php

namespace Rector\Tests\Php80\Rector\Property\NestedAnnotationToAttributeRector\Fixture;

use Doctrine\ORM\Mapping\Table;

/**
* @Table(uniqueConstraints={@\Doctrine\ORM\Mapping\UniqueConstraint(name="some_name")})
*/
class DoctrineUniqueConstraints
{
}

?>
-----
<?php

namespace Rector\Tests\Php80\Rector\Property\NestedAnnotationToAttributeRector\Fixture;

use Doctrine\ORM\Mapping\Table;

#[Table]
#[\Doctrine\ORM\Mapping\UniqueConstraint(name: 'some_name')]
class DoctrineUniqueConstraints
{
}

?>
2 changes: 1 addition & 1 deletion rules/Php80/NodeFactory/NestedAttrGroupsFactory.php
Expand Up @@ -48,6 +48,6 @@ public function create(array $nestedDoctrineTagAndAnnotationToAttributes, array
$attributeGroups = [...$attributeGroups, ...$nestedAttributeGroups];
}

return $attributeGroups;
return array_unique($attributeGroups, SORT_REGULAR);
}
}
Expand Up @@ -189,10 +189,16 @@ private function transformDoctrineAnnotationClassesToAttributeGroups(PhpDocInfo
private function matchAnnotationToAttribute(
DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode
): NestedAnnotationToAttribute|null {
$doctrineResolvedClass = $doctrineAnnotationTagValueNode->identifierTypeNode->getAttribute(
PhpDocAttributeKey::RESOLVED_CLASS
);

foreach ($this->nestedAnnotationsToAttributes as $nestedAnnotationToAttribute) {
$doctrineResolvedClass = $doctrineAnnotationTagValueNode->identifierTypeNode->getAttribute(
PhpDocAttributeKey::RESOLVED_CLASS
);
foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $annotationClass) {
if ($annotationClass->getAttributeClass() === $doctrineResolvedClass) {
return $nestedAnnotationToAttribute;
}
}

if ($doctrineResolvedClass !== $nestedAnnotationToAttribute->getTag()) {
continue;
Expand Down
Expand Up @@ -36,7 +36,7 @@
* @see https://regex101.com/r/bGp2V0/2
* @var string
*/
private const LONG_ANNOTATION_REGEX = '#@\\\\(?<class_name>.*?)(?<annotation_content>\(.*?\)|,|\r?\n|$)#';
public const LONG_ANNOTATION_REGEX = '#@\\\\(?<class_name>.*?)(?<annotation_content>\(.*?\)|,|\r?\n|$)#';

/**
* @see https://regex101.com/r/xWaLOz/1
Expand Down
39 changes: 37 additions & 2 deletions src/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php
Expand Up @@ -11,17 +11,24 @@
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Nop;
use PhpParser\Node\Stmt\Use_;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\TokenIteratorFactory;
use Rector\BetterPhpDocParser\PhpDocParser\DoctrineAnnotationDecorator;
use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Php80\ValueObject\AnnotationPropertyToAttributeClass;
use Rector\Php80\ValueObject\NestedAnnotationToAttribute;
use Rector\PhpAttribute\AnnotationToAttributeMapper;
use Rector\PhpAttribute\AttributeArrayNameInliner;
use Rector\PhpAttribute\NodeAnalyzer\ExprParameterReflectionTypeCorrector;
use Webmozart\Assert\Assert;

final readonly class PhpNestedAttributeGroupFactory
{
Expand All @@ -30,7 +37,9 @@ public function __construct(
private AttributeNameFactory $attributeNameFactory,
private NamedArgsFactory $namedArgsFactory,
private ExprParameterReflectionTypeCorrector $exprParameterReflectionTypeCorrector,
private AttributeArrayNameInliner $attributeArrayNameInliner
private AttributeArrayNameInliner $attributeArrayNameInliner,
private TokenIteratorFactory $tokenIteratorFactory,
private StaticDoctrineAnnotationParser $staticDoctrineAnnotationParser
) {
}

Expand Down Expand Up @@ -212,8 +221,34 @@ private function createFromExplicitProperties(

foreach ($nestedArrayItemNode->value->getValues() as $arrayItemNode) {
$nestedDoctrineAnnotationTagValueNode = $arrayItemNode->value;

if (! $nestedDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
throw new ShouldNotHappenException();
Assert::string($nestedDoctrineAnnotationTagValueNode);

$match = Strings::match($nestedDoctrineAnnotationTagValueNode, DoctrineAnnotationDecorator::LONG_ANNOTATION_REGEX);

if (! isset($match['class_name'])) {
throw new ShouldNotHappenException();
}

$identifierTypeNode = new IdentifierTypeNode($match['class_name']);
$identifierTypeNode->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $match['class_name']);

$annotationContent = $match['annotation_content'] ?? '';
$nestedTokenIterator = $this->tokenIteratorFactory->create($annotationContent);

// mimics doctrine behavior just in phpdoc-parser syntax :)
// https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L742
$values = $this->staticDoctrineAnnotationParser->resolveAnnotationMethodCall(
$nestedTokenIterator,
new Nop()
);

$nestedDoctrineAnnotationTagValueNode = new DoctrineAnnotationTagValueNode(
$identifierTypeNode,
$match['annotation_content'] ?? '',
$values
);
}

$attributeArgs = $this->createAttributeArgs(
Expand Down

0 comments on commit 82d1e1b

Please sign in to comment.