[Nette 3.0] Add #[Inject] attribute (#5376)

This commit is contained in:
Tomas Votruba 2021-01-30 16:06:43 +01:00 committed by GitHub
parent 2df4732c6c
commit e1310abebb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 718 additions and 390 deletions

View File

@ -19,16 +19,10 @@ To get e.g. return type, use `PhpDocInfo` value object with useful methods:
```php
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
// ...
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO);
// be always sure to check the PhpDocInfo exists and was set
if ($phpDocInfo === null) {
return null;
}
/** @var PhpDocInfoFactory $phpDocInfoFactory */
$phpDocInfo = $phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
// then use any method you like
$returnType = $phpDocInfo->getReturnType();
@ -39,16 +33,11 @@ var_dump($returnType);
## How to Remove node?
```php
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
public function __construct(
private PhpDocTagRemover $phpDocTagRemover
) {
}
/** @var PhpDocInfo $phpDocInfo */
$this->phpDocTagRemover->removeByName($phpDocInfo, 'return');
$phpDocInfo->removeByType(ReturnTagValueNode::class);
```
## How create PhpDocInfo for a new node?
@ -57,7 +46,10 @@ In case you build a new node and want to work with its doc block, you need to cr
```php
// the "PhpDocInfoFactory" service is already available in children of "AbstractRector"
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($node);
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
/** @var PhpDocInfoFactory $phpDocInfoFactory */
$phpDocInfo = $phpDocInfoFactory->createFromNodeOrEmpty($node);
```
## How to get Param with Names and Types?

View File

@ -12,6 +12,11 @@ final class DataProviderTagValueNode implements PhpDocTagValueNode, AttributeAwa
{
use AttributeTrait;
/**
* @var string
*/
public const NAME = '@dataprovider';
/**
* @var string
*/

View File

@ -4,23 +4,37 @@ declare(strict_types=1);
namespace Rector\AttributeAwarePhpDoc\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
final class RequiredTagValueNode implements PhpDocTagValueNode, AttributeAwareNodeInterface, 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, AttributeAwareNodeInterface
{
use AttributeTrait;
/**
* @var string
*/
public const NAME = '@required';
public function __construct()
{
parent::__construct(self::NAME, new AttributeAwareGenericTagValueNode(''));
}
public function __toString(): string
{
return '';
return self::NAME;
}
public function getShortName(): string
{
return 'Required';
return self::NAME;
}
public function getAttributeClassName(): string

View File

@ -8,9 +8,9 @@ use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\DocParser;
use Doctrine\Common\Annotations\Reader;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\DoctrineAnnotationGenerated\ConstantPreservingAnnotationReader;
use Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser;
use Rector\PhpAttribute\ValueObject\TagName;
final class AnnotationReaderFactory
{
@ -39,7 +39,7 @@ final class AnnotationReaderFactory
'Gedmo\Versioned',
'Versioned',
// nette @inject dummy annotation
TagName::INJECT,
NetteInjectTagNode::NAME,
];
public function create(): Reader

View File

@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\Contract;
interface GenericPhpDocNodeFactoryInterface extends PhpDocNodeFactoryInterface
{
/**
* @return string[]
*/
public function getTagValueNodeClassesToAnnotationClasses(): array;
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\Contract;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
interface PhpDocParserAwareInterface
{
public function setPhpDocParser(PhpDocParser $phpDocParser): void;
}

View File

@ -7,7 +7,7 @@ namespace Rector\BetterPhpDocParser\Contract;
interface SpecificPhpDocNodeFactoryInterface extends PhpDocNodeFactoryInterface
{
/**
* @return string[]
* @return class-string[]
*/
public function getClasses(): array;
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\Contract;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Parser\TokenIterator;
interface StringTagMatchingPhpDocNodeFactoryInterface
{
public function match(string $tag): bool;
public function createFromTokens(TokenIterator $tokenIterator): ?Node;
}

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocInfo;
use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\Node as PhpDocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
@ -274,15 +275,19 @@ final class PhpDocInfo
}
/**
* @template T as \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode
* @template T as \PHPStan\PhpDocParser\Ast\Node
* @param class-string<T> $type
* @return T|null
*/
public function getByType(string $type): ?PhpDocTagValueNode
public function getByType(string $type): ?PhpDocNode
{
$this->ensureTypeIsTagValueNode($type, __METHOD__);
foreach ($this->phpDocNode->children as $phpDocChildNode) {
if (is_a($phpDocChildNode, $type, true)) {
return $phpDocChildNode;
}
if (! $phpDocChildNode instanceof PhpDocTagNode) {
continue;
}
@ -309,6 +314,11 @@ final class PhpDocInfo
$foundTagsValueNodes = [];
foreach ($this->phpDocNode->children as $phpDocChildNode) {
if (is_a($phpDocChildNode, $type, true)) {
$foundTagsValueNodes[] = $phpDocChildNode;
continue;
}
if (! $phpDocChildNode instanceof PhpDocTagNode) {
continue;
}
@ -333,6 +343,11 @@ final class PhpDocInfo
$this->ensureTypeIsTagValueNode($type, __METHOD__);
foreach ($this->phpDocNode->children as $key => $phpDocChildNode) {
if (is_a($phpDocChildNode, $type, true)) {
unset($this->phpDocNode->children[$key]);
$this->markAsChanged();
}
if (! $phpDocChildNode instanceof PhpDocTagNode) {
continue;
}
@ -342,7 +357,6 @@ final class PhpDocInfo
}
unset($this->phpDocNode->children[$key]);
$this->markAsChanged();
}
}
@ -366,6 +380,9 @@ final class PhpDocInfo
return $paramTypesByName;
}
/**
* @todo remove to keep united API, just 1 usage
*/
public function addBareTag(string $tag): void
{
$tag = '@' . ltrim($tag, '@');
@ -376,6 +393,10 @@ final class PhpDocInfo
public function addTagValueNode(PhpDocTagValueNode $phpDocTagValueNode): void
{
if (is_a($phpDocTagValueNode, PhpDocTagNode::class)) {
throw new ShouldNotHappenException();
}
$name = $this->resolveNameForPhpDocTagValueNode($phpDocTagValueNode);
$attributeAwarePhpDocTagNode = new AttributeAwarePhpDocTagNode($name, $phpDocTagValueNode);
@ -513,7 +534,7 @@ final class PhpDocInfo
}
}
throw new NotImplementedException();
throw new NotImplementedException(get_class($phpDocTagValueNode));
}
/**

View File

@ -4,8 +4,8 @@ declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocManipulator;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
final class PhpDocTagRemover
@ -29,21 +29,26 @@ final class PhpDocTagRemover
}
}
public function removeTagValueFromNode(PhpDocInfo $phpDocInfo, PhpDocTagValueNode $phpDocTagValueNode): void
public function removeTagValueFromNode(PhpDocInfo $phpDocInfo, Node $desiredNode): void
{
$attributeAwarePhpDocNode = $phpDocInfo->getPhpDocNode();
foreach ($attributeAwarePhpDocNode->children as $key => $phpDocChildNode) {
if ($phpDocChildNode === $desiredNode) {
unset($attributeAwarePhpDocNode->children[$key]);
$phpDocInfo->markAsChanged();
continue;
}
if (! $phpDocChildNode instanceof PhpDocTagNode) {
continue;
}
if ($phpDocChildNode->value !== $phpDocTagValueNode) {
if ($phpDocChildNode->value !== $desiredNode) {
continue;
}
unset($attributeAwarePhpDocNode->children[$key]);
$phpDocInfo->markAsChanged();
}
}

View File

@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory\Doctrine\Property_;
use Doctrine\ORM\Mapping\Embedded;
use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\BetterPhpDocParser\Contract\GenericPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\PhpDocNodeFactory\AbstractPhpDocNodeFactory;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Class_\EmbeddedTagValueNode;
final class DoctrineEmbeddedPhpDocNodeFactory extends AbstractPhpDocNodeFactory implements GenericPhpDocNodeFactoryInterface
{
/**
* @return array<string, string>
*/
public function getTagValueNodeClassesToAnnotationClasses(): array
{
return [
EmbeddedTagValueNode::class => 'Doctrine\ORM\Mapping\Embedded',
];
}
public function createFromNodeAndTokens(
Node $node,
TokenIterator $tokenIterator,
string $annotationClass
): ?PhpDocTagValueNode {
$annotation = $this->nodeAnnotationReader->readAnnotation($node, $annotationClass);
if (! $annotation instanceof Embedded) {
return null;
}
$content = $this->resolveContentFromTokenIterator($tokenIterator);
$items = $this->annotationItemsResolver->resolve($annotation);
$fullyQualifiedClassName = $this->resolveFqnTargetEntity($annotation->class, $node);
return new EmbeddedTagValueNode($items, $content, $fullyQualifiedClassName);
}
}

View File

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory\Doctrine\Property_;
use Doctrine\ORM\Mapping\ManyToMany;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\OneToOne;
use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\BetterPhpDocParser\Contract\GenericPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\PhpDocNodeFactory\AbstractPhpDocNodeFactory;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\ManyToManyTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\ManyToOneTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\OneToManyTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\OneToOneTagValueNode;
final class DoctrineTargetEntityPhpDocNodeFactory extends AbstractPhpDocNodeFactory implements GenericPhpDocNodeFactoryInterface
{
/**
* @return array<string, string>
*/
public function getTagValueNodeClassesToAnnotationClasses(): array
{
return [
OneToOneTagValueNode::class => 'Doctrine\ORM\Mapping\OneToOne',
OneToManyTagValueNode::class => 'Doctrine\ORM\Mapping\OneToMany',
ManyToManyTagValueNode::class => 'Doctrine\ORM\Mapping\ManyToMany',
ManyToOneTagValueNode::class => 'Doctrine\ORM\Mapping\ManyToOne',
];
}
public function createFromNodeAndTokens(
Node $node,
TokenIterator $tokenIterator,
string $annotationClass
): ?PhpDocTagValueNode {
/** @var OneToOne|OneToMany|ManyToMany|ManyToOne|null $annotation */
$annotation = $this->nodeAnnotationReader->readAnnotation($node, $annotationClass);
if ($annotation === null) {
return null;
}
$tagValueNodeClassesToAnnotationClasses = $this->getTagValueNodeClassesToAnnotationClasses();
$tagValueNodeClass = array_search($annotationClass, $tagValueNodeClassesToAnnotationClasses, true);
$content = $this->resolveContentFromTokenIterator($tokenIterator);
$items = $this->annotationItemsResolver->resolve($annotation);
$fullyQualifiedTargetEntity = $this->resolveFqnTargetEntity($annotation->targetEntity, $node);
return new $tagValueNodeClass($items, $content, $fullyQualifiedTargetEntity);
}
}

View File

@ -4,11 +4,20 @@ declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory;
use Doctrine\ORM\Mapping\Annotation;
use Doctrine\ORM\Mapping\Embedded;
use Doctrine\ORM\Mapping\ManyToMany;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\ORM\Mapping\OneToOne;
use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\BetterPhpDocParser\Contract\GenericPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\Contract\Doctrine\DoctrineRelationTagValueNodeInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\AbstractTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Class_\EmbeddableTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Class_\EmbeddedTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Class_\EntityTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Class_\InheritanceTypeTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\ColumnTagValueNode;
@ -16,6 +25,10 @@ use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\CustomId
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\GeneratedValueTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\IdTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\JoinColumnTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\ManyToManyTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\ManyToOneTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\OneToManyTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\OneToOneTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Gedmo\BlameableTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Gedmo\LocaleTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Gedmo\LoggableTagValueNode;
@ -42,10 +55,10 @@ use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\Validator\Constrain
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\Validator\Constraints\AssertRangeTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\Validator\Constraints\AssertTypeTagValueNode;
final class MultiPhpDocNodeFactory extends AbstractPhpDocNodeFactory implements GenericPhpDocNodeFactoryInterface
final class MultiPhpDocNodeFactory extends AbstractPhpDocNodeFactory implements PhpDocNodeFactoryInterface
{
/**
* @return array<string, string>
* @return array<class-string<AbstractTagValueNode>, class-string<Annotation>>
*/
public function getTagValueNodeClassesToAnnotationClasses(): array
{
@ -91,6 +104,15 @@ final class MultiPhpDocNodeFactory extends AbstractPhpDocNodeFactory implements
JMSServiceValueNode::class => 'JMS\DiExtraBundle\Annotation\Service',
SerializerTypeTagValueNode::class => 'JMS\Serializer\Annotation\Type',
PHPDIInjectTagValueNode::class => 'DI\Annotation\Inject',
// Doctrine
OneToOneTagValueNode::class => 'Doctrine\ORM\Mapping\OneToOne',
OneToManyTagValueNode::class => 'Doctrine\ORM\Mapping\OneToMany',
ManyToManyTagValueNode::class => 'Doctrine\ORM\Mapping\ManyToMany',
ManyToOneTagValueNode::class => 'Doctrine\ORM\Mapping\ManyToOne',
// @todo cover with reflection / services to avoid forgetting registering it?
EmbeddedTagValueNode::class => 'Doctrine\ORM\Mapping\Embedded',
];
}
@ -99,17 +121,38 @@ final class MultiPhpDocNodeFactory extends AbstractPhpDocNodeFactory implements
TokenIterator $tokenIterator,
string $annotationClass
): ?PhpDocTagValueNode {
$tagValueNodeClassesToAnnotationClasses = $this->getTagValueNodeClassesToAnnotationClasses();
$tagValueNodeClass = array_search($annotationClass, $tagValueNodeClassesToAnnotationClasses, true);
$annotation = $this->nodeAnnotationReader->readAnnotation($node, $annotationClass);
if ($annotation === null) {
return null;
}
$tagValueNodeClassesToAnnotationClasses = $this->getTagValueNodeClassesToAnnotationClasses();
$tagValueNodeClass = array_search($annotationClass, $tagValueNodeClassesToAnnotationClasses, true);
if ($tagValueNodeClass === false) {
return null;
}
$items = $this->annotationItemsResolver->resolve($annotation);
$content = $this->annotationContentResolver->resolveFromTokenIterator($tokenIterator);
if (is_a($tagValueNodeClass, DoctrineRelationTagValueNodeInterface::class, true)) {
/** @var ManyToOne|OneToMany|ManyToMany|OneToOne|Embedded $annotation */
$fullyQualifiedTargetEntity = $this->resolveEntityClass($annotation, $node);
return new $tagValueNodeClass($items, $content, $fullyQualifiedTargetEntity);
}
return new $tagValueNodeClass($items, $content);
}
/**
* @param ManyToOne|OneToMany|ManyToMany|OneToOne|Embedded $annotation
*/
private function resolveEntityClass(object $annotation, Node $node): string
{
if ($annotation instanceof Embedded) {
return $this->resolveFqnTargetEntity($annotation->class, $node);
}
return $this->resolveFqnTargetEntity($annotation->targetEntity, $node);
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory\StringMatchingPhpDocNodeFactory;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\BetterPhpDocParser\Contract\StringTagMatchingPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteCrossOriginTagNode;
final class NetteCrossOriginPhpDocNodeFactory implements StringTagMatchingPhpDocNodeFactoryInterface
{
public function match(string $tag): bool
{
return $tag === NetteCrossOriginTagNode::NAME;
}
public function createFromTokens(TokenIterator $tokenIterator): ?Node
{
return new NetteCrossOriginTagNode();
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory\StringMatchingPhpDocNodeFactory;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\BetterPhpDocParser\Contract\StringTagMatchingPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
final class NetteInjectPhpDocNodeFactory implements StringTagMatchingPhpDocNodeFactoryInterface
{
public function match(string $tag): bool
{
return $tag === NetteInjectTagNode::NAME;
}
public function createFromTokens(TokenIterator $tokenIterator): ?Node
{
return new NetteInjectTagNode();
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory\StringMatchingPhpDocNodeFactory;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\BetterPhpDocParser\Contract\StringTagMatchingPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteCrossOriginTagNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NettePersistentTagNode;
final class NettePersistentPhpDocNodeFactory implements StringTagMatchingPhpDocNodeFactoryInterface
{
public function match(string $tag): bool
{
return $tag === NettePersistentTagNode::NAME;
}
public function createFromTokens(TokenIterator $tokenIterator): ?Node
{
return new NettePersistentTagNode();
}
}

View File

@ -2,17 +2,20 @@
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory;
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory\StringMatchingPhpDocNodeFactory;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Parser\ParserException;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\DataProviderTagValueNode;
use Rector\BetterPhpDocParser\Contract\PhpDocParserAwareInterface;
use Rector\BetterPhpDocParser\Contract\StringTagMatchingPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\PHPUnit\PHPUnitDataProviderTagValueNode;
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
final class PHPUnitDataProviderDocNodeFactory
final class PHPUnitDataProviderDocNodeFactory implements StringTagMatchingPhpDocNodeFactoryInterface, PhpDocParserAwareInterface
{
/**
* @var PrivatesCaller
@ -29,14 +32,14 @@ final class PHPUnitDataProviderDocNodeFactory
$this->privatesCaller = $privatesCaller;
}
public function createFromTokens(TokenIterator $tokenIterator): ?PhpDocTagValueNode
public function createFromTokens(TokenIterator $tokenIterator): ?Node
{
try {
$tokenIterator->pushSavePoint();
$attributeAwareDataProviderTagValueNode = $this->parseDataProviderTagValue($tokenIterator);
$phpUnitDataProviderTagValueNode = $this->parseDataProviderTagValue($tokenIterator);
$tokenIterator->dropSavePoint();
return $attributeAwareDataProviderTagValueNode;
return $phpUnitDataProviderTagValueNode;
} catch (ParserException $parserException) {
$tokenIterator->rollback();
$description = $this->privatesCaller->callPrivateMethod(
@ -45,7 +48,8 @@ final class PHPUnitDataProviderDocNodeFactory
[$tokenIterator]
);
return new InvalidTagValueNode($description, $parserException);
$invalidTagValueNode = new InvalidTagValueNode($description, $parserException);
return new PhpDocTagNode('', $invalidTagValueNode);
}
}
@ -57,10 +61,15 @@ final class PHPUnitDataProviderDocNodeFactory
$this->phpDocParser = $phpDocParser;
}
public function match(string $tag): bool
{
return strtolower($tag) === PHPUnitDataProviderTagValueNode::NAME;
}
/**
* Override of parent private method to allow reference: https://github.com/rectorphp/rector/pull/1735
*/
private function parseDataProviderTagValue(TokenIterator $tokenIterator): DataProviderTagValueNode
private function parseDataProviderTagValue(TokenIterator $tokenIterator): PHPUnitDataProviderTagValueNode
{
$method = $this->privatesCaller->callPrivateMethod(
$this->phpDocParser,
@ -68,6 +77,6 @@ final class PHPUnitDataProviderDocNodeFactory
[$tokenIterator]
);
return new DataProviderTagValueNode($method);
return new PHPUnitDataProviderTagValueNode($method);
}
}

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocNodeFactory\StringMatchingPhpDocNodeFactory;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\SymfonyRequiredTagNode;
use Rector\BetterPhpDocParser\Contract\StringTagMatchingPhpDocNodeFactoryInterface;
final class SymfonyRequirePhpDocNodeFactory implements StringTagMatchingPhpDocNodeFactoryInterface
{
public function match(string $tag): bool
{
return $tag === SymfonyRequiredTagNode::NAME;
}
public function createFromTokens(TokenIterator $tokenIterator): ?Node
{
return new SymfonyRequiredTagNode();
}
}

View File

@ -15,18 +15,17 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\RequiredTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Ast\AttributeAwareNodeFactory;
use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
use Rector\BetterPhpDocParser\Contract\GenericPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocParserAwareInterface;
use Rector\BetterPhpDocParser\Contract\SpecificPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\PhpDocNodeFactory\PHPUnitDataProviderDocNodeFactory;
use Rector\BetterPhpDocParser\Contract\StringTagMatchingPhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\PhpDocNodeFactory\MultiPhpDocNodeFactory;
use Rector\BetterPhpDocParser\Printer\MultilineSpaceFormatPreserver;
use Rector\BetterPhpDocParser\ValueObject\StartAndEnd;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
@ -82,12 +81,13 @@ final class BetterPhpDocParser extends PhpDocParser
private $annotationContentResolver;
/**
* @var PHPUnitDataProviderDocNodeFactory
* @var StringTagMatchingPhpDocNodeFactoryInterface[]
*/
private $phpUnitDataProviderDocNodeFactory;
private $stringTagMatchingPhpDocNodeFactories = [];
/**
* @param PhpDocNodeFactoryInterface[] $phpDocNodeFactories
* @param StringTagMatchingPhpDocNodeFactoryInterface[] $stringTagMatchingPhpDocNodeFactories
*/
public function __construct(
TypeParser $typeParser,
@ -97,8 +97,8 @@ final class BetterPhpDocParser extends PhpDocParser
CurrentNodeProvider $currentNodeProvider,
ClassAnnotationMatcher $classAnnotationMatcher,
AnnotationContentResolver $annotationContentResolver,
PHPUnitDataProviderDocNodeFactory $phpUnitDataProviderDocNodeFactory,
array $phpDocNodeFactories = []
array $phpDocNodeFactories = [],
array $stringTagMatchingPhpDocNodeFactories = []
) {
parent::__construct($typeParser, $constExprParser);
@ -109,9 +109,9 @@ final class BetterPhpDocParser extends PhpDocParser
$this->currentNodeProvider = $currentNodeProvider;
$this->classAnnotationMatcher = $classAnnotationMatcher;
$this->annotationContentResolver = $annotationContentResolver;
$this->phpUnitDataProviderDocNodeFactory = $phpUnitDataProviderDocNodeFactory;
$this->setPhpDocNodeFactories($phpDocNodeFactories);
$this->stringTagMatchingPhpDocNodeFactories = $stringTagMatchingPhpDocNodeFactories;
}
/**
@ -150,43 +150,41 @@ final class BetterPhpDocParser extends PhpDocParser
{
$tag = $this->resolveTag($tokenIterator);
$phpDocTagValueNode = $this->parseTagValue($tokenIterator, $tag);
$phpDocTagNode = $this->createPhpDocTagNodeFromStringMatch($tag, $tokenIterator);
if ($phpDocTagNode instanceof PhpDocTagNode) {
return $phpDocTagNode;
}
if ($phpDocTagNode instanceof PhpDocTagValueNode) {
return new PhpDocTagNode($tag, $phpDocTagNode);
}
$phpDocTagValueNode = $this->parseTagValue($tokenIterator, $tag);
return new PhpDocTagNode($tag, $phpDocTagValueNode);
}
public function parseTagValue(TokenIterator $tokenIterator, string $tag): PhpDocTagValueNode
{
// needed for reference support in params, see https://github.com/rectorphp/rector/issues/1734
$tagValueNode = null;
$currentPhpNode = $this->currentNodeProvider->getNode();
if (! $currentPhpNode instanceof \PhpParser\Node) {
throw new ShouldNotHappenException();
}
$lowercasedTag = strtolower($tag);
$tagValueNode = null;
if ($lowercasedTag === '@dataprovider') {
$this->phpUnitDataProviderDocNodeFactory->setPhpDocParser($this);
$tagValueNode = $this->phpUnitDataProviderDocNodeFactory->createFromTokens($tokenIterator);
} elseif ($lowercasedTag === '@' . TagName::REQUIRED) {
$tagValueNode = new RequiredTagValueNode();
} else {
// class-annotation
$phpDocNodeFactory = $this->matchTagToPhpDocNodeFactory($tag);
if ($phpDocNodeFactory !== null) {
$fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName(
$tag,
$currentPhpNode
);
// class-annotation
$phpDocNodeFactory = $this->matchTagToPhpDocNodeFactory($tag);
if ($phpDocNodeFactory !== null) {
$fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName(
$tag,
$currentPhpNode
);
$tagValueNode = $phpDocNodeFactory->createFromNodeAndTokens(
$currentPhpNode,
$tokenIterator,
$fullyQualifiedAnnotationClass
);
}
$tagValueNode = $phpDocNodeFactory->createFromNodeAndTokens(
$currentPhpNode,
$tokenIterator,
$fullyQualifiedAnnotationClass
);
}
$originalTokenIterator = clone $tokenIterator;
@ -303,7 +301,7 @@ final class BetterPhpDocParser extends PhpDocParser
return $phpDocNodeFactory->getClasses();
}
if ($phpDocNodeFactory instanceof GenericPhpDocNodeFactoryInterface) {
if ($phpDocNodeFactory instanceof MultiPhpDocNodeFactory) {
return $phpDocNodeFactory->getTagValueNodeClassesToAnnotationClasses();
}
@ -373,4 +371,21 @@ final class BetterPhpDocParser extends PhpDocParser
return $tokenEnd;
}
private function createPhpDocTagNodeFromStringMatch(string $tag, TokenIterator $tokenIterator): ?Node
{
foreach ($this->stringTagMatchingPhpDocNodeFactories as $stringTagMatchingPhpDocNodeFactory) {
if (! $stringTagMatchingPhpDocNodeFactory->match($tag)) {
continue;
}
if ($stringTagMatchingPhpDocNodeFactory instanceof PhpDocParserAwareInterface) {
$stringTagMatchingPhpDocNodeFactory->setPhpDocParser($this);
}
return $stringTagMatchingPhpDocNodeFactory->createFromTokens($tokenIterator);
}
return null;
}
}

View File

@ -16,7 +16,7 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
final class ClassAnnotationMatcher
{
/**
* @var string[]
* @var array<string, string>
*/
private $fullyQualifiedNameByHash = [];

View File

@ -14,7 +14,6 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocTagNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
@ -326,7 +325,6 @@ final class PhpDocInfoPrinter
$output .= $tagSpaceSeparator;
}
/** @var AttributeAwarePhpDocTagNode $phpDocTagNode */
if ($this->hasDescription($phpDocTagNode)) {
$quotedDescription = preg_quote($phpDocTagNode->value->description, '#');
$pattern = Strings::replace($quotedDescription, '#[\s]+#', '\s+');
@ -421,20 +419,24 @@ final class PhpDocInfoPrinter
return '';
}
private function hasDescription(AttributeAwarePhpDocTagNode $attributeAwarePhpDocTagNode): bool
/**
* @param AttributeAwareNodeInterface&PhpDocTagNode $phpDocTagNode
*/
private function hasDescription(PhpDocTagNode $phpDocTagNode): bool
{
$hasDescriptionWithOriginalSpaces = $attributeAwarePhpDocTagNode->getAttribute(
$hasDescriptionWithOriginalSpaces = $phpDocTagNode->getAttribute(
Attribute::HAS_DESCRIPTION_WITH_ORIGINAL_SPACES
);
if (! $hasDescriptionWithOriginalSpaces) {
return false;
}
if (! property_exists($attributeAwarePhpDocTagNode->value, 'description')) {
if (! property_exists($phpDocTagNode->value, 'description')) {
return false;
}
return (bool) $attributeAwarePhpDocTagNode->value->description;
return (bool) $phpDocTagNode->value->description;
}
private function explodeAndImplode(string $content, string $explodeChar, string $implodeChar): string

View File

@ -3,9 +3,10 @@ declare(strict_types=1);
namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Class_;
use Rector\BetterPhpDocParser\Contract\Doctrine\DoctrineRelationTagValueNodeInterface;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTagValueNode;
final class EmbeddedTagValueNode extends AbstractDoctrineTagValueNode
final class EmbeddedTagValueNode extends AbstractDoctrineTagValueNode implements DoctrineRelationTagValueNodeInterface
{
/**
* @var string
@ -24,18 +25,23 @@ final class EmbeddedTagValueNode extends AbstractDoctrineTagValueNode
return '@ORM\Embedded';
}
public function getClass(): string
{
return $this->items['class'];
}
public function getFullyQualifiedClassName(): string
{
return $this->fullyQualifiedClassName;
}
public function getColumnPrefix(): ?string
{
return $this->items['columnPrefix'];
}
public function getTargetEntity(): ?string
{
return $this->items['class'];
}
public function getFullyQualifiedTargetEntity(): string
{
return $this->fullyQualifiedClassName;
}
public function changeTargetEntity(string $targetEntity): void
{
$this->items['class'] = $targetEntity;
}
}

View File

@ -6,6 +6,7 @@ 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
{
@ -39,6 +40,6 @@ final class EntityTagValueNode extends AbstractDoctrineTagValueNode implements P
public function getAttributeClassName(): string
{
return 'TBA';
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
}
}

View File

@ -6,6 +6,7 @@ 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
{
@ -39,6 +40,6 @@ final class ColumnTagValueNode extends AbstractDoctrineTagValueNode implements P
public function getAttributeClassName(): string
{
return 'TBA';
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
}
}

View File

@ -7,6 +7,7 @@ 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\BetterPhpDocParser\Tests\PhpDocParser\TagValueNodeReprint\TagValueNodeReprintTest
@ -25,6 +26,6 @@ final class GeneratedValueTagValueNode extends AbstractDoctrineTagValueNode impl
public function getAttributeClassName(): string
{
return 'TBA';
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
}
}

View File

@ -6,6 +6,7 @@ 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
{
@ -16,6 +17,6 @@ final class IdTagValueNode extends AbstractDoctrineTagValueNode implements PhpAt
public function getAttributeClassName(): string
{
return 'TBA';
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
}
}

View File

@ -7,6 +7,7 @@ namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\TagAwareNodeInterface;
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
{
@ -54,6 +55,6 @@ final class JoinColumnTagValueNode extends AbstractDoctrineTagValueNode implemen
public function getAttributeClassName(): string
{
return 'TBA';
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
}
}

View File

@ -9,6 +9,7 @@ use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\AbstractDoctrineTa
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
{
@ -125,7 +126,7 @@ final class JoinTableTagValueNode extends AbstractDoctrineTagValueNode implement
public function getAttributeClassName(): string
{
return 'TBA';
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
}
/**

View File

@ -9,6 +9,7 @@ use Rector\BetterPhpDocParser\Contract\Doctrine\MappedByNodeInterface;
use Rector\BetterPhpDocParser\Contract\Doctrine\ToManyTagNodeInterface;
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
{
@ -74,6 +75,6 @@ final class ManyToManyTagValueNode extends AbstractDoctrineTagValueNode implemen
public function getAttributeClassName(): string
{
return 'TBA';
return PhpAttributeGroupFactory::TO_BE_ANNOUNCED;
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareGenericTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
final class NetteCrossOriginTagNode extends PhpDocTagNode implements PhpAttributableTagNodeInterface, AttributeAwareNodeInterface
{
use AttributeTrait;
/**
* @var string
*/
public const NAME = '@crossOrigin';
public function __construct()
{
parent::__construct(self::NAME, new AttributeAwareGenericTagValueNode(''));
}
public function getShortName(): string
{
return self::NAME;
}
public function getAttributeClassName(): string
{
return 'Nette\Application\Attributes\CrossOrigin';
}
/**
* @return mixed[]
*/
public function getAttributableItems(): array
{
return [];
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareGenericTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
/**
* @see \Rector\BetterPhpDocParser\PhpDocNodeFactory\StringMatchingPhpDocNodeFactory\NetteInjectPhpDocNodeFactory
*/
final class NetteInjectTagNode extends PhpDocTagNode implements PhpAttributableTagNodeInterface, AttributeAwareNodeInterface
{
use AttributeTrait;
/**
* @var string
*/
public const NAME = '@inject';
public function __construct()
{
parent::__construct(self::NAME, new AttributeAwareGenericTagValueNode(''));
}
public function getShortName(): string
{
return self::NAME;
}
public function getAttributeClassName(): string
{
return 'Nette\DI\Attributes\Inject';
}
/**
* @return mixed[]
*/
public function getAttributableItems(): array
{
return [];
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareGenericTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
/**
* @see \Rector\BetterPhpDocParser\PhpDocNodeFactory\StringMatchingPhpDocNodeFactory\NettePersistentPhpDocNodeFactory
*/
final class NettePersistentTagNode extends PhpDocTagNode implements PhpAttributableTagNodeInterface, AttributeAwareNodeInterface
{
use AttributeTrait;
/**
* @var string
*/
public const NAME = '@persistent';
public function __construct()
{
parent::__construct(self::NAME, new AttributeAwareGenericTagValueNode(''));
}
public function getShortName(): string
{
return self::NAME;
}
public function getAttributeClassName(): string
{
return 'Nette\Application\Attributes\Persistent';
}
/**
* @return mixed[]
*/
public function getAttributableItems(): array
{
return [];
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\ValueObject\PhpDocNode\PHPUnit;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
final class PHPUnitDataProviderTagValueNode implements PhpDocTagValueNode, AttributeAwareNodeInterface
{
use AttributeTrait;
/**
* @var string
*/
public const NAME = '@dataprovider';
/**
* @var string
*/
private $method;
public function __construct(string $method)
{
$this->method = $method;
}
public function __toString(): string
{
return $this->method;
}
public function getMethod(): string
{
return $this->method;
}
public function getMethodName(): string
{
return trim($this->method, '()');
}
public function changeMethodName(string $method): void
{
$this->method = $method . '()';
}
}

View File

@ -35,6 +35,6 @@ final class AssertEmailTagValueNode extends AbstractTagValueNode implements Type
public function getAttributeClassName(): string
{
return 'TBA';
return 'Symfony\Component\Validator\Constraints\Email';
}
}

View File

@ -26,6 +26,6 @@ final class AssertRangeTagValueNode extends AbstractTagValueNode implements Type
public function getAttributeClassName(): string
{
return 'TBA';
return 'Symfony\Component\Validator\Constraints\Range';
}
}

View File

@ -15,15 +15,15 @@ use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
use Rector\PhpAttribute\Printer\PhpAttributteGroupFactory;
use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory;
use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment;
final class AnnotationToAttributeConverter
{
/**
* @var PhpAttributteGroupFactory
* @var PhpAttributeGroupFactory
*/
private $phpAttributteGroupFactory;
private $phpAttributeGroupFactory;
/**
* @var PhpDocInfoFactory
@ -36,11 +36,11 @@ final class AnnotationToAttributeConverter
private $phpDocTagRemover;
public function __construct(
PhpAttributteGroupFactory $phpAttributteGroupFactory,
PhpAttributeGroupFactory $phpAttributeGroupFactory,
PhpDocInfoFactory $phpDocInfoFactory,
PhpDocTagRemover $phpDocTagRemover
) {
$this->phpAttributteGroupFactory = $phpAttributteGroupFactory;
$this->phpAttributeGroupFactory = $phpAttributeGroupFactory;
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->phpDocTagRemover = $phpDocTagRemover;
}
@ -52,8 +52,6 @@ final class AnnotationToAttributeConverter
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$hasNewAttrGroups = false;
// 0. has 0 nodes, nothing to change
/** @var PhpAttributableTagNodeInterface[] $phpAttributableTagNodes */
$phpAttributableTagNodes = $phpDocInfo->findAllByType(PhpAttributableTagNodeInterface::class);
@ -61,6 +59,8 @@ final class AnnotationToAttributeConverter
return null;
}
$hasNewAttrGroups = false;
// 1. keep only those, whom's attribute class exists
$phpAttributableTagNodes = $this->filterOnlyExistingAttributes($phpAttributableTagNodes);
if ($phpAttributableTagNodes !== []) {
@ -73,7 +73,7 @@ final class AnnotationToAttributeConverter
}
// 3. convert annotations to attributes
$newAttrGroups = $this->phpAttributteGroupFactory->create($phpAttributableTagNodes);
$newAttrGroups = $this->phpAttributeGroupFactory->create($phpAttributableTagNodes);
$node->attrGroups = array_merge($node->attrGroups, $newAttrGroups);
if ($hasNewAttrGroups) {

View File

@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Rector\PhpAttribute\Contract;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\Node;
interface PhpAttributableTagNodeInterface extends PhpDocTagValueNode
interface PhpAttributableTagNodeInterface extends Node
{
public function getShortName(): string;

View File

@ -14,12 +14,15 @@ use PhpParser\Node\Name\FullyQualified;
use Rector\PhpAttribute\Contract\ManyPhpAttributableTagNodeInterface;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
final class PhpAttributteGroupFactory
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 TBA = 'TBA';
public const TO_BE_ANNOUNCED = 'TBA';
/**
* @param PhpAttributableTagNodeInterface[] $phpAttributableTagNodes
@ -100,7 +103,7 @@ final class PhpAttributteGroupFactory
private function resolveAttributeClassName(PhpAttributableTagNodeInterface $phpAttributableTagNode): Name
{
if ($phpAttributableTagNode->getAttributeClassName() !== self::TBA) {
if ($phpAttributableTagNode->getAttributeClassName() !== self::TO_BE_ANNOUNCED) {
return new FullyQualified($phpAttributableTagNode->getAttributeClassName());
}

View File

@ -6,20 +6,8 @@ namespace Rector\PhpAttribute\ValueObject;
final class TagName
{
/**
* 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
* @var string
*/
public const REQUIRED = 'required';
/**
* @var string
*/
public const API = 'api';
/**
* @var string
*/
public const INJECT = 'inject';
}

View File

@ -6,9 +6,9 @@ namespace Rector\PostRector\NodeAnalyzer;
use PhpParser\Node\Stmt\Class_;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\Core\ValueObject\MethodName;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PhpAttribute\ValueObject\TagName;
use ReflectionClass;
use ReflectionMethod;
@ -47,7 +47,7 @@ final class NetteInjectDetector
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
if ($phpDocInfo->hasByName(TagName::INJECT)) {
if ($phpDocInfo->hasByType(NetteInjectTagNode::class)) {
return true;
}
}

View File

@ -523,3 +523,14 @@ parameters:
-
message: '#There should be no empty class#'
path: src/Rector/AbstractRector.php
- '#Method Rector\\BetterPhpDocParser\\PhpDocNodeFactory\\MultiPhpDocNodeFactory\:\:getTagValueNodeClassesToAnnotationClasses\(\) should return array<class\-string<Rector\\BetterPhpDocParser\\ValueObject\\PhpDocNode\\AbstractTagValueNode\>, class\-string<Doctrine\\ORM\\Mapping\\Annotation\>\> but returns array<string, string\>#'
-
message: '#Use value object over return of values#'
path: packages/better-php-doc-parser/src/PhpDocNodeFactory/MultiPhpDocNodeFactory.php
-
message: '#Do not use setter on a service#'
path: packages/better-php-doc-parser/src/PhpDocNodeFactory/NetteInjectDocNodeFactory.php
- '#Content of method "getFunctionLikePhpDocInfo\(\)" is duplicated with method "getFunctionLikePhpDocInfo\(\)" in "Rector\\NodeTypeResolver\\NodeTypeResolver\\ParamTypeResolver" class\. Use unique content or abstract service instead#'

View File

@ -173,8 +173,8 @@ CODE_SAMPLE
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($callee);
foreach ($this->throwablesToAnnotate as $throwableToAnnotate) {
$docComment = $this->throwsFactory->crateDocTagNodeFromClass($throwableToAnnotate);
$phpDocInfo->addPhpDocTagNode($docComment);
$phpDocTagNode = $this->throwsFactory->crateDocTagNodeFromClass($throwableToAnnotate);
$phpDocInfo->addPhpDocTagNode($phpDocTagNode);
}
}

View File

@ -11,10 +11,10 @@ use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\SymfonyRequiredTagNode;
use Rector\Core\Rector\AbstractRector;
use Rector\DeadCode\Comparator\CurrentAndParentClassMethodComparator;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -148,6 +148,6 @@ CODE_SAMPLE
private function hasRequiredAnnotation(ClassMethod $classMethod): bool
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
return $phpDocInfo->hasByName(TagName::REQUIRED);
return $phpDocInfo->hasByType(SymfonyRequiredTagNode::class);
}
}

View File

@ -88,14 +88,9 @@ CODE_SAMPLE
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
foreach ($this->annotationsToRemove as $annotationToRemove) {
if ($phpDocInfo->hasByName($annotationToRemove)) {
$this->phpDocTagRemover->removeByName($phpDocInfo, $annotationToRemove);
continue;
}
$this->phpDocTagRemover->removeByName($phpDocInfo, $annotationToRemove);
if (is_a($annotationToRemove, PhpDocTagValueNode::class, true) && $phpDocInfo->hasByType(
$annotationToRemove
)) {
if (is_a($annotationToRemove, PhpDocTagValueNode::class, true)) {
$phpDocInfo->removeByType($annotationToRemove);
}
}

View File

@ -6,10 +6,8 @@ namespace Rector\DeadDocBlock\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareReturnTagValueNode;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\Core\Rector\AbstractRector;
use Rector\DeadDocBlock\DeadReturnTagValueNodeAnalyzer;
use Rector\DeadDocBlock\TagRemover\ReturnTagRemover;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -19,21 +17,13 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class RemoveUselessReturnTagRector extends AbstractRector
{
/**
* @var DeadReturnTagValueNodeAnalyzer
* @var ReturnTagRemover
*/
private $deadReturnTagValueNodeAnalyzer;
private $returnTagRemover;
/**
* @var PhpDocTagRemover
*/
private $phpDocTagRemover;
public function __construct(
DeadReturnTagValueNodeAnalyzer $deadReturnTagValueNodeAnalyzer,
PhpDocTagRemover $phpDocTagRemover
) {
$this->deadReturnTagValueNodeAnalyzer = $deadReturnTagValueNodeAnalyzer;
$this->phpDocTagRemover = $phpDocTagRemover;
public function __construct(ReturnTagRemover $returnTagRemover)
{
$this->returnTagRemover = $returnTagRemover;
}
public function getRuleDefinition(): RuleDefinition
@ -85,18 +75,12 @@ CODE_SAMPLE
public function refactor(Node $node): ?Node
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$this->returnTagRemover->removeReturnTagIfUseless($phpDocInfo, $node);
$attributeAwareReturnTagValueNode = $phpDocInfo->getReturnTagValue();
if (! $attributeAwareReturnTagValueNode instanceof AttributeAwareReturnTagValueNode) {
return null;
if ($phpDocInfo->hasChanged()) {
return $node;
}
if (! $this->deadReturnTagValueNodeAnalyzer->isDead($attributeAwareReturnTagValueNode, $node)) {
return null;
}
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $attributeAwareReturnTagValueNode);
return $node;
return null;
}
}

View File

@ -5,10 +5,10 @@ declare(strict_types=1);
namespace Rector\DeadDocBlock\TagRemover;
use PhpParser\Node\FunctionLike;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\Type\Type;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareReturnTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\DeadDocBlock\DeadReturnTagValueNodeAnalyzer;
final class ReturnTagRemover
@ -18,17 +18,9 @@ final class ReturnTagRemover
*/
private $deadReturnTagValueNodeAnalyzer;
/**
* @var PhpDocTagRemover
*/
private $phpDocTagRemover;
public function __construct(
DeadReturnTagValueNodeAnalyzer $deadReturnTagValueNodeAnalyzer,
PhpDocTagRemover $phpDocTagRemover
) {
public function __construct(DeadReturnTagValueNodeAnalyzer $deadReturnTagValueNodeAnalyzer)
{
$this->deadReturnTagValueNodeAnalyzer = $deadReturnTagValueNodeAnalyzer;
$this->phpDocTagRemover = $phpDocTagRemover;
}
public function removeReturnTagIfUseless(PhpDocInfo $phpDocInfo, FunctionLike $functionLike): void
@ -47,6 +39,6 @@ final class ReturnTagRemover
return;
}
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $attributeAwareReturnTagValueNode);
$phpDocInfo->removeByType(ReturnTagValueNode::class);
}
}

View File

@ -7,13 +7,13 @@ namespace Rector\DependencyInjection\NodeFactory;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\SymfonyRequiredTagNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\CodingStyle\Naming\ClassNaming;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\ValueObject\FrameworkName;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder;
use Symplify\Astral\ValueObject\NodeBuilder\ParamBuilder;
@ -87,7 +87,7 @@ final class InjectMethodFactory
if ($framework === FrameworkName::SYMFONY) {
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
$phpDocInfo->addBareTag(TagName::REQUIRED);
$phpDocInfo->addPhpDocTagNode(new SymfonyRequiredTagNode());
}
return $classMethod;

View File

@ -13,7 +13,6 @@ use Rector\BetterPhpDocParser\Contract\Doctrine\DoctrineRelationTagValueNodeInte
use Rector\BetterPhpDocParser\Contract\Doctrine\ToManyTagNodeInterface;
use Rector\BetterPhpDocParser\Contract\Doctrine\ToOneTagNodeInterface;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\JoinColumnTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Doctrine\Property_\OneToOneTagValueNode;
use Rector\Core\Rector\AbstractRector;
@ -41,11 +40,6 @@ final class AddUuidMirrorForRelationPropertyRector extends AbstractRector
*/
private $uuidMigrationDataCollector;
/**
* @var PhpDocTagRemover
*/
private $phpDocTagRemover;
/**
* @var DoctrineDocBlockResolver
*/
@ -54,12 +48,10 @@ final class AddUuidMirrorForRelationPropertyRector extends AbstractRector
public function __construct(
PhpDocTagNodeFactory $phpDocTagNodeFactory,
UuidMigrationDataCollector $uuidMigrationDataCollector,
PhpDocTagRemover $phpDocTagRemover,
DoctrineDocBlockResolver $doctrineDocBlockResolver
) {
$this->phpDocTagNodeFactory = $phpDocTagNodeFactory;
$this->uuidMigrationDataCollector = $uuidMigrationDataCollector;
$this->phpDocTagRemover = $phpDocTagRemover;
$this->doctrineDocBlockResolver = $doctrineDocBlockResolver;
}
@ -279,11 +271,8 @@ CODE_SAMPLE
private function refactorToManyPropertyPhpDocInfo(PhpDocInfo $phpDocInfo, Property $property): void
{
$doctrineJoinColumnTagValueNode = $phpDocInfo->getByType(JoinColumnTagValueNode::class);
if ($doctrineJoinColumnTagValueNode !== null) {
// replace @ORM\JoinColumn with @ORM\JoinTable
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineJoinColumnTagValueNode);
}
// replace @ORM\JoinColumn with @ORM\JoinTable
$phpDocInfo->removeByType(JoinColumnTagValueNode::class);
$joinTableTagNode = $this->phpDocTagNodeFactory->createJoinTableTagNode($property);
$phpDocInfo->addPhpDocTagNode($joinTableTagNode);

View File

@ -8,13 +8,12 @@ use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\FamilyTree\NodeAnalyzer\ClassChildAnalyzer;
use Rector\FamilyTree\NodeAnalyzer\PropertyUsageAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -39,25 +38,18 @@ final class AnnotatedPropertyInjectToConstructorInjectionRector extends Abstract
*/
private $classChildAnalyzer;
/**
* @var PhpDocTagRemover
*/
private $phpDocTagRemover;
public function __construct(
ClassChildAnalyzer $classChildAnalyzer,
PropertyUsageAnalyzer $propertyUsageAnalyzer,
PhpDocTagRemover $phpDocTagRemover
PropertyUsageAnalyzer $propertyUsageAnalyzer
) {
$this->propertyUsageAnalyzer = $propertyUsageAnalyzer;
$this->classChildAnalyzer = $classChildAnalyzer;
$this->phpDocTagRemover = $phpDocTagRemover;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Turns non-private properties with `@annotation` to private properties and constructor injection',
'Turns non-private properties with `@inject` to private properties and constructor injection',
[
new CodeSample(
<<<'CODE_SAMPLE'
@ -102,7 +94,7 @@ CODE_SAMPLE
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$this->phpDocTagRemover->removeByName($phpDocInfo, TagName::INJECT);
$phpDocInfo->removeByType(NetteInjectTagNode::class);
if ($this->propertyUsageAnalyzer->isPropertyFetchedInChildClass($node)) {
$this->makeProtected($node);
@ -123,7 +115,7 @@ CODE_SAMPLE
private function shouldSkipProperty(Property $property): bool
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
if (! $phpDocInfo->hasByName(TagName::INJECT)) {
if (! $phpDocInfo->hasByType(NetteInjectTagNode::class)) {
return true;
}

View File

@ -8,12 +8,11 @@ use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\MethodName;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\FamilyTree\NodeAnalyzer\PropertyUsageAnalyzer;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -29,15 +28,9 @@ final class MoveInjectToExistingConstructorRector extends AbstractRector
*/
private $propertyUsageAnalyzer;
/**
* @var PhpDocTagRemover
*/
private $phpDocTagRemover;
public function __construct(PropertyUsageAnalyzer $propertyUsageAnalyzer, PhpDocTagRemover $phpDocTagRemover)
public function __construct(PropertyUsageAnalyzer $propertyUsageAnalyzer)
{
$this->propertyUsageAnalyzer = $propertyUsageAnalyzer;
$this->phpDocTagRemover = $phpDocTagRemover;
}
public function getRuleDefinition(): RuleDefinition
@ -141,7 +134,7 @@ CODE_SAMPLE
private function removeInjectAnnotation(Property $property): void
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$this->phpDocTagRemover->removeByName($phpDocInfo, TagName::INJECT);
$phpDocInfo->removeByType(NetteInjectTagNode::class);
}
private function changePropertyVisibility(Property $injectProperty): void
@ -160,6 +153,6 @@ CODE_SAMPLE
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
return $phpDocInfo->hasByName(TagName::INJECT);
return $phpDocInfo->hasByType(NetteInjectTagNode::class);
}
}

View File

@ -27,7 +27,7 @@ use Symfony\Component\Validator\Constraints as Assert;
class AssertRange
{
#[@Assert\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')]
#[\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;
}

View File

@ -26,7 +26,7 @@ use Symfony\Component\Validator\Constraints as Assert;
class EntityColumnAndAssertEmail
{
#[@ORM\Column(type: 'string', unique: true)]
#[@Assert\Email(message: 'This value is not a valid email address.')]
#[\Symfony\Component\Validator\Constraints\Email(message: 'This value is not a valid email address.')]
public $name;
}

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\Php80\Tests\Rector\Class_\AnnotationToAttributeRector\Fixture;
final class NetteCrossOrigin
{
/**
* @crossOrigin
*/
public function run()
{
}
}
?>
-----
<?php
namespace Rector\Php80\Tests\Rector\Class_\AnnotationToAttributeRector\Fixture;
final class NetteCrossOrigin
{
#[\Nette\Application\Attributes\CrossOrigin]
public function run()
{
}
}
?>

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\Php80\Tests\Rector\Class_\AnnotationToAttributeRector\Fixture;
final class NetteInject
{
/**
* @inject
* @var SomeType
*/
public $someDependency;
}
?>
-----
<?php
namespace Rector\Php80\Tests\Rector\Class_\AnnotationToAttributeRector\Fixture;
final class NetteInject
{
/**
* @var SomeType
*/
#[\Nette\DI\Attributes\Inject]
public $someDependency;
}
?>

View File

@ -0,0 +1,29 @@
<?php
namespace Rector\Php80\Tests\Rector\Class_\AnnotationToAttributeRector\Fixture;
final class NettePersistent
{
/**
* @persistent
*/
public function run()
{
}
}
?>
-----
<?php
namespace Rector\Php80\Tests\Rector\Class_\AnnotationToAttributeRector\Fixture;
final class NettePersistent
{
#[\Nette\Application\Attributes\Persistent]
public function run()
{
}
}
?>

View File

@ -106,6 +106,7 @@ CODE_SAMPLE
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
foreach (self::ANNOTATION_TO_METHOD as $annotationName => $methodName) {
if (! $phpDocInfo->hasByName($annotationName)) {
continue;

View File

@ -8,7 +8,7 @@ use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Class_;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\DataProviderTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\PHPUnit\PHPUnitDataProviderTagValueNode;
use Rector\Core\Rector\AbstractRector;
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -112,27 +112,23 @@ CODE_SAMPLE
foreach ($class->getMethods() as $classMethod) {
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
/** @var DataProviderTagValueNode[] $dataProviderTagValueNodes */
$dataProviderTagValueNodes = $phpDocInfo->findAllByType(DataProviderTagValueNode::class);
if ($dataProviderTagValueNodes === []) {
/** @var PHPUnitDataProviderTagValueNode[] $phpunitDataProviderTagValueNodes */
$phpunitDataProviderTagValueNodes = $phpDocInfo->findAllByType(PHPUnitDataProviderTagValueNode::class);
if ($phpunitDataProviderTagValueNodes === []) {
continue;
}
foreach ($dataProviderTagValueNodes as $dataProviderTagValueNode) {
$oldMethodName = $dataProviderTagValueNode->getMethod();
foreach ($phpunitDataProviderTagValueNodes as $dataProviderTagValueNode) {
$oldMethodName = $dataProviderTagValueNode->getMethodName();
if (! Strings::startsWith($oldMethodName, 'test')) {
continue;
}
$newMethodName = $this->createNewMethodName($oldMethodName);
$dataProviderTagValueNode->changeMethod($newMethodName);
$oldMethodName = trim($oldMethodName, '()');
$newMethodName = trim($newMethodName, '()');
$dataProviderTagValueNode->changeMethodName($newMethodName);
$phpDocInfo->markAsChanged();
$this->providerMethodNamesToNewNames[$oldMethodName] = $newMethodName;
$phpDocInfo->markAsChanged();
}
}
}

View File

@ -8,6 +8,8 @@ use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\SymfonyRequiredTagNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\Caching\Contract\Rector\ZeroCacheRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Doctrine\PhpDocParser\DoctrineDocBlockResolver;
@ -160,7 +162,11 @@ CODE_SAMPLE
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
return $phpDocInfo->hasByNames([TagName::API, TagName::REQUIRED]);
if ($phpDocInfo->hasByType(SymfonyRequiredTagNode::class)) {
return true;
}
return $phpDocInfo->hasByName(TagName::API);
}
private function shouldSkipClassLike(Class_ $class): bool
@ -198,7 +204,7 @@ CODE_SAMPLE
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
return $phpDocInfo->hasByName(TagName::INJECT);
return $phpDocInfo->hasByType(NetteInjectTagNode::class);
}
private function shouldSkipClassMethod(ClassMethod $classMethod): bool

View File

@ -8,6 +8,8 @@ use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Property;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\SymfonyRequiredTagNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
@ -26,9 +28,9 @@ final class PrivatizeLocalPropertyToPrivatePropertyRector extends AbstractRector
private const ANNOTATIONS_REQUIRING_PUBLIC = [
TagName::API,
// Symfony DI
TagName::REQUIRED,
SymfonyRequiredTagNode::NAME,
// other DI
TagName::INJECT,
NetteInjectTagNode::NAME,
];
/**

View File

@ -9,7 +9,6 @@ use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Use_;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Sensio\SensioRouteTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
use Rector\Core\Rector\AbstractRector;
@ -24,16 +23,6 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*/
final class ReplaceSensioRouteAnnotationWithSymfonyRector extends AbstractRector
{
/**
* @var PhpDocTagRemover
*/
private $phpDocTagRemover;
public function __construct(PhpDocTagRemover $phpDocTagRemover)
{
$this->phpDocTagRemover = $phpDocTagRemover;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
@ -99,10 +88,7 @@ CODE_SAMPLE
return null;
}
/** @var SensioRouteTagValueNode $sensioRouteTagValueNode */
$sensioRouteTagValueNode = $phpDocInfo->getByType(SensioRouteTagValueNode::class);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $sensioRouteTagValueNode);
$phpDocInfo->removeByType(SensioRouteTagValueNode::class);
// unset service, that is deprecated
$items = $sensioRouteTagValueNode->getItems();

View File

@ -7,9 +7,9 @@ namespace Rector\Symfony\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\SymfonyRequiredTagNode;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -61,7 +61,8 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->hasTagByName($node, TagName::REQUIRED)) {
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
if (! $phpDocInfo->hasByType(SymfonyRequiredTagNode::class)) {
return null;
}

View File

@ -7,7 +7,6 @@ namespace Rector\Symfony3\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Sensio\SensioMethodTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
use Rector\Core\Rector\AbstractRector;
@ -23,16 +22,6 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*/
final class MergeMethodAnnotationToRouteAnnotationRector extends AbstractRector
{
/**
* @var PhpDocTagRemover
*/
private $phpDocTagRemover;
public function __construct(PhpDocTagRemover $phpDocTagRemover)
{
$this->phpDocTagRemover = $phpDocTagRemover;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
@ -110,8 +99,7 @@ CODE_SAMPLE
}
$symfonyRouteTagValueNode->changeMethods($methods);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $sensioMethodTagValueNode);
$phpDocInfo->removeByType(SensioMethodTagValueNode::class);
return $node;
}

View File

@ -13,9 +13,9 @@ use PhpParser\Node\Stmt\Return_;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\DataProviderTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\PHPUnit\PHPUnitDataProviderTagValueNode;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -86,8 +86,8 @@ final class PHPUnitDataProviderParamTypeInferer implements ParamTypeInfererInter
{
$phpDocInfo = $this->getFunctionLikePhpDocInfo($param);
$attributeAwareDataProviderTagValueNode = $phpDocInfo->getByType(DataProviderTagValueNode::class);
if (! $attributeAwareDataProviderTagValueNode instanceof DataProviderTagValueNode) {
$phpUnitDataProviderTagValueNode = $phpDocInfo->getByType(PHPUnitDataProviderTagValueNode::class);
if (! $phpUnitDataProviderTagValueNode instanceof PHPUnitDataProviderTagValueNode) {
return null;
}
@ -96,7 +96,7 @@ final class PHPUnitDataProviderParamTypeInferer implements ParamTypeInfererInter
return null;
}
return $classLike->getMethod($attributeAwareDataProviderTagValueNode->getMethodName());
return $classLike->getMethod($phpUnitDataProviderTagValueNode->getMethodName());
}
/**

View File

@ -45,6 +45,7 @@ use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Exception\NotImplementedException;
use Rector\Core\Php\PhpVersionProvider;
@ -52,7 +53,6 @@ use Rector\Core\ValueObject\MethodName;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
use Rector\PostRector\ValueObject\PropertyMetadata;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder;
@ -265,7 +265,7 @@ final class NodeFactory
// add @inject
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$phpDocInfo->addBareTag(TagName::INJECT);
$phpDocInfo->addPhpDocTagNode(new NetteInjectTagNode());
return $property;
}