[DoctrineAnnotationSyncer] Remove to move to static reflection (#6006)

This commit is contained in:
Tomas Votruba 2021-03-29 15:01:35 +02:00 committed by GitHub
parent ca8cfa103b
commit 5fa858b072
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 16 additions and 804 deletions

View File

@ -29,10 +29,6 @@ jobs:
name: 'PHPStan for config'
run: composer phpstan-config
-
name: "Check doctrine/annotation"
run: "bin/rector sync-annotation-parser --dry-run --ansi"
# see https://github.com/rectorphp/rector-generator
-
name: 'Rector Generate From Recipe'

View File

@ -99,8 +99,7 @@
"Rector\\Core\\Tests\\": "tests",
"Rector\\RuleDocGenerator\\": "utils/rule-doc-generator/src",
"Rector\\PHPStanExtensions\\": "utils/phpstan-extensions/src",
"Rector\\PHPStanExtensions\\Tests\\": "utils/phpstan-extensions/tests",
"Rector\\Utils\\DoctrineAnnotationParserSyncer\\": "utils/doctrine-annotation-parser-syncer/src"
"Rector\\PHPStanExtensions\\Tests\\": "utils/phpstan-extensions/tests"
},
"classmap": [
"stubs/Annotations",

View File

@ -31,6 +31,12 @@ final class PhpDocInfoPrinter
*/
public const CLOSING_DOCBLOCK_REGEX = '#\*\/(\s+)?$#';
/**
* @var string
* @see https://regex101.com/r/Jzqzpw/1
*/
private const MISSING_NEWLINE_REGEX = '#([^\s])\*/$#';
/**
* @var string
*/
@ -125,6 +131,10 @@ final class PhpDocInfoPrinter
public function printNew(PhpDocInfo $phpDocInfo): string
{
$docContent = (string) $phpDocInfo->getPhpDocNode();
// fix missing newline in the end of docblock - keep BC compatible for both cases until phpstan with phpdoc-parser 0.5.2 is released
$docContent = Strings::replace($docContent, self::MISSING_NEWLINE_REGEX, "$1\n */");
if ($phpDocInfo->isSingleLine()) {
return $this->docBlockInliner->inline($docContent);
}

View File

@ -213,12 +213,6 @@ parameters:
- rules/Php80/Rector/Switch_/ChangeSwitchToMatchRector.php
- packages/NodeNestingScope/NodeFinder/ScopeAwareNodeFinder.php
# internal generating Rector
-
message: '#Class "Rector\\Utils\\DoctrineAnnotationParserSyncer\\Rector\\(.*?)" is missing @see annotation with test case class reference#'
paths:
- utils/doctrine-annotation-parser-syncer/src/Rector/*
-
message: '#Do not use setter on a service#'
paths:

View File

@ -273,7 +273,8 @@ CODE_SAMPLE
$throwableType = new ObjectType('Throwable');
$type = new ObjectType($class->toString());
return $throwableType->isSuperTypeOf($type)->yes();
return $throwableType->isSuperTypeOf($type)
->yes();
}
private function hasCall(Node $node): bool

View File

@ -110,7 +110,7 @@ CODE_SAMPLE
return true;
}
/** @var FuncCall|MethodCall|New_|NullsafeMethodCall|StaticCall $expr */
/** @var FuncCall|MethodCall|New_|NullsafeMethodCall|StaticCall $expr */
$expr = $assign->expr;
if (! $this->isCall($expr)) {
return false;
@ -136,7 +136,7 @@ CODE_SAMPLE
return false;
}
private function isCall(Expr $expr) : bool
private function isCall(Expr $expr): bool
{
return $expr instanceof FuncCall || $expr instanceof MethodCall || $expr instanceof New_ || $expr instanceof NullsafeMethodCall || $expr instanceof StaticCall;
}

View File

@ -7,12 +7,12 @@ namespace Rector\TypeDeclaration\Rector\Property;
use PhpParser\Node;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\MixedType;
use PHPStan\Type\NullType;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Core\Rector\AbstractRector;
use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use PHPStan\Type\NullType;
/**
* @see \Rector\Tests\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector\PropertyTypeDeclarationRectorTest

View File

@ -77,13 +77,6 @@ final class RectorConfigsResolver
}
}
// load config only if "sync" command is used to avoid useless rules traversing
if ($argvInput->getFirstArgument() === 'sync-annotation-parser') {
$configFileInfos[] = new SmartFileInfo(
__DIR__ . '/../../utils/doctrine-annotation-parser-syncer/config/config.php'
);
}
return new BootstrapConfigs($mainConfigFileInfo, $configFileInfos);
}
}

View File

@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->public()
->autowire()
->autoconfigure();
$services->load('Rector\Utils\DoctrineAnnotationParserSyncer\\', __DIR__ . '/../src');
};

View File

@ -1,42 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor;
use Rector\PostRector\Application\PostFileProcessor;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
final class ClassSyncerNodeTraverser extends NodeTraverser
{
/**
* @var PostFileProcessor
*/
private $postFileProcessor;
/**
* @param ClassSyncerRectorInterface[] $classSyncerRectors
*/
public function __construct(array $classSyncerRectors, PostFileProcessor $postFileProcessor)
{
foreach ($classSyncerRectors as $classSyncerRector) {
/** @var ClassSyncerRectorInterface&NodeVisitor $classSyncerRector */
$this->addVisitor($classSyncerRector);
}
$this->postFileProcessor = $postFileProcessor;
}
/**
* @param Node[] $nodes
* @return Node[]
*/
public function traverse(array $nodes): array
{
$traversedNodes = parent::traverse($nodes);
return $this->postFileProcessor->traverse($traversedNodes);
}
}

View File

@ -1,77 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Command;
use Rector\Core\Configuration\Option;
use Rector\Utils\DoctrineAnnotationParserSyncer\FileSyncer\AnnotationReaderClassSyncer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\PackageBuilder\Console\ShellCode;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
final class SyncAnnotationParserCommand extends Command
{
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var ParameterProvider
*/
private $parameterProvider;
/**
* @var AnnotationReaderClassSyncer
*/
private $annotationReaderClassSyncer;
public function __construct(
AnnotationReaderClassSyncer $annotationReaderClassSyncer,
SymfonyStyle $symfonyStyle,
ParameterProvider $parameterProvider
) {
$this->symfonyStyle = $symfonyStyle;
$this->parameterProvider = $parameterProvider;
$this->annotationReaderClassSyncer = $annotationReaderClassSyncer;
parent::__construct();
}
protected function configure(): void
{
$this->setDescription('[DEV] Generate value-preserving DocParser from doctrine/annotation');
$this->addOption(
Option::OPTION_DRY_RUN,
'n',
InputOption::VALUE_NONE,
'See diff of changes, do not save them to files.'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
// disable imports
$this->parameterProvider->changeParameter(Option::AUTO_IMPORT_NAMES, false);
$dryRun = (bool) $input->getOption(Option::OPTION_DRY_RUN);
$isSuccess = $this->annotationReaderClassSyncer->sync($dryRun);
if (! $isSuccess) {
$message = 'Doctrine Annotation files have changed, sync them: bin/rector sync-annotation-parser';
$this->symfonyStyle->error($message);
return ShellCode::ERROR;
}
$this->symfonyStyle->success('Constant preserving doctrine/annotation parser was updated');
return ShellCode::SUCCESS;
}
}

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector;
interface ClassSyncerRectorInterface
{
}

View File

@ -1,92 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\FileSyncer;
use PhpParser\Node;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\FileSystemRector\Parser\FileInfoParser;
use Rector\Utils\DoctrineAnnotationParserSyncer\ClassSyncerNodeTraverser;
use Symplify\SmartFileSystem\SmartFileInfo;
use Symplify\SmartFileSystem\SmartFileSystem;
final class AnnotationReaderClassSyncer
{
/**
* @var array<string, string>
*/
private const VENDOR_FILES_TO_LOCAL_FILES = [
__DIR__ . '/../../../../vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php' => __DIR__ . '/../../../../packages/DoctrineAnnotationGenerated/ConstantPreservingAnnotationReader.php',
__DIR__ . '/../../../../vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php' => __DIR__ . '/../../../../packages/DoctrineAnnotationGenerated/ConstantPreservingDocParser.php',
];
/**
* @var SmartFileSystem
*/
private $smartFileSystem;
/**
* @var FileInfoParser
*/
private $fileInfoParser;
/**
* @var ClassSyncerNodeTraverser
*/
private $classSyncerNodeTraverser;
/**
* @var BetterStandardPrinter
*/
private $betterStandardPrinter;
public function __construct(
BetterStandardPrinter $betterStandardPrinter,
FileInfoParser $fileInfoParser,
SmartFileSystem $smartFileSystem,
ClassSyncerNodeTraverser $classSyncerNodeTraverser
) {
$this->betterStandardPrinter = $betterStandardPrinter;
$this->fileInfoParser = $fileInfoParser;
$this->smartFileSystem = $smartFileSystem;
$this->classSyncerNodeTraverser = $classSyncerNodeTraverser;
}
public function sync(bool $isDryRun): bool
{
foreach (self::VENDOR_FILES_TO_LOCAL_FILES as $vendorFile => $localFile) {
$docParserFileInfo = new SmartFileInfo($vendorFile);
$fileNodes = $this->fileInfoParser->parseFileInfoToNodesAndDecorate($docParserFileInfo);
$changedNodes = $this->classSyncerNodeTraverser->traverse($fileNodes);
if ($isDryRun) {
return ! $this->hasContentChanged($fileNodes, $localFile);
}
// print file
$printedContent = $this->betterStandardPrinter->prettyPrintFile($changedNodes);
$this->smartFileSystem->dumpFile($localFile, $printedContent);
return true;
}
}
/**
* @param Node[] $nodes
*/
private function hasContentChanged(array $nodes, string $targetFilePath): bool
{
$finalContent = $this->betterStandardPrinter->prettyPrintFile($nodes);
// nothing to validate against
if (! file_exists($targetFilePath)) {
return false;
}
$currentContent = $this->smartFileSystem->readFile($targetFilePath);
// has content changed
return $finalContent !== $currentContent;
}
}

View File

@ -1,85 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Rector\Assign;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name\FullyQualified;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class AssignNewDocParserRector extends AbstractRector implements ClassSyncerRectorInterface
{
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
*/
public function refactor(Node $node): ?Node
{
/** @var Scope $scope */
$scope = $node->getAttribute(AttributeKey::SCOPE);
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return null;
}
if ($classReflection->getName() !== 'Doctrine\Common\Annotations\AnnotationReader') {
return null;
}
if (! $this->isName($node->var, 'preParser')) {
return null;
}
$node->expr = new New_(new FullyQualified('Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser'));
return $node;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Change $this->preParser assign to new doc parser', [
new CodeSample(
<<<'CODE_SAMPLE'
namespace Doctrine\Common\Annotations;
class AnnotationReader
{
public function run()
{
$this->preParser = ...
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
namespace Doctrine\Common\Annotations;
class AnnotationReader
{
public function run()
{
$this->preParser = new \Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser();
}
}
CODE_SAMPLE
),
]);
}
}

View File

@ -1,84 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\MethodName;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class ChangeOriginalTypeToCustomRector extends AbstractRector implements ClassSyncerRectorInterface
{
/**
* @return array<class-string<\PhpParser\Node>>
*/
public function getNodeTypes(): array
{
return [ClassMethod::class];
}
/**
* @param ClassMethod $node
*/
public function refactor(Node $node): ?Node
{
/** @var Scope $scope */
$scope = $node->getAttribute(AttributeKey::SCOPE);
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return null;
}
if ($classReflection->getName() !== 'Doctrine\Common\Annotations\AnnotationReader') {
return null;
}
if (! $this->isName($node, MethodName::CONSTRUCT)) {
return null;
}
$firstParam = $node->params[0];
$firstParam->type = new FullyQualified('Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser');
return $node;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Change DocParser type to custom one', [
new CodeSample(
<<<'CODE_SAMPLE'
namespace Doctrine\Common\Annotations;
class AnnotationReader
{
public function __construct(... $parser)
{
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
namespace Doctrine\Common\Annotations;
class AnnotationReader
{
public function __construct(\Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser $parser)
{
}
}
CODE_SAMPLE
),
]);
}
}

View File

@ -1,150 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Rector\AbstractRector;
use Rector\DoctrineAnnotationGenerated\DataCollector\ResolvedConstantStaticCollector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\DoctrineAnnotationGenerated\ConstantPreservingDocParser::Constant()
*/
final class LogIdentifierAndResolverValueInConstantClassMethodRector extends AbstractRector implements ClassSyncerRectorInterface
{
/**
* @return array<class-string<\PhpParser\Node>>
*/
public function getNodeTypes(): array
{
return [ClassMethod::class];
}
/**
* @param ClassMethod $node
*/
public function refactor(Node $node): ?Node
{
/** @var Scope $scope */
$scope = $node->getAttribute(AttributeKey::SCOPE);
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return null;
}
if ($classReflection->getName() !== 'Doctrine\Common\Annotations\DocParser') {
return null;
}
if (! $this->isName($node->name, 'Constant')) {
return null;
}
// 1. store original value right in the start
if (! isset($node->stmts[0])) {
return null;
}
$firstStmt = $node->stmts[0];
unset($node->stmts[0]);
$assignExpression = $this->createAssignOriginalIdentifierExpression();
$node->stmts = array_merge([$firstStmt], [$assignExpression], (array) $node->stmts);
// 2. record value in each return
$this->traverseNodesWithCallable($node->stmts, function (Node $node): ?Return_ {
if (! $node instanceof Return_) {
return null;
}
if ($node->expr === null) {
return null;
}
// assign resolved value to temporary variable
$resolvedValueVariable = new Variable('resolvedValue');
$assign = new Assign($resolvedValueVariable, $node->expr);
$assignExpression = new Expression($assign);
$this->addNodeBeforeNode($assignExpression, $node);
// log the value in static call
$originalIdentifier = new Variable('originalIdentifier');
$staticCallExpression = $this->createStaticCallExpression($originalIdentifier, $resolvedValueVariable);
$this->addNodeBeforeNode($staticCallExpression, $node);
$node->expr = $resolvedValueVariable;
return $node;
});
return $node;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Log original and changed constant value', [
new CodeSample(
<<<'CODE_SAMPLE'
namespace Doctrine\Common\Annotations;
class AnnotationReader
{
public function Constant()
{
// ...
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
namespace Doctrine\Common\Annotations;
class AnnotationReader
{
public function Constant()
{
$identifier = $this->Identifier();
$originalIdentifier = $identifier;
// ...
}
}
CODE_SAMPLE
),
]);
}
private function createAssignOriginalIdentifierExpression(): Expression
{
$originalIdentifier = new Variable('originalIdentifier');
$identifier = new Variable('identifier');
$assign = new Assign($originalIdentifier, $identifier);
return new Expression($assign);
}
private function createStaticCallExpression(Variable $identifierVariable, Variable $resolvedVariable): Expression
{
$arguments = [$identifierVariable, $resolvedVariable];
$staticCall = $this->nodeFactory->createStaticCall(
ResolvedConstantStaticCollector::class,
'collect',
$arguments
);
return new Expression($staticCall);
}
}

View File

@ -1,69 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Rector\Namespace_;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Namespace_;
use Rector\Core\Rector\AbstractRector;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class RenameAnnotationReaderClassRector extends AbstractRector implements ClassSyncerRectorInterface
{
/**
* @return array<class-string<\PhpParser\Node>>
*/
public function getNodeTypes(): array
{
return [Namespace_::class];
}
/**
* @param Namespace_ $node
*/
public function refactor(Node $node): ?Node
{
$firstClass = $this->betterNodeFinder->findFirstInstanceOf($node, Class_::class);
if (! $firstClass instanceof Class_) {
return null;
}
if (! $this->isName($firstClass, 'Doctrine\Common\Annotations\AnnotationReader')) {
return null;
}
$firstClass->name = new Identifier('ConstantPreservingAnnotationReader');
$node->name = new Name('Rector\DoctrineAnnotationGenerated');
return $node;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Rename AnnotationReader to own constant preserving format', [
new CodeSample(
<<<'CODE_SAMPLE'
namespace Doctrine\Common\Annotations;
class AnnotationReader
{
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
namespace Rector\DoctrineAnnotationGenerated;
class ConstantPreservingAnnotationReader
{
}
CODE_SAMPLE
),
]);
}
}

View File

@ -1,69 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Rector\Namespace_;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Namespace_;
use Rector\Core\Rector\AbstractRector;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class RenameDocParserClassRector extends AbstractRector implements ClassSyncerRectorInterface
{
/**
* @return array<class-string<\PhpParser\Node>>
*/
public function getNodeTypes(): array
{
return [Namespace_::class];
}
/**
* @param Namespace_ $node
*/
public function refactor(Node $node): ?Node
{
$firstClass = $this->betterNodeFinder->findFirstInstanceOf($node, Class_::class);
if (! $firstClass instanceof Class_) {
return null;
}
if (! $this->isName($firstClass, 'Doctrine\Common\Annotations\DocParser')) {
return null;
}
$firstClass->name = new Identifier('ConstantPreservingDocParser');
$node->name = new Name('Rector\DoctrineAnnotationGenerated');
return $node;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Rename DocParser to own constant preserving format', [
new CodeSample(
<<<'CODE_SAMPLE'
namespace Doctrine\Common\Annotations;
class DocParser
{
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
namespace Rector\DoctrineAnnotationGenerated;
class ConstantPreservingDocParser
{
}
CODE_SAMPLE
),
]);
}
}

View File

@ -1,88 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Rector\StaticCall;
use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class RemoveAnnotationRegistryRegisterFileRector extends AbstractRector implements ClassSyncerRectorInterface
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove registerFile() static calls from AnnotationParser', [
new CodeSample(
<<<'CODE_SAMPLE'
class AnnotationParser
{
public function run()
{
Doctrine\Common\Annotations\AnnotationRegistry::registerFile('...');
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class AnnotationParser
{
public function run()
{
}
}
CODE_SAMPLE
),
]);
}
/**
* @return array<class-string<\PhpParser\Node>>
*/
public function getNodeTypes(): array
{
return [StaticCall::class];
}
/**
* @param StaticCall $node
*/
public function refactor(Node $node): ?Node
{
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
return null;
}
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return null;
}
if (! $classReflection->isSubclassOf(
'Doctrine\Common\Annotations\DocParser'
) && ! $classReflection->isSubclassOf('Doctrine\Common\Annotations\AnnotationReader')) {
return null;
}
$callerType = $this->nodeTypeResolver->resolve($node->class);
if (! $callerType->isSuperTypeOf(new ObjectType('Doctrine\Common\Annotations\AnnotationRegistry'))->yes()) {
return null;
}
if (! $this->isName($node->name, 'registerFile')) {
return null;
}
$this->removeNode($node);
return $node;
}
}