mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-29 19:37:55 +01:00
[PHP 8.0] Refactor attributes from magic interface to explicit list (#5926)
This commit is contained in:
parent
44375f6637
commit
68026636bf
@ -27,7 +27,6 @@ use Rector\Core\Configuration\CurrentNodeProvider;
|
||||
use Rector\Core\Exception\NotImplementedYetException;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Util\StaticInstanceOf;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
|
||||
/**
|
||||
@ -493,7 +492,6 @@ final class PhpDocInfo
|
||||
$desiredTypes = array_merge([
|
||||
PhpDocTagValueNode::class,
|
||||
PhpDocTagNode::class,
|
||||
PhpAttributableTagNodeInterface::class,
|
||||
], NodeTypes::TYPE_AWARE_NODES);
|
||||
|
||||
if (StaticInstanceOf::isOneOf($type, $desiredTypes)) {
|
||||
|
@ -7,13 +7,11 @@ namespace Rector\BetterPhpDocParser\ValueObject\PhpDoc;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
|
||||
/**
|
||||
* Use by Symfony to autowire dependencies outside constructor,
|
||||
* @see https://symfony.com/doc/current/service_container/autowiring.html#autowiring-other-methods-e-g-setters-and-public-typed-properties
|
||||
*/
|
||||
final class SymfonyRequiredTagNode extends PhpDocTagNode implements PhpAttributableTagNodeInterface
|
||||
final class SymfonyRequiredTagNode extends PhpDocTagNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@ -34,17 +32,4 @@ final class SymfonyRequiredTagNode extends PhpDocTagNode implements PhpAttributa
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return 'Symfony\Contracts\Service\Attribute\Required';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getAttributableItems(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,14 @@ abstract class AbstractTagValueNode implements PhpDocTagValueNode
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getItemsWithoutDefaults(): array
|
||||
{
|
||||
return $this->filterOutMissingItems($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
|
@ -10,11 +10,4 @@ use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\AbstractTagValueNode;
|
||||
|
||||
abstract class AbstractDoctrineTagValueNode extends AbstractTagValueNode implements DoctrineTagNodeInterface, ShortNameAwareTagInterface
|
||||
{
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getAttributableItems(): array
|
||||
{
|
||||
return $this->filterOutMissingItems($this->items);
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,8 @@ declare(strict_types=1);
|
||||
namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Class_;
|
||||
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
|
||||
final class EntityTagValueNode extends AbstractDoctrineTagValueNode implements PhpAttributableTagNodeInterface
|
||||
final class EntityTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@ -34,17 +32,4 @@ final class EntityTagValueNode extends AbstractDoctrineTagValueNode implements P
|
||||
{
|
||||
return '@ORM\Entity';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getAttributableItems(): array
|
||||
{
|
||||
return $this->filterOutMissingItems($this->items);
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,8 @@ declare(strict_types=1);
|
||||
namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_;
|
||||
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
|
||||
final class ColumnTagValueNode extends AbstractDoctrineTagValueNode implements PhpAttributableTagNodeInterface
|
||||
final class ColumnTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
public function changeType(string $type): void
|
||||
{
|
||||
@ -37,9 +35,4 @@ final class ColumnTagValueNode extends AbstractDoctrineTagValueNode implements P
|
||||
{
|
||||
return $this->items['options'] ?? [];
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,11 @@ namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_;
|
||||
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\SilentKeyNodeInterface;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\TagValueNodeReprintTest
|
||||
*/
|
||||
final class GeneratedValueTagValueNode extends AbstractDoctrineTagValueNode implements PhpAttributableTagNodeInterface, SilentKeyNodeInterface
|
||||
final class GeneratedValueTagValueNode extends AbstractDoctrineTagValueNode implements SilentKeyNodeInterface
|
||||
{
|
||||
public function getShortName(): string
|
||||
{
|
||||
@ -23,9 +21,4 @@ final class GeneratedValueTagValueNode extends AbstractDoctrineTagValueNode impl
|
||||
{
|
||||
return 'strategy';
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
|
||||
}
|
||||
}
|
||||
|
@ -5,18 +5,11 @@ declare(strict_types=1);
|
||||
namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_;
|
||||
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
|
||||
final class IdTagValueNode extends AbstractDoctrineTagValueNode implements PhpAttributableTagNodeInterface
|
||||
final class IdTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
public function getShortName(): string
|
||||
{
|
||||
return '@ORM\Id';
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,8 @@ use Rector\BetterPhpDocParser\Contract\PhpDocNode\TagAwareNodeInterface;
|
||||
use Rector\BetterPhpDocParser\Printer\ArrayPartPhpDocTagPrinter;
|
||||
use Rector\BetterPhpDocParser\Printer\TagValueNodePrinter;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
|
||||
final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode implements TagAwareNodeInterface, PhpAttributableTagNodeInterface
|
||||
final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode implements TagAwareNodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@ -64,9 +62,4 @@ final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode implemen
|
||||
{
|
||||
$this->shortName = $shortName;
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,8 @@ use Rector\BetterPhpDocParser\Printer\TagValueNodePrinter;
|
||||
use Rector\BetterPhpDocParser\ValueObject\AroundSpaces;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTagValueNode;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\PhpAttribute\Contract\ManyPhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
|
||||
final class JoinTableTagValueNode extends AbstractDoctrineTagValueNode implements PhpAttributableTagNodeInterface, ManyPhpAttributableTagNodeInterface
|
||||
final class JoinTableTagValueNode extends AbstractDoctrineTagValueNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@ -96,47 +93,6 @@ final class JoinTableTagValueNode extends AbstractDoctrineTagValueNode implement
|
||||
return '@ORM\JoinTable';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getAttributableItems(): array
|
||||
{
|
||||
$items = [];
|
||||
|
||||
if ($this->name !== null) {
|
||||
$items['name'] = $this->name;
|
||||
}
|
||||
|
||||
if ($this->schema !== null) {
|
||||
$items['schema'] = $this->schema;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed[]>
|
||||
*/
|
||||
public function provide(): array
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->joinColumns as $joinColumn) {
|
||||
$items[$joinColumn->getShortName()] = $joinColumn->getAttributableItems();
|
||||
}
|
||||
|
||||
foreach ($this->inverseJoinColumns as $inverseJoinColumn) {
|
||||
$items['@ORM\InverseJoinColumn'] = $inverseJoinColumn->getAttributableItems();
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
|
@ -10,10 +10,8 @@ use Rector\BetterPhpDocParser\Contract\Doctrine\ToManyTagNodeInterface;
|
||||
use Rector\BetterPhpDocParser\Printer\ArrayPartPhpDocTagPrinter;
|
||||
use Rector\BetterPhpDocParser\Printer\TagValueNodePrinter;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
|
||||
final class ManyToManyTagValueNode extends AbstractDoctrineTagValueNode implements ToManyTagNodeInterface, MappedByNodeInterface, InversedByNodeInterface, PhpAttributableTagNodeInterface
|
||||
final class ManyToManyTagValueNode extends AbstractDoctrineTagValueNode implements ToManyTagNodeInterface, MappedByNodeInterface, InversedByNodeInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@ -76,9 +74,4 @@ final class ManyToManyTagValueNode extends AbstractDoctrineTagValueNode implemen
|
||||
{
|
||||
return '@ORM\ManyToMany';
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,11 @@ use Rector\BetterPhpDocParser\Contract\PhpDocNode\ClassNameAwareTagInterface;
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\ShortNameAwareTagInterface;
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\SilentKeyNodeInterface;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\AbstractTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\TagValueNodeReprintTest
|
||||
*/
|
||||
final class SymfonyRouteTagValueNode extends AbstractTagValueNode implements ShortNameAwareTagInterface, SilentKeyNodeInterface, PhpAttributableTagNodeInterface, ClassNameAwareTagInterface
|
||||
final class SymfonyRouteTagValueNode extends AbstractTagValueNode implements ShortNameAwareTagInterface, SilentKeyNodeInterface, ClassNameAwareTagInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@ -59,19 +58,6 @@ final class SymfonyRouteTagValueNode extends AbstractTagValueNode implements Sho
|
||||
$this->tagValueNodeConfiguration->mimic($abstractTagValueNode->tagValueNodeConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getAttributableItems(): array
|
||||
{
|
||||
return $this->filterOutMissingItems($this->items);
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return self::CLASS_NAME;
|
||||
}
|
||||
|
||||
public function getClassName(): string
|
||||
{
|
||||
return self::CLASS_NAME;
|
||||
|
@ -7,12 +7,11 @@ namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\Validator\Con
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\ShortNameAwareTagInterface;
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\SilentKeyNodeInterface;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\AbstractTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint\TagValueNodeReprintTest
|
||||
*/
|
||||
final class AssertEmailTagValueNode extends AbstractTagValueNode implements ShortNameAwareTagInterface, PhpAttributableTagNodeInterface, SilentKeyNodeInterface
|
||||
final class AssertEmailTagValueNode extends AbstractTagValueNode implements ShortNameAwareTagInterface, SilentKeyNodeInterface
|
||||
{
|
||||
public function getShortName(): string
|
||||
{
|
||||
@ -23,17 +22,4 @@ final class AssertEmailTagValueNode extends AbstractTagValueNode implements Shor
|
||||
{
|
||||
return 'choices';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getAttributableItems(): array
|
||||
{
|
||||
return $this->filterOutMissingItems($this->items);
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return 'Symfony\Component\Validator\Constraints\Email';
|
||||
}
|
||||
}
|
||||
|
@ -6,25 +6,11 @@ namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\Validator\Con
|
||||
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\ShortNameAwareTagInterface;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\AbstractTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
|
||||
final class AssertRangeTagValueNode extends AbstractTagValueNode implements ShortNameAwareTagInterface, PhpAttributableTagNodeInterface
|
||||
final class AssertRangeTagValueNode extends AbstractTagValueNode implements ShortNameAwareTagInterface
|
||||
{
|
||||
public function getShortName(): string
|
||||
{
|
||||
return '@Assert\Range';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getAttributableItems(): array
|
||||
{
|
||||
return $this->filterOutMissingItems($this->items);
|
||||
}
|
||||
|
||||
public function getAttributeClassName(): string
|
||||
{
|
||||
return 'Symfony\Component\Validator\Constraints\Range';
|
||||
}
|
||||
}
|
||||
|
@ -1,111 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PhpAttribute;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Attribute;
|
||||
use PhpParser\Node\Expr\ArrowFunction;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment;
|
||||
|
||||
final class AnnotationToAttributeConverter
|
||||
{
|
||||
/**
|
||||
* @var PhpAttributeGroupFactory
|
||||
*/
|
||||
private $phpAttributeGroupFactory;
|
||||
|
||||
/**
|
||||
* @var PhpDocInfoFactory
|
||||
*/
|
||||
private $phpDocInfoFactory;
|
||||
|
||||
/**
|
||||
* @var PhpDocTagRemover
|
||||
*/
|
||||
private $phpDocTagRemover;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
PhpAttributeGroupFactory $phpAttributeGroupFactory,
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
PhpDocTagRemover $phpDocTagRemover,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->phpAttributeGroupFactory = $phpAttributeGroupFactory;
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->phpDocTagRemover = $phpDocTagRemover;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_|Property|ClassMethod|Function_|Closure|ArrowFunction $node
|
||||
*/
|
||||
public function convertNode(Node $node): ?Node
|
||||
{
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
|
||||
|
||||
// 0. has 0 nodes, nothing to change
|
||||
/** @var PhpAttributableTagNodeInterface[] $phpAttributableTagNodes */
|
||||
$phpAttributableTagNodes = $phpDocInfo->findAllByType(PhpAttributableTagNodeInterface::class);
|
||||
if ($phpAttributableTagNodes === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hasNewAttrGroups = false;
|
||||
|
||||
// 1. keep only those, whom's attribute class exists
|
||||
$phpAttributableTagNodes = $this->filterOnlyExistingAttributes($phpAttributableTagNodes);
|
||||
if ($phpAttributableTagNodes !== []) {
|
||||
$hasNewAttrGroups = true;
|
||||
}
|
||||
|
||||
// 2. remove tags
|
||||
foreach ($phpAttributableTagNodes as $phpAttributableTagNode) {
|
||||
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $phpAttributableTagNode);
|
||||
}
|
||||
|
||||
// 3. convert annotations to attributes
|
||||
$newAttrGroups = $this->phpAttributeGroupFactory->create($phpAttributableTagNodes);
|
||||
$node->attrGroups = array_merge($node->attrGroups, $newAttrGroups);
|
||||
|
||||
if ($hasNewAttrGroups) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PhpAttributableTagNodeInterface[] $phpAttributableTagNodes
|
||||
* @return PhpAttributableTagNodeInterface[]
|
||||
*/
|
||||
private function filterOnlyExistingAttributes(array $phpAttributableTagNodes): array
|
||||
{
|
||||
if (StaticPHPUnitEnvironment::isPHPUnitRun()) {
|
||||
return $phpAttributableTagNodes;
|
||||
}
|
||||
|
||||
return array_filter(
|
||||
$phpAttributableTagNodes,
|
||||
function (PhpAttributableTagNodeInterface $phpAttributableTagNode): bool {
|
||||
return $this->reflectionProvider->hasClass($phpAttributableTagNode->getAttributeClassName());
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PhpAttribute\Contract;
|
||||
|
||||
interface ManyPhpAttributableTagNodeInterface
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed[]>
|
||||
*/
|
||||
public function provide(): array;
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PhpAttribute\Contract;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\Node;
|
||||
|
||||
interface PhpAttributableTagNodeInterface extends Node
|
||||
{
|
||||
public function getShortName(): string;
|
||||
|
||||
public function getAttributeClassName(): string;
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getAttributableItems(): array;
|
||||
}
|
@ -9,66 +9,25 @@ use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Attribute;
|
||||
use PhpParser\Node\AttributeGroup;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use Rector\PhpAttribute\Contract\ManyPhpAttributableTagNodeInterface;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use PHPStan\PhpDocParser\Ast\Node;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\AbstractTagValueNode;
|
||||
use Rector\Php80\ValueObject\AnnotationToAttribute;
|
||||
|
||||
final class PhpAttributeGroupFactory
|
||||
{
|
||||
/**
|
||||
* A dummy placeholder for annotation, that we know will be converted to attributes,
|
||||
* but don't have specific attribute class yet.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const TO_BE_ANNOUNCED = 'TBA';
|
||||
|
||||
/**
|
||||
* @param PhpAttributableTagNodeInterface[] $phpAttributableTagNodes
|
||||
* @return AttributeGroup[]
|
||||
*/
|
||||
public function create(array $phpAttributableTagNodes): array
|
||||
public function create(Node $node, AnnotationToAttribute $annotationToAttribute): AttributeGroup
|
||||
{
|
||||
$attributeGroups = [];
|
||||
foreach ($phpAttributableTagNodes as $phpAttributableTagNode) {
|
||||
$currentAttributeGroups = $this->printPhpAttributableTagNode($phpAttributableTagNode);
|
||||
$attributeGroups = array_merge($attributeGroups, $currentAttributeGroups);
|
||||
$fullyQualified = new FullyQualified($annotationToAttribute->getAttributeClass());
|
||||
|
||||
if ($node instanceof AbstractTagValueNode) {
|
||||
$args = $this->createArgsFromItems($node->getItemsWithoutDefaults());
|
||||
} else {
|
||||
$args = [];
|
||||
}
|
||||
|
||||
return $attributeGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Arg[]
|
||||
*/
|
||||
public function printItemsToAttributeArgs(PhpAttributableTagNodeInterface $phpAttributableTagNode): array
|
||||
{
|
||||
$items = $phpAttributableTagNode->getAttributableItems();
|
||||
return $this->createArgsFromItems($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AttributeGroup[]
|
||||
*/
|
||||
private function printPhpAttributableTagNode(PhpAttributableTagNodeInterface $phpAttributableTagNode): array
|
||||
{
|
||||
$args = $this->printItemsToAttributeArgs($phpAttributableTagNode);
|
||||
|
||||
$attributeClassName = $this->resolveAttributeClassName($phpAttributableTagNode);
|
||||
|
||||
$attributeGroups = [];
|
||||
$attributeGroups[] = $this->createAttributeGroupFromNameAndArgs($attributeClassName, $args);
|
||||
|
||||
if ($phpAttributableTagNode instanceof ManyPhpAttributableTagNodeInterface) {
|
||||
foreach ($phpAttributableTagNode->provide() as $shortName => $items) {
|
||||
$args = $this->createArgsFromItems($items);
|
||||
$name = new Name($shortName);
|
||||
$attributeGroups[] = $this->createAttributeGroupFromNameAndArgs($name, $args);
|
||||
}
|
||||
}
|
||||
|
||||
return $attributeGroups;
|
||||
$attribute = new Attribute($fullyQualified, $args);
|
||||
return new AttributeGroup([$attribute]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,24 +60,6 @@ final class PhpAttributeGroupFactory
|
||||
return $args;
|
||||
}
|
||||
|
||||
private function resolveAttributeClassName(PhpAttributableTagNodeInterface $phpAttributableTagNode): Name
|
||||
{
|
||||
if ($phpAttributableTagNode->getAttributeClassName() !== self::TO_BE_ANNOUNCED) {
|
||||
return new FullyQualified($phpAttributableTagNode->getAttributeClassName());
|
||||
}
|
||||
|
||||
return new Name($phpAttributableTagNode->getShortName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Arg[] $args
|
||||
*/
|
||||
private function createAttributeGroupFromNameAndArgs(Name $name, array $args): AttributeGroup
|
||||
{
|
||||
$attribute = new Attribute($name, $args);
|
||||
return new AttributeGroup([$attribute]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $items
|
||||
*/
|
||||
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class IdColumnGeneratedvalue
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class IdColumnGeneratedvalue
|
||||
{
|
||||
#[@ORM\Column(type: 'integer')]
|
||||
#[@ORM\GeneratedValue]
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class JoinTable
|
||||
{
|
||||
/**
|
||||
* @ORM\JoinTable(name="users_phonenumbers",
|
||||
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@ORM\JoinColumn(name="phonenumber_id", referencedColumnName="id", unique=true)}
|
||||
* )
|
||||
*/
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class JoinTable
|
||||
{
|
||||
#[@ORM\JoinTable(name: 'users_phonenumbers')]
|
||||
#[@ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id')]
|
||||
#[@ORM\InverseJoinColumn(name: 'phonenumber_id', referencedColumnName: 'id', unique: true)]
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class JustId
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
*/
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class JustId
|
||||
{
|
||||
#[@ORM\Id]
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class ManyToMany
|
||||
{
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Phonenumber")
|
||||
*/
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class ManyToMany
|
||||
{
|
||||
#[@ORM\ManyToMany(targetEntity: 'Phonenumber')]
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
@ -2,13 +2,11 @@
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class EntityColumnAndAssertEmail
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="string", unique=true)
|
||||
* @Assert\Email(message="The email '{{ value }}' is not a valid email.")
|
||||
*/
|
||||
public $name;
|
||||
@ -20,12 +18,10 @@ class EntityColumnAndAssertEmail
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class EntityColumnAndAssertEmail
|
||||
{
|
||||
#[@ORM\Column(type: 'string', unique: true)]
|
||||
#[\Symfony\Component\Validator\Constraints\Email(message: 'This value is not a valid email address.')]
|
||||
public $name;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class AssertRange
|
||||
final class AssertRange
|
||||
{
|
||||
/**
|
||||
* @Assert\Range(
|
||||
@ -21,11 +21,11 @@ class AssertRange
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture\RFC;
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
class AssertRange
|
||||
final class AssertRange
|
||||
{
|
||||
#[\Symfony\Component\Validator\Constraints\Range(min: 120, max: 180, minMessage: 'You must be at least {{ limit }}cm tall to enter', maxMessage: 'You cannot be taller than {{ limit }}cm to enter')]
|
||||
protected $height;
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass="MyProject\UserRepository", readOnly=true)
|
||||
*/
|
||||
class EntityWithRepository
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[@ORM\Entity(repositoryClass: 'MyProject\UserRepository', readOnly: true)]
|
||||
class EntityWithRepository
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SomeClass
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[@ORM\Entity]
|
||||
class SomeClass
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture;
|
||||
|
||||
final class NetteCrossOrigin
|
||||
{
|
||||
/**
|
||||
* @crossOrigin
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Fixture;
|
||||
|
||||
final class NetteCrossOrigin
|
||||
{
|
||||
#[\Nette\Application\Attributes\CrossOrigin]
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -3,13 +3,11 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Php80\Rector\Class_\AnnotationToAttributeRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$containerConfigurator->import(__DIR__ . '/configured_rule.php');
|
||||
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
|
||||
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(AnnotationToAttributeRector::class);
|
||||
};
|
||||
|
@ -2,11 +2,47 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\SymfonyRequiredTagNode;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\Validator\Constraints\AssertEmailTagValueNode;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\Validator\Constraints\AssertRangeTagValueNode;
|
||||
use Rector\Nette\PhpDoc\Node\NetteCrossOriginTagNode;
|
||||
use Rector\Nette\PhpDoc\Node\NetteInjectTagNode;
|
||||
use Rector\Nette\PhpDoc\Node\NettePersistentTagNode;
|
||||
use Rector\Php80\Rector\Class_\AnnotationToAttributeRector;
|
||||
use Rector\Php80\ValueObject\AnnotationToAttribute;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(AnnotationToAttributeRector::class)
|
||||
->call('configure', [[
|
||||
AnnotationToAttributeRector::ANNOTATION_TO_ATTRIBUTE => ValueObjectInliner::inline([
|
||||
// nette 3.0+, see https://github.com/nette/application/commit/d2471134ed909210de8a3e8559931902b1bee67b#diff-457507a8bdc046dd4f3a4aa1ca51794543fbb1e06f03825ab69ee864549a570c
|
||||
new AnnotationToAttribute(NetteInjectTagNode::class, 'Nette\DI\Attributes\Inject'),
|
||||
new AnnotationToAttribute(NettePersistentTagNode::class, 'Nette\Application\Attributes\Persistent'),
|
||||
new AnnotationToAttribute(NetteCrossOriginTagNode::class, 'Nette\Application\Attributes\CrossOrigin'),
|
||||
|
||||
$services->set(AnnotationToAttributeRector::class);
|
||||
// symfony
|
||||
new AnnotationToAttribute(
|
||||
SymfonyRequiredTagNode::class,
|
||||
'Symfony\Contracts\Service\Attribute\Required'
|
||||
),
|
||||
new AnnotationToAttribute(
|
||||
SymfonyRouteTagValueNode::class,
|
||||
'Symfony\Component\Routing\Annotation\Route'
|
||||
),
|
||||
|
||||
// symfony/validation
|
||||
new AnnotationToAttribute(
|
||||
AssertEmailTagValueNode::class,
|
||||
'Symfony\Component\Validator\Constraints\Email'
|
||||
),
|
||||
new AnnotationToAttribute(
|
||||
AssertRangeTagValueNode::class,
|
||||
'Symfony\Component\Validator\Constraints\Range'
|
||||
),
|
||||
]),
|
||||
]]);
|
||||
};
|
||||
|
@ -34,11 +34,11 @@ final class DoctrineItemDefaultValueManipulator
|
||||
string $item,
|
||||
$defaultValue
|
||||
): bool {
|
||||
$attributableItems = $doctrineTagValueNode->getAttributableItems();
|
||||
if (! isset($attributableItems[$item])) {
|
||||
$items = $doctrineTagValueNode->getItems();
|
||||
if (! isset($items[$item])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $attributableItems[$item] === $defaultValue;
|
||||
return $items[$item] === $defaultValue;
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,17 @@ use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\PhpAttribute\AnnotationToAttributeConverter;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\Php80\ValueObject\AnnotationToAttribute;
|
||||
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* @see https://wiki.php.net/rfc/attributes_v2
|
||||
@ -23,22 +30,40 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
*
|
||||
* @see \Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\AnnotationToAttributeRectorTest
|
||||
*/
|
||||
final class AnnotationToAttributeRector extends AbstractRector
|
||||
final class AnnotationToAttributeRector extends AbstractRector implements ConfigurableRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var AnnotationToAttributeConverter
|
||||
* @var string
|
||||
*/
|
||||
private $annotationToAttributeConverter;
|
||||
public const ANNOTATION_TO_ATTRIBUTE = 'annotation_to_attribute';
|
||||
|
||||
public function __construct(AnnotationToAttributeConverter $annotationToAttributeConverter)
|
||||
{
|
||||
$this->annotationToAttributeConverter = $annotationToAttributeConverter;
|
||||
/**
|
||||
* @var AnnotationToAttribute[]
|
||||
*/
|
||||
private $annotationsToAttributes = [];
|
||||
|
||||
/**
|
||||
* @var PhpAttributeGroupFactory
|
||||
*/
|
||||
private $phpAttributeGroupFactory;
|
||||
|
||||
/**
|
||||
* @var PhpDocTagRemover
|
||||
*/
|
||||
private $phpDocTagRemover;
|
||||
|
||||
public function __construct(
|
||||
PhpAttributeGroupFactory $phpAttributeGroupFactory,
|
||||
PhpDocTagRemover $phpDocTagRemover
|
||||
) {
|
||||
$this->phpAttributeGroupFactory = $phpAttributeGroupFactory;
|
||||
$this->phpDocTagRemover = $phpDocTagRemover;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Change annotation to attribute', [
|
||||
new CodeSample(
|
||||
new ConfiguredCodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
@ -64,6 +89,14 @@ class SymfonyRoute
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
, [
|
||||
self::ANNOTATION_TO_ATTRIBUTE => [
|
||||
new AnnotationToAttribute(
|
||||
SymfonyRouteTagValueNode::class,
|
||||
'Symfony\Component\Routing\Annotation\Route'
|
||||
),
|
||||
],
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
@ -88,6 +121,48 @@ CODE_SAMPLE
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
return $this->annotationToAttributeConverter->convertNode($node);
|
||||
if (! $this->isAtLeastPhpVersion(PhpVersionFeature::ATTRIBUTES)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
|
||||
if (! $phpDocInfo instanceof PhpDocInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hasNewAttrGroups = false;
|
||||
|
||||
foreach ($this->annotationsToAttributes as $annotationToAttribute) {
|
||||
$tagNodeType = $annotationToAttribute->getTagNodeClass();
|
||||
$phpDocTagNode = $phpDocInfo->getByType($tagNodeType);
|
||||
if (! $phpDocTagNode instanceof \PHPStan\PhpDocParser\Ast\Node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1. remove php-doc tag
|
||||
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $phpDocTagNode);
|
||||
|
||||
// 2. add attributes
|
||||
$node->attrGroups[] = $this->phpAttributeGroupFactory->create($phpDocTagNode, $annotationToAttribute);
|
||||
|
||||
$hasNewAttrGroups = true;
|
||||
}
|
||||
|
||||
if ($hasNewAttrGroups) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, AnnotationToAttribute[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$annotationsToAttributes = $configuration[self::ANNOTATION_TO_ATTRIBUTE] ?? [];
|
||||
Assert::allIsInstanceOf($annotationsToAttributes, AnnotationToAttribute::class);
|
||||
|
||||
$this->annotationsToAttributes = $annotationsToAttributes;
|
||||
}
|
||||
}
|
||||
|
46
rules/Php80/ValueObject/AnnotationToAttribute.php
Normal file
46
rules/Php80/ValueObject/AnnotationToAttribute.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Php80\ValueObject;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\Node;
|
||||
|
||||
final class AnnotationToAttribute
|
||||
{
|
||||
/**
|
||||
* @var class-string<Node>
|
||||
*/
|
||||
private $tagNodeClass;
|
||||
|
||||
/**
|
||||
* @var class-string
|
||||
*/
|
||||
private $attributeClass;
|
||||
|
||||
/**
|
||||
* @param class-string<Node> $tagNodeClass
|
||||
* @param class-string $attributeClass
|
||||
*/
|
||||
public function __construct(string $tagNodeClass, string $attributeClass)
|
||||
{
|
||||
$this->tagNodeClass = $tagNodeClass;
|
||||
$this->attributeClass = $attributeClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Node>
|
||||
*/
|
||||
public function getTagNodeClass(): string
|
||||
{
|
||||
return $this->tagNodeClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string
|
||||
*/
|
||||
public function getAttributeClass(): string
|
||||
{
|
||||
return $this->attributeClass;
|
||||
}
|
||||
}
|
@ -188,4 +188,10 @@ final class PhpVersionFeature
|
||||
* @var int
|
||||
*/
|
||||
public const PROPERTY_PROMOTION = PhpVersion::PHP_80;
|
||||
|
||||
/**
|
||||
* @see https://wiki.php.net/rfc/attributes_v2
|
||||
* @var int
|
||||
*/
|
||||
public const ATTRIBUTES = PhpVersion::PHP_80;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user