Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PHP 8.0] Add NestedAnnotationToAttributeRector #2781

Merged
merged 5 commits into from
Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion packages-tests/PhpAttribute/UseAliasNameMatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ public function test(

// uses
$useAliasMetadata = $this->useAliasNameMatcher->match($uses, $shortAnnotationName, $annotationToAttribute);

$this->assertInstanceOf(UseAliasMetadata::class, $useAliasMetadata);

// test new use import
Expand Down
4 changes: 2 additions & 2 deletions packages/PhpAttribute/NodeFactory/AttributeNameFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -23,7 +23,7 @@ public function __construct(
* @param Use_[] $uses
*/
public function create(
AnnotationToAttribute $annotationToAttribute,
AnnotationToAttributeInterface $annotationToAttribute,
DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode,
array $uses
): FullyQualified|Name {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ public function createArgsFromItems(array $items, string $attributeClass): array
}

/**
* @todo deprecated
*
* @param mixed[] $items
* @return mixed[]
*/
Expand Down
141 changes: 141 additions & 0 deletions packages/PhpAttribute/NodeFactory/PhpNestedAttributeGroupFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

declare(strict_types=1);

namespace Rector\PhpAttribute\NodeFactory;

use Nette\Utils\Strings;
use PhpParser\Node\Arg;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Use_;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\Php80\ValueObject\NestedAnnotationToAttribute;
use Rector\PhpAttribute\AnnotationToAttributeMapper;
use Rector\PhpAttribute\AttributeArrayNameInliner;
use Rector\PhpAttribute\NodeAnalyzer\ExprParameterReflectionTypeCorrector;

final class PhpNestedAttributeGroupFactory
{
/**
* @var string
* @see https://regex101.com/r/g3d9jy/1
*/
private const SHORT_ORM_ALIAS_REGEX = '#^@ORM#';

public function __construct(
private readonly AnnotationToAttributeMapper $annotationToAttributeMapper,
private readonly AttributeNameFactory $attributeNameFactory,
private readonly NamedArgsFactory $namedArgsFactory,
private readonly ExprParameterReflectionTypeCorrector $exprParameterReflectionTypeCorrector,
private readonly AttributeArrayNameInliner $attributeArrayNameInliner
) {
}

/**
* @param Use_[] $uses
*/
public function create(
DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode,
NestedAnnotationToAttribute $nestedAnnotationToAttribute,
array $uses
): AttributeGroup {
$values = $doctrineAnnotationTagValueNode->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);
}
}
63 changes: 0 additions & 63 deletions packages/PhpAttribute/RemovableAnnotationAnalyzer.php

This file was deleted.

4 changes: 2 additions & 2 deletions packages/PhpAttribute/UseAliasNameMatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -21,7 +21,7 @@ final class UseAliasNameMatcher
public function match(
array $uses,
string $shortAnnotationName,
AnnotationToAttribute $annotationToAttribute
AnnotationToAttributeInterface $annotationToAttribute
): ?UseAliasMetadata {
$shortAnnotationName = trim($shortAnnotationName, '@');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\Doctrine;
namespace Rector\Tests\Php80\Rector\Property\NestedAnnotationToAttributeRector\Fixture;

use Doctrine\ORM\Mapping as ORM;

Expand All @@ -19,7 +19,7 @@ final class DoctrineNestedJoinColumns
-----
<?php

namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\Doctrine;
namespace Rector\Tests\Php80\Rector\Property\NestedAnnotationToAttributeRector\Fixture;

use Doctrine\ORM\Mapping as ORM;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

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

use Doctrine\ORM\Mapping as ORM;

final class MultipleInversedJoinColumns
{
/**
* @ORM\JoinTable(name="join_table_name",
* inverseJoinColumns={
* @ORM\JoinColumn(name="target_id"),
* @ORM\JoinColumn(name="another_id")
* }
* )
*/
private $collection;
}

?>
-----
<?php

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

use Doctrine\ORM\Mapping as ORM;

final class MultipleInversedJoinColumns
{
#[ORM\JoinTable(name: 'join_table_name')]
#[ORM\InverseJoinColumn(name: 'target_id')]
#[ORM\InverseJoinColumn(name: 'another_id')]
private $collection;
}

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

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

use Doctrine\ORM\Mapping as ORM;

final class SomeEntity
{
/**
* @ORM\JoinTable(name="join_table_name",
* joinColumns={@ORM\JoinColumn(name="origin_id")},
* inverseJoinColumns={@ORM\JoinColumn(name="target_id")}
* )
*/
private $collection;
}

?>
-----
<?php

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

use Doctrine\ORM\Mapping as ORM;

final class SomeEntity
{
#[ORM\JoinTable(name: 'join_table_name')]
#[ORM\JoinColumn(name: 'origin_id')]
#[ORM\InverseJoinColumn(name: 'target_id')]
private $collection;
}

?>