Add PHPStanAttributeTypeSyncer

This commit is contained in:
TomasVotruba 2020-01-12 14:21:49 +01:00
parent f586d42b0f
commit 464eb22030
18 changed files with 727 additions and 4 deletions

View File

@ -50,6 +50,7 @@
"Rector\\": "src",
"Rector\\Autodiscovery\\": "packages/Autodiscovery/src",
"Rector\\Architecture\\": "packages/Architecture/src",
"Rector\\AttributeAwarePhpDoc\\": "packages/AttributeAwarePhpDoc/src",
"Rector\\BetterPhpDocParser\\": "packages/BetterPhpDocParser/src",
"Rector\\CakePHP\\": "packages/CakePHP/src",
"Rector\\Celebrity\\": "packages/Celebrity/src",
@ -102,6 +103,7 @@
"Rector\\ZendToSymfony\\": "packages/ZendToSymfony/src",
"Rector\\Utils\\DocumentationGenerator\\": "utils/DocumentationGenerator/src",
"Rector\\Utils\\RectorGenerator\\": "utils/RectorGenerator/src",
"Rector\\Utils\\PHPStanAttributeTypeSyncer\\": "utils/PHPStanAttributeTypeSyncer/src",
"Rector\\StrictCodeQuality\\": "packages/StrictCodeQuality/src",
"Rector\\DynamicTypeAnalysis\\": "packages/DynamicTypeAnalysis/src",
"Rector\\PhpDeglobalize\\": "packages/PhpDeglobalize/src",

View File

@ -1,5 +1,6 @@
services:
Rector\Renaming\Rector\Class_\RenameClassRector:
# see https://github.com/doctrine/persistence/pull/71
$oldToNewClasses:
Doctrine\Common\Persistence\Event\LifecycleEventArgs: Doctrine\Persistence\Event\LifecycleEventArgs
Doctrine\Common\Persistence\Event\LoadClassMetadataEventArgs: Doctrine\Persistence\Event\LoadClassMetadataEventArgs

View File

@ -22,6 +22,7 @@ services:
parameters:
sets:
- 'psr12'
- 'php70'
- 'php71'
- 'symplify'
- 'common'

View File

@ -6,7 +6,7 @@ use Symfony\Component\Yaml\Yaml;
require __DIR__ . '/../vendor/autoload.php';
### 11. CONFIGURE INPUT
### CONFIGURE INPUT HERE
$diffPath = 'https://github.com/symfony/symfony/commit/53a4711520d52bccd20fa6e616731114fa6eb61f.diff';
$classNamePrefix = 'Doctrine';

View File

@ -0,0 +1,9 @@
services:
_defaults:
autowire: true
public: true
Rector\AttributeAwarePhpDoc\:
resource: '../src'
exclude:
- '../src/Ast/*'

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Rector\AttributeAwarePhpDoc;
use Rector\AttributeAwarePhpDoc\Contract\AttributeNodeAwareFactory\AttributeNodeAwareFactoryInterface;
final class AttributeAwareNodeFactoryCollector
{
/**
* @var AttributeNodeAwareFactoryInterface[]
*/
private $attributeAwareNodeFactories = [];
/**
* @param AttributeNodeAwareFactoryInterface[] $attributeAwareNodeFactories
*/
public function __construct(array $attributeAwareNodeFactories)
{
$this->attributeAwareNodeFactories = $attributeAwareNodeFactories;
}
/**
* @return AttributeNodeAwareFactoryInterface[]
*/
public function provide(): array
{
return $this->attributeAwareNodeFactories;
}
/**
* @return string[]
*/
public function getSupportedNodeClasses(): array
{
$supportedNodeClasses = [];
foreach ($this->attributeAwareNodeFactories as $attributeAwareNodeFactory) {
$supportedNodeClasses[] = $attributeAwareNodeFactory->getOriginalNodeClass();
}
return $supportedNodeClasses;
}
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Rector\AttributeAwarePhpDoc\Contract\AttributeNodeAwareFactory;
use PHPStan\PhpDocParser\Ast\Node;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
interface AttributeNodeAwareFactoryInterface
{
public function getOriginalNodeClass(): string;
public function isMatch(Node $node): bool;
public function create(Node $node): AttributeAwareNodeInterface;
}

View File

@ -31,6 +31,7 @@ use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use Rector\AttributeAwarePhpDoc\AttributeAwareNodeFactoryCollector;
use Rector\BetterPhpDocParser\Ast\PhpDocNodeTraverser;
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwareDeprecatedTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwareExtendsTagValueNode;
@ -69,13 +70,21 @@ final class AttributeAwareNodeFactory
*/
private $phpDocNodeTraverser;
public function __construct(PhpDocNodeTraverser $phpDocNodeTraverser)
{
/**
* @var AttributeAwareNodeFactoryCollector
*/
private $attributeAwareNodeFactoryCollector;
public function __construct(
PhpDocNodeTraverser $phpDocNodeTraverser,
AttributeAwareNodeFactoryCollector $attributeAwareNodeFactoryCollector
) {
$this->phpDocNodeTraverser = $phpDocNodeTraverser;
$this->attributeAwareNodeFactoryCollector = $attributeAwareNodeFactoryCollector;
}
/**
* @return PhpDocNode|PhpDocChildNode|PhpDocTagValueNode AttributeAwareNodeInterface
* @return PhpDocNode|PhpDocChildNode|PhpDocTagValueNode|AttributeAwareNodeInterface
*/
public function createFromNode(Node $node): AttributeAwareNodeInterface
{
@ -95,6 +104,14 @@ final class AttributeAwareNodeFactory
return new AttributeAwarePhpDocNode($node->children);
}
foreach ($this->attributeAwareNodeFactoryCollector->provide() as $attributeNodeAwareFactory) {
if (! $attributeNodeAwareFactory->isMatch($node)) {
continue;
}
return $attributeNodeAwareFactory->create($node);
}
if ($node instanceof PhpDocTagNode) {
return new AttributeAwarePhpDocTagNode($node->name, $node->value);
}

View File

@ -0,0 +1,10 @@
services:
_defaults:
public: true
autowire: true
autoconfigure: true
Rector\Utils\PHPStanAttributeTypeSyncer\:
resource: '../src'
exclude:
- '../src/ValueObject/*'

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\ClassNaming;
use Rector\CodingStyle\Naming\ClassNaming;
use Rector\Utils\PHPStanAttributeTypeSyncer\ValueObject\Paths;
final class AttributeClassNaming
{
/**
* @var ClassNaming
*/
private $classNaming;
public function __construct(ClassNaming $classNaming)
{
$this->classNaming = $classNaming;
}
public function createAttributeAwareShortClassName(string $nodeClass): string
{
$shortMissingNodeClass = $this->classNaming->getShortName($nodeClass);
return 'AttributeAware' . $shortMissingNodeClass;
}
public function createAttributeAwareFactoryShortClassName(string $nodeClass): string
{
$shortMissingNodeClass = $this->classNaming->getShortName($nodeClass);
return 'AttributeAware' . $shortMissingNodeClass . 'Factory';
}
public function createAttributeAwareClassName(string $nodeClass): string
{
return Paths::NAMESPACE_PHPDOC_NODE . '\\' . $this->createAttributeAwareShortClassName($nodeClass);
}
public function createAttributeAwareFactoryClassName(string $nodeClass): string
{
return Paths::NAMESPACE_NODE_FACTORY . '\\' . $this->createAttributeAwareFactoryShortClassName($nodeClass);
}
}

View File

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Command;
use Rector\AttributeAwarePhpDoc\AttributeAwareNodeFactoryCollector;
use Rector\Console\Command\AbstractCommand;
use Rector\Console\Shell;
use Rector\Utils\PHPStanAttributeTypeSyncer\Finder\NodeClassFinder;
use Rector\Utils\PHPStanAttributeTypeSyncer\Generator\AttributeAwareNodeFactoryGenerator;
use Rector\Utils\PHPStanAttributeTypeSyncer\Generator\AttributeAwareNodeGenerator;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\PackageBuilder\Console\Command\CommandNaming;
final class SyncTypesCommand extends AbstractCommand
{
/**
* @var AttributeAwareNodeFactoryCollector
*/
private $attributeAwareNodeFactoryCollector;
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var NodeClassFinder
*/
private $nodeClassFinder;
/**
* @var AttributeAwareNodeGenerator
*/
private $attributeAwareNodeGenerator;
/**
* @var AttributeAwareNodeFactoryGenerator
*/
private $attributeAwareNodeFactoryGenerator;
public function __construct(
AttributeAwareNodeFactoryCollector $attributeAwareNodeFactoryCollector,
SymfonyStyle $symfonyStyle,
NodeClassFinder $nodeClassFinder,
AttributeAwareNodeGenerator $attributeAwareNodeGenerator,
AttributeAwareNodeFactoryGenerator $attributeAwareNodeFactoryGenerator
) {
$this->attributeAwareNodeFactoryCollector = $attributeAwareNodeFactoryCollector;
$this->symfonyStyle = $symfonyStyle;
$this->nodeClassFinder = $nodeClassFinder;
$this->attributeAwareNodeGenerator = $attributeAwareNodeGenerator;
$this->attributeAwareNodeFactoryGenerator = $attributeAwareNodeFactoryGenerator;
parent::__construct();
}
protected function configure(): void
{
$this->setName(CommandNaming::classToName(self::class));
$this->setDescription('[Dev] Synchronize PHPStan types to attribute aware types in Rectors');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$missingNodeClasses = $this->getMissingNodeClasses();
if ($missingNodeClasses === []) {
$this->symfonyStyle->success(
'All PHPStan Doc Parser nodes are covered with attribute aware mirror in Rector'
);
return Shell::CODE_SUCCESS;
}
$this->symfonyStyle->error('These classes are missing their attribute aware brother');
foreach ($missingNodeClasses as $missingNodeClass) {
// 1. generate node
$this->attributeAwareNodeGenerator->generateFromPhpDocParserNodeClass($missingNodeClass);
// 2. generate node factory...
$this->attributeAwareNodeFactoryGenerator->generateFromPhpDocParserNodeClass($missingNodeClass);
}
return Shell::CODE_ERROR;
}
/**
* @return string[]
*/
private function getMissingNodeClasses(): array
{
$phpDocParserTagValueNodeClasses = $this->nodeClassFinder->findCurrentPHPDocParserNodeClasses();
$supportedNodeClasses = $this->attributeAwareNodeFactoryCollector->getSupportedNodeClasses();
return array_diff($phpDocParserTagValueNodeClasses, $supportedNodeClasses);
}
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Finder;
use Nette\Loaders\RobotLoader;
final class NodeClassFinder
{
/**
* @return string[]
*/
public function findCurrentPHPDocParserNodeClasses(): array
{
return $this->findClassesByNamePatternInDirectories(
'*Node.php',
[
// @todo not sure if needed
// __DIR__ . '/../../../../vendor/phpstan/phpdoc-parser/src/Ast/Type',
__DIR__ . '/../../../../vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc',
]
);
}
/**
* @param string[] $directories
* @return string[]
*/
private function findClassesByNamePatternInDirectories(string $namePattern, array $directories): array
{
$robotLoader = new RobotLoader();
foreach ($directories as $directory) {
$robotLoader->addDirectory($directory);
}
$robotLoader->setTempDirectory(sys_get_temp_dir() . '/_phpdoc_parser_ast');
$robotLoader->acceptFiles = [$namePattern];
$robotLoader->rebuild();
$classLikesToPaths = $robotLoader->getIndexedClasses();
$classLikes = array_keys($classLikesToPaths);
$excludedClasses = ['PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode'];
// keep only classes, skip interfaces
$classes = [];
foreach ($classLikes as $classLike) {
if (! class_exists($classLike)) {
continue;
}
$classes[] = $classLike;
}
return array_diff($classes, $excludedClasses);
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Generator;
use Nette\Utils\FileSystem;
use PhpParser\Node\Stmt\Namespace_;
use Rector\PhpParser\Printer\BetterStandardPrinter;
abstract class AbstractAttributeAwareNodeGenerator
{
/**
* @var BetterStandardPrinter
*/
private $betterStandardPrinter;
/**
* @required
*/
public function autowireAbstractAttributeAwareNodeGenerator(BetterStandardPrinter $betterStandardPrinter): void
{
$this->betterStandardPrinter = $betterStandardPrinter;
}
protected function printNamespaceToFile(Namespace_ $namespace, string $targetFilePath): void
{
$fileContent = $this->betterStandardPrinter->prettyPrintFile([$namespace]);
FileSystem::write($targetFilePath, $fileContent);
}
}

View File

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Generator;
use Rector\Utils\PHPStanAttributeTypeSyncer\ClassNaming\AttributeClassNaming;
use Rector\Utils\PHPStanAttributeTypeSyncer\NodeFactory\AttributeAwareClassFactoryFactory;
use Symfony\Component\Console\Style\SymfonyStyle;
final class AttributeAwareNodeFactoryGenerator extends AbstractAttributeAwareNodeGenerator
{
/**
* @var AttributeClassNaming
*/
private $attributeClassNaming;
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var AttributeAwareClassFactoryFactory
*/
private $attributeAwareClassFactoryFactory;
public function __construct(
AttributeClassNaming $attributeClassNaming,
SymfonyStyle $symfonyStyle,
AttributeAwareClassFactoryFactory $attributeAwareClassFactoryFactory
) {
$this->attributeClassNaming = $attributeClassNaming;
$this->symfonyStyle = $symfonyStyle;
$this->attributeAwareClassFactoryFactory = $attributeAwareClassFactoryFactory;
}
public function generateFromPhpDocParserNodeClass(string $phpDocParserNodeClass): void
{
$shortClassName = $this->attributeClassNaming->createAttributeAwareFactoryShortClassName(
$phpDocParserNodeClass
);
// write file
$targetFilePath = __DIR__ . '/../../../../packages/AttributeAwarePhpDoc/src/AttributeAwareNodeFactory/' . $shortClassName . '.php';
// prevent file override
if (file_exists($targetFilePath)) {
$realTargetFilePath = realpath($targetFilePath);
$this->symfonyStyle->note(sprintf('File "%s" already exists, skipping', $realTargetFilePath));
return;
}
$namespace = $this->attributeAwareClassFactoryFactory->createFromPhpDocParserNodeClass($phpDocParserNodeClass);
$this->printNamespaceToFile($namespace, $targetFilePath);
$this->reportGeneratedAttributeAwareNode($phpDocParserNodeClass, $targetFilePath);
}
private function reportGeneratedAttributeAwareNode(string $missingNodeClass, string $filePath): void
{
$attributeAwareFullyQualifiedClassName = $this->attributeClassNaming->createAttributeAwareClassName(
$missingNodeClass
);
$this->symfonyStyle->note(sprintf(
'Class "%s" now has freshly generated "%s" in "%s"',
$missingNodeClass,
$attributeAwareFullyQualifiedClassName,
$filePath
));
}
}

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Generator;
use Rector\Utils\PHPStanAttributeTypeSyncer\ClassNaming\AttributeClassNaming;
use Rector\Utils\PHPStanAttributeTypeSyncer\NodeFactory\AttributeAwareClassFactory;
use Symfony\Component\Console\Style\SymfonyStyle;
final class AttributeAwareNodeGenerator extends AbstractAttributeAwareNodeGenerator
{
/**
* @var AttributeClassNaming
*/
private $attributeClassNaming;
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var AttributeAwareClassFactory
*/
private $attributeAwareClassFactory;
public function __construct(
AttributeClassNaming $attributeClassNaming,
SymfonyStyle $symfonyStyle,
AttributeAwareClassFactory $attributeAwareClassFactory
) {
$this->attributeClassNaming = $attributeClassNaming;
$this->symfonyStyle = $symfonyStyle;
$this->attributeAwareClassFactory = $attributeAwareClassFactory;
}
public function generateFromPhpDocParserNodeClass(string $phpDocParserNodeClass): void
{
$shortClassName = $this->attributeClassNaming->createAttributeAwareShortClassName($phpDocParserNodeClass);
// write file
$targetFilePath = __DIR__ . '/../../../../packages/AttributeAwarePhpDoc/src/Ast/PhpDoc/' . $shortClassName . '.php';
// prevent file override
if (file_exists($targetFilePath)) {
$realTargetFilePath = realpath($targetFilePath);
$this->symfonyStyle->note(sprintf('File "%s" already exists, skipping', $realTargetFilePath));
return;
}
$namespace = $this->attributeAwareClassFactory->createFromPhpDocParserNodeClass($phpDocParserNodeClass);
$this->printNamespaceToFile($namespace, $targetFilePath);
$this->reportGeneratedAttributeAwareNode($phpDocParserNodeClass);
}
private function reportGeneratedAttributeAwareNode(string $missingNodeClass): void
{
$attributeAwareFullyQualifiedClassName = $this->attributeClassNaming->createAttributeAwareClassName(
$missingNodeClass
);
$this->symfonyStyle->note(sprintf(
'Class "%s" now has freshly generated "%s"',
$missingNodeClass,
$attributeAwareFullyQualifiedClassName
));
}
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\NodeFactory;
use PhpParser\Builder\Class_;
use PhpParser\BuilderFactory;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Namespace_;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\Utils\PHPStanAttributeTypeSyncer\ClassNaming\AttributeClassNaming;
use Rector\Utils\PHPStanAttributeTypeSyncer\ValueObject\Paths;
final class AttributeAwareClassFactory
{
/**
* @var BuilderFactory
*/
private $builderFactory;
/**
* @var AttributeClassNaming
*/
private $attributeClassNaming;
public function __construct(BuilderFactory $builderFactory, AttributeClassNaming $attributeClassNaming)
{
$this->builderFactory = $builderFactory;
$this->attributeClassNaming = $attributeClassNaming;
}
public function createFromPhpDocParserNodeClass(string $nodeClass): Namespace_
{
$namespaceBuilder = $this->builderFactory->namespace(Paths::NAMESPACE_PHPDOC_NODE);
$shortClassName = $this->attributeClassNaming->createAttributeAwareShortClassName($nodeClass);
$classBuilder = $this->createClassBuilder($nodeClass, $shortClassName);
$useTrait = $this->builderFactory->useTrait(new FullyQualified(AttributeTrait::class));
$classBuilder->addStmt($useTrait);
$namespaceBuilder->addStmt($classBuilder->getNode());
return $namespaceBuilder->getNode();
}
private function createClassBuilder(string $nodeClass, string $shortClassName): Class_
{
$classBuilder = $this->builderFactory->class($shortClassName);
$classBuilder->makeFinal();
$classBuilder->extend(new FullyQualified($nodeClass));
$classBuilder->implement(new FullyQualified(AttributeAwareNodeInterface::class));
return $classBuilder;
}
}

View File

@ -0,0 +1,166 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\NodeFactory;
use PhpParser\Builder\Param;
use PhpParser\BuilderFactory;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Return_;
use PHPStan\PhpDocParser\Ast\Node;
use Rector\AttributeAwarePhpDoc\Contract\AttributeNodeAwareFactory\AttributeNodeAwareFactoryInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\Utils\PHPStanAttributeTypeSyncer\ClassNaming\AttributeClassNaming;
use Rector\Utils\PHPStanAttributeTypeSyncer\ValueObject\Paths;
use ReflectionClass;
final class AttributeAwareClassFactoryFactory
{
/**
* @var BuilderFactory
*/
private $builderFactory;
/**
* @var AttributeClassNaming
*/
private $attributeClassNaming;
public function __construct(BuilderFactory $builderFactory, AttributeClassNaming $attributeClassNaming)
{
$this->builderFactory = $builderFactory;
$this->attributeClassNaming = $attributeClassNaming;
}
public function createFromPhpDocParserNodeClass(string $nodeClass): Namespace_
{
$namespaceBuilder = $this->builderFactory->namespace(Paths::NAMESPACE_NODE_FACTORY);
$shortClassName = $this->attributeClassNaming->createAttributeAwareFactoryShortClassName($nodeClass);
$classBuilder = $this->builderFactory->class($shortClassName);
$classBuilder->makeFinal();
$classBuilder->implement(new FullyQualified(AttributeNodeAwareFactoryInterface::class));
$classMethods = $this->createClassMethods($nodeClass);
$classBuilder->addStmts($classMethods);
$namespaceBuilder->addStmt($classBuilder->getNode());
return $namespaceBuilder->getNode();
}
/**
* @return ClassMethod[]
*/
private function createClassMethods(string $nodeClass): array
{
$classMethods = [];
$classMethods[] = $this->createGetOriginalNodeClass($nodeClass);
$nodeParam = $this->builderFactory->param('node');
$nodeParam->setType(new FullyQualified(Node::class));
$classMethods[] = $this->createIsMatchClassMethod($nodeClass, $nodeParam);
$classMethods[] = $this->createCreateClassMethod($nodeClass, $nodeParam);
return $classMethods;
}
private function createIsAFuncCall(string $nodeClass): FuncCall
{
return new FuncCall(new Name('is_a'), [
new Variable('node'),
$this->createClassReference($nodeClass),
new ConstFetch(new Name('true')),
]);
}
private function createClassReference(string $nodeClass): ClassConstFetch
{
return new ClassConstFetch(new FullyQualified($nodeClass), 'class');
}
private function createIsMatchClassMethod(string $nodeClass, Param $param): ClassMethod
{
$isMatchClassMethod = $this->builderFactory->method('isMatch');
$isMatchClassMethod->addParam($param);
$isMatchClassMethod->makePublic();
$isMatchClassMethod->setReturnType('bool');
$isAFuncCall = $this->createIsAFuncCall($nodeClass);
$isMatchClassMethod->addStmt(new Return_($isAFuncCall));
return $isMatchClassMethod->getNode();
}
private function createGetOriginalNodeClass(string $nodeClass): ClassMethod
{
$getOriginalNodeClassClassMethod = $this->builderFactory->method('getOriginalNodeClass');
$getOriginalNodeClassClassMethod->makePublic();
$getOriginalNodeClassClassMethod->setReturnType('string');
$classReference = $this->createClassReference($nodeClass);
$getOriginalNodeClassClassMethod->addStmt(new Return_($classReference));
return $getOriginalNodeClassClassMethod->getNode();
}
private function createCreateClassMethod(string $nodeClass, Param $nodeParam): ClassMethod
{
$createClassMethod = $this->builderFactory->method('create');
$createClassMethod->addParam($nodeParam);
$createClassMethod->makePublic();
$createClassMethod->setReturnType(new FullyQualified(AttributeAwareNodeInterface::class));
// add @param doc with more precise type
$paramDocBlock = sprintf('/**%s * @param \\%s%s */', PHP_EOL, $nodeClass, PHP_EOL);
$createClassMethod->setDocComment($paramDocBlock);
$attributeAwareClassName = $this->attributeClassNaming->createAttributeAwareClassName($nodeClass);
$new = new New_(new FullyQualified($attributeAwareClassName));
// complete new args
$this->completeNewArgs($new, $nodeClass);
$createClassMethod->addStmt(new Return_($new));
return $createClassMethod->getNode();
}
private function completeNewArgs(New_ $new, string $phpDocParserNodeClass): void
{
// ...
$reflectionClass = new ReflectionClass($phpDocParserNodeClass);
$constructorReflectionMethod = $reflectionClass->getConstructor();
// no constructor → no params to add
if ($constructorReflectionMethod === null) {
return;
}
$phpDocParserNodeVariable = new Variable('node');
foreach ($constructorReflectionMethod->getParameters() as $parameter) {
$parameterName = $parameter->getName();
$new->args[] = new Arg(new PropertyFetch($phpDocParserNodeVariable, $parameterName));
}
}
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Rector\Utils\PHPStanAttributeTypeSyncer\ValueObject;
final class Paths
{
/**
* @var string
*/
public const NAMESPACE_PHPDOC_NODE = 'Rector\AttributeAwarePhpDoc\Ast\PhpDoc';
/**
* @var string
*/
public const NAMESPACE_NODE_FACTORY = 'Rector\AttributeAwarePhpDoc\AttributeAwareNodeFactory';
}