mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-01 08:50:50 +00:00
drop PHPStanAttributeTypeSyncer, handled by external package (#5131)
This commit is contained in:
parent
074298d35c
commit
d1df8f76cd
4
.github/workflows/code_analysis.yaml
vendored
4
.github/workflows/code_analysis.yaml
vendored
|
@ -43,10 +43,6 @@ jobs:
|
|||
# this is very slow, so it has to be in own workflow
|
||||
run: bin/rector validate-sets --ansi
|
||||
|
||||
-
|
||||
name: 'Validate PHPStan Types Compatibility'
|
||||
run: bin/rector sync-types --ansi
|
||||
|
||||
-
|
||||
name: 'Validate PHPStan Mappers Compatibility'
|
||||
run: bin/rector check-static-type-mappers --ansi
|
||||
|
|
|
@ -283,7 +283,6 @@
|
|||
"Rector\\Utils\\DoctrineAnnotationParserSyncer\\": "utils/doctrine-annotation-parser-syncer/src",
|
||||
"Rector\\Utils\\NodeDocumentationGenerator\\": "utils/node-documentation-generator/src",
|
||||
"Rector\\Utils\\NodeDocumentationGenerator\\Tests\\": "utils/node-documentation-generator/tests",
|
||||
"Rector\\Utils\\PHPStanAttributeTypeSyncer\\": "utils/phpstan-attribute-type-syncer/src",
|
||||
"Rector\\Utils\\PHPStanTypeMapperChecker\\": "utils/phpstan-type-mapper-checker/src",
|
||||
"Rector\\Utils\\ProjectValidator\\": "utils/project-validator/src",
|
||||
"Rector\\Carbon\\Tests\\": "rules/carbon/tests"
|
||||
|
|
|
@ -6,7 +6,7 @@ sonar.projectKey=rectorphp_rector
|
|||
# wildcards don't work :(
|
||||
sonar.sources=src,rules/autodiscovery/src,rules/architecture/src,packages/attribute-aware-php-doc/src,packages/better-php-doc-parser/src,rules/cakephp/src,rules/code-quality/src,rules/coding-style/src,packages/console-differ/src,rules/dead-code/src,rules/doctrine/src,rules/doctrine-code-quality/src,packages/file-system-rector/src,rules/laravel/src,rules/legacy/src,rules/mysql-to-mysqli/src,rules/nette-tester-to-phpunit/src,rules/nette-to-symfony/src,rules/nette/src,packages/node-collector/src,packages/node-type-resolver/src,packages/node-name-resolver/src,packages/phpstan-static-type-mapper/src,rules/phpunit-symfony/src,rules/phpunit/src,rules/psr4/src,rules/php-spec-to-phpunit/src,rules/php52/src,rules/php53/src,rules/php54/src,rules/php55/src,rules/php56/src,rules/php70/src,rules/php71/src,rules/php72/src,rules/php73/src,rules/php74/src,rules/php80/src,rules/removing-static/src,rules/renaming/src,rules/restoration/src,rules/dependency-injection/src,rules/sensio/src,packages/static-type-mapper/src,rules/symfony-code-quality/src,rules/symfony-phpunit/src,rules/symfony/src,rules/twig/src,rules/type-declaration/src,packages/vendor-locker/src,packages/rector-generator/src,rules/code-quality-strict/src,rules/phalcon/src,rules/doctrine-gedmo-to-knplabs/src,rules/polyfill/src,rules/generic/src
|
||||
|
||||
sonar.tests=tests,rules/autodiscovery/tests,rules/architecture/tests,packages/better-php-doc-parser/tests,rules/cakephp/tests,rules/code-quality/tests,rules/coding-style/tests,rules/dead-code/tests,rules/doctrine/tests,rules/doctrine-code-quality/tests,rules/laravel/tests,rules/legacy/tests,rules/mysql-to-mysqli/tests,rules/nette-tester-to-phpunit/tests,rules/nette-to-symfony/tests,rules/nette/tests,packages/node-type-resolver/tests,utils/phpstan-extensions/src,rules/phpunit-symfony/tests,rules/phpunit/tests,rules/psr4/tests,rules/php-spec-to-phpunit/tests,rules/php52/tests,rules/php53/tests,rules/php54/tests,rules/php55/tests,rules/php56/tests,rules/php70/tests,rules/php71/tests,rules/php72/tests,rules/php73/tests,rules/php74/tests,rules/php80/tests,rules/removing-static/tests,rules/renaming/tests,rules/restoration/tests,rules/dependency-injection/tests,rules/sensio/tests,rules/symfony-code-quality/tests,rules/symfony-phpunit/tests,rules/symfony/tests,rules/twig/tests,rules/type-declaration/tests,rules/code-quality-strict/tests,rules/phalcon/tests,utils/node-documentation-generator/src,utils/phpstan-attribute-type-syncer/src,utils/phpstan-type-mapper-checker/src,rules/doctrine-gedmo-to-knplabs/tests,rules/polyfill/tests,rules/generic/tests
|
||||
sonar.tests=tests,rules/autodiscovery/tests,rules/architecture/tests,packages/better-php-doc-parser/tests,rules/cakephp/tests,rules/code-quality/tests,rules/coding-style/tests,rules/dead-code/tests,rules/doctrine/tests,rules/doctrine-code-quality/tests,rules/laravel/tests,rules/legacy/tests,rules/mysql-to-mysqli/tests,rules/nette-tester-to-phpunit/tests,rules/nette-to-symfony/tests,rules/nette/tests,packages/node-type-resolver/tests,utils/phpstan-extensions/src,rules/phpunit-symfony/tests,rules/phpunit/tests,rules/psr4/tests,rules/php-spec-to-phpunit/tests,rules/php52/tests,rules/php53/tests,rules/php54/tests,rules/php55/tests,rules/php56/tests,rules/php70/tests,rules/php71/tests,rules/php72/tests,rules/php73/tests,rules/php74/tests,rules/php80/tests,rules/removing-static/tests,rules/renaming/tests,rules/restoration/tests,rules/dependency-injection/tests,rules/sensio/tests,rules/symfony-code-quality/tests,rules/symfony-phpunit/tests,rules/symfony/tests,rules/twig/tests,rules/type-declaration/tests,rules/code-quality-strict/tests,rules/phalcon/tests,utils/node-documentation-generator/src,utils/phpstan-type-mapper-checker/src,rules/doctrine-gedmo-to-knplabs/tests,rules/polyfill/tests,rules/generic/tests
|
||||
|
||||
# possibly manual operation needed :/
|
||||
# https://docs.sonarqube.org/latest/project-administration/narrowing-the-focus/
|
||||
|
|
|
@ -1,17 +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\PHPStanAttributeTypeSyncer\\', __DIR__ . '/../src')
|
||||
->exclude([__DIR__ . '/../src/ValueObject']);
|
||||
};
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\PHPStanAttributeTypeSyncer\ClassNaming;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
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
|
||||
{
|
||||
if (Strings::contains($nodeClass, '\\Type\\')) {
|
||||
$namespace = Paths::NAMESPACE_TYPE_NODE;
|
||||
} else {
|
||||
$namespace = Paths::NAMESPACE_PHPDOC_NODE;
|
||||
}
|
||||
|
||||
return $namespace . '\\' . $this->createAttributeAwareShortClassName($nodeClass);
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Command;
|
||||
|
||||
use Rector\AttributeAwarePhpDoc\AttributeAwareNodeFactoryCollector;
|
||||
use Rector\Core\Console\Command\AbstractCommand;
|
||||
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\ShellCode;
|
||||
|
||||
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,
|
||||
AttributeAwareNodeFactoryGenerator $attributeAwareNodeFactoryGenerator,
|
||||
AttributeAwareNodeGenerator $attributeAwareNodeGenerator,
|
||||
NodeClassFinder $nodeClassFinder,
|
||||
SymfonyStyle $symfonyStyle
|
||||
) {
|
||||
$this->attributeAwareNodeFactoryCollector = $attributeAwareNodeFactoryCollector;
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
$this->nodeClassFinder = $nodeClassFinder;
|
||||
$this->attributeAwareNodeGenerator = $attributeAwareNodeGenerator;
|
||||
$this->attributeAwareNodeFactoryGenerator = $attributeAwareNodeFactoryGenerator;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$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 ShellCode::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 ShellCode::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getMissingNodeClasses(): array
|
||||
{
|
||||
$phpDocParserTagValueNodeClasses = $this->nodeClassFinder->findCurrentPHPDocParserNodeClasses();
|
||||
$supportedNodeClasses = $this->attributeAwareNodeFactoryCollector->getSupportedNodeClasses();
|
||||
|
||||
return array_diff($phpDocParserTagValueNodeClasses, $supportedNodeClasses);
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Finder;
|
||||
|
||||
use Nette\Loaders\RobotLoader;
|
||||
|
||||
final class NodeClassFinder
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const EXCLUDED_CLASSES = ['PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode'];
|
||||
|
||||
/**
|
||||
* @return class-string[]
|
||||
*/
|
||||
public function findCurrentPHPDocParserNodeClasses(): array
|
||||
{
|
||||
return $this->findClassesByNamePatternInDirectories(
|
||||
'*Node.php',
|
||||
[
|
||||
__DIR__ . '/../../../../vendor/phpstan/phpdoc-parser/src/Ast/Type',
|
||||
__DIR__ . '/../../../../vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $directories
|
||||
* @return class-string[]
|
||||
*/
|
||||
private function findClassesByNamePatternInDirectories(string $namePattern, array $directories): array
|
||||
{
|
||||
$robotLoader = new RobotLoader();
|
||||
$robotLoader->addDirectory(...$directories);
|
||||
|
||||
$robotLoader->setTempDirectory(sys_get_temp_dir() . '/_phpdoc_parser_ast');
|
||||
|
||||
$robotLoader->acceptFiles = [$namePattern];
|
||||
|
||||
$robotLoader->rebuild();
|
||||
|
||||
$indexedClasses = $robotLoader->getIndexedClasses();
|
||||
|
||||
$classLikes = array_keys($indexedClasses);
|
||||
|
||||
// keep only classes, skip interfaces
|
||||
$classes = [];
|
||||
foreach ($classLikes as $classLike) {
|
||||
if (! class_exists($classLike)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classes[] = $classLike;
|
||||
}
|
||||
|
||||
return array_diff($classes, self::EXCLUDED_CLASSES);
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Generator;
|
||||
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
abstract class AbstractAttributeAwareNodeGenerator
|
||||
{
|
||||
/**
|
||||
* @var BetterStandardPrinter
|
||||
*/
|
||||
private $betterStandardPrinter;
|
||||
|
||||
/**
|
||||
* @var SmartFileSystem
|
||||
*/
|
||||
private $smartFileSystem;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireAbstractAttributeAwareNodeGenerator(
|
||||
BetterStandardPrinter $betterStandardPrinter,
|
||||
SmartFileSystem $smartFileSystem
|
||||
): void {
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
$this->smartFileSystem = $smartFileSystem;
|
||||
}
|
||||
|
||||
protected function printNamespaceToFile(Namespace_ $namespace, string $targetFilePath): void
|
||||
{
|
||||
$fileContent = $this->betterStandardPrinter->prettyPrintFile([$namespace]);
|
||||
|
||||
$this->smartFileSystem->dumpFile($targetFilePath, $fileContent);
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Generator;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
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(
|
||||
AttributeAwareClassFactoryFactory $attributeAwareClassFactoryFactory,
|
||||
AttributeClassNaming $attributeClassNaming,
|
||||
SymfonyStyle $symfonyStyle
|
||||
) {
|
||||
$this->attributeClassNaming = $attributeClassNaming;
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
$this->attributeAwareClassFactoryFactory = $attributeAwareClassFactoryFactory;
|
||||
}
|
||||
|
||||
public function generateFromPhpDocParserNodeClass(string $phpDocParserNodeClass): void
|
||||
{
|
||||
$targetFilePath = $this->resolveTargetFilePath($phpDocParserNodeClass);
|
||||
|
||||
// prevent file override
|
||||
if (file_exists($targetFilePath)) {
|
||||
$realTargetFilePath = realpath($targetFilePath);
|
||||
$message = sprintf('File "%s" already exists, skipping', $realTargetFilePath);
|
||||
$this->symfonyStyle->note($message);
|
||||
return;
|
||||
}
|
||||
|
||||
$namespace = $this->attributeAwareClassFactoryFactory->createFromPhpDocParserNodeClass($phpDocParserNodeClass);
|
||||
$this->printNamespaceToFile($namespace, $targetFilePath);
|
||||
|
||||
$this->reportGeneratedAttributeAwareNode($phpDocParserNodeClass, $targetFilePath);
|
||||
}
|
||||
|
||||
private function resolveTargetFilePath(string $phpDocParserNodeClass): string
|
||||
{
|
||||
$shortClassName = $this->attributeClassNaming->createAttributeAwareFactoryShortClassName(
|
||||
$phpDocParserNodeClass
|
||||
);
|
||||
|
||||
if (Strings::contains($phpDocParserNodeClass, '\\Type\\')) {
|
||||
return __DIR__ . '/../../../../packages/attribute-aware-php-doc/src/AttributeAwareNodeFactory/Type/' . $shortClassName . '.php';
|
||||
}
|
||||
|
||||
return __DIR__ . '/../../../../packages/attribute-aware-php-doc/src/AttributeAwareNodeFactory/PhpDoc/' . $shortClassName . '.php';
|
||||
}
|
||||
|
||||
private function reportGeneratedAttributeAwareNode(string $missingNodeClass, string $filePath): void
|
||||
{
|
||||
$attributeAwareFullyQualifiedClassName = $this->attributeClassNaming->createAttributeAwareClassName(
|
||||
$missingNodeClass
|
||||
);
|
||||
$message = sprintf(
|
||||
'Class "%s" now has freshly generated "%s" in "%s"',
|
||||
$missingNodeClass,
|
||||
$attributeAwareFullyQualifiedClassName,
|
||||
$filePath
|
||||
);
|
||||
|
||||
$this->symfonyStyle->note($message);
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\PHPStanAttributeTypeSyncer\Generator;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
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(
|
||||
AttributeAwareClassFactory $attributeAwareClassFactory,
|
||||
AttributeClassNaming $attributeClassNaming,
|
||||
SymfonyStyle $symfonyStyle
|
||||
) {
|
||||
$this->attributeClassNaming = $attributeClassNaming;
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
$this->attributeAwareClassFactory = $attributeAwareClassFactory;
|
||||
}
|
||||
|
||||
public function generateFromPhpDocParserNodeClass(string $phpDocParserNodeClass): void
|
||||
{
|
||||
$targetFilePath = $this->resolveTargetFilePath($phpDocParserNodeClass);
|
||||
|
||||
// prevent file override
|
||||
if (file_exists($targetFilePath)) {
|
||||
$realTargetFilePath = realpath($targetFilePath);
|
||||
$message = sprintf('File "%s" already exists, skipping', $realTargetFilePath);
|
||||
$this->symfonyStyle->note($message);
|
||||
return;
|
||||
}
|
||||
|
||||
$namespace = $this->attributeAwareClassFactory->createFromPhpDocParserNodeClass($phpDocParserNodeClass);
|
||||
|
||||
$this->printNamespaceToFile($namespace, $targetFilePath);
|
||||
|
||||
$this->reportGeneratedAttributeAwareNode($phpDocParserNodeClass);
|
||||
}
|
||||
|
||||
private function resolveTargetFilePath(string $phpDocParserNodeClass): string
|
||||
{
|
||||
$shortClassName = $this->attributeClassNaming->createAttributeAwareShortClassName($phpDocParserNodeClass);
|
||||
|
||||
if (Strings::contains($phpDocParserNodeClass, '\\Type\\')) {
|
||||
return __DIR__ . '/../../../../packages/attribute-aware-php-doc/src/Ast/Type/' . $shortClassName . '.php';
|
||||
}
|
||||
|
||||
return __DIR__ . '/../../../../packages/attribute-aware-php-doc/src/Ast/PhpDoc/' . $shortClassName . '.php';
|
||||
}
|
||||
|
||||
private function reportGeneratedAttributeAwareNode(string $missingNodeClass): void
|
||||
{
|
||||
$attributeAwareFullyQualifiedClassName = $this->attributeClassNaming->createAttributeAwareClassName(
|
||||
$missingNodeClass
|
||||
);
|
||||
$message = sprintf(
|
||||
'Class "%s" now has freshly generated "%s"',
|
||||
$missingNodeClass,
|
||||
$attributeAwareFullyQualifiedClassName
|
||||
);
|
||||
|
||||
$this->symfonyStyle->note($message);
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\PHPStanAttributeTypeSyncer\NodeFactory;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
|
||||
use Rector\Core\PhpParser\Builder\ClassBuilder;
|
||||
use Rector\Core\PhpParser\Builder\NamespaceBuilder;
|
||||
use Rector\Core\PhpParser\Builder\TraitUseBuilder;
|
||||
use Rector\Utils\PHPStanAttributeTypeSyncer\ClassNaming\AttributeClassNaming;
|
||||
use Rector\Utils\PHPStanAttributeTypeSyncer\ValueObject\Paths;
|
||||
|
||||
final class AttributeAwareClassFactory
|
||||
{
|
||||
/**
|
||||
* @var AttributeClassNaming
|
||||
*/
|
||||
private $attributeClassNaming;
|
||||
|
||||
public function __construct(AttributeClassNaming $attributeClassNaming)
|
||||
{
|
||||
$this->attributeClassNaming = $attributeClassNaming;
|
||||
}
|
||||
|
||||
public function createFromPhpDocParserNodeClass(string $nodeClass): Namespace_
|
||||
{
|
||||
if (Strings::contains($nodeClass, '\\Type\\')) {
|
||||
$namespace = Paths::NAMESPACE_TYPE_NODE;
|
||||
} else {
|
||||
$namespace = Paths::NAMESPACE_PHPDOC_NODE;
|
||||
}
|
||||
|
||||
$namespaceBuilder = new NamespaceBuilder($namespace);
|
||||
|
||||
$shortClassName = $this->attributeClassNaming->createAttributeAwareShortClassName($nodeClass);
|
||||
$classBuilder = $this->createClassBuilder($nodeClass, $shortClassName);
|
||||
|
||||
$traitUseBuilder = new TraitUseBuilder(new FullyQualified(AttributeTrait::class));
|
||||
$classBuilder->addStmt($traitUseBuilder);
|
||||
|
||||
$namespaceBuilder->addStmt($classBuilder->getNode());
|
||||
|
||||
return $namespaceBuilder->getNode();
|
||||
}
|
||||
|
||||
private function createClassBuilder(string $nodeClass, string $shortClassName): ClassBuilder
|
||||
{
|
||||
$classBuilder = new ClassBuilder($shortClassName);
|
||||
$classBuilder->makeFinal();
|
||||
$classBuilder->extend(new FullyQualified($nodeClass));
|
||||
$classBuilder->implement(new FullyQualified(AttributeAwareNodeInterface::class));
|
||||
|
||||
return $classBuilder;
|
||||
}
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils\PHPStanAttributeTypeSyncer\NodeFactory;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
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\Core\PhpParser\Builder\ClassBuilder;
|
||||
use Rector\Core\PhpParser\Builder\MethodBuilder;
|
||||
use Rector\Core\PhpParser\Builder\NamespaceBuilder;
|
||||
use Rector\Core\PhpParser\Builder\ParamBuilder;
|
||||
use Rector\Utils\PHPStanAttributeTypeSyncer\ClassNaming\AttributeClassNaming;
|
||||
use Rector\Utils\PHPStanAttributeTypeSyncer\ValueObject\Paths;
|
||||
use ReflectionClass;
|
||||
|
||||
final class AttributeAwareClassFactoryFactory
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const NODE = 'node';
|
||||
|
||||
/**
|
||||
* @var AttributeClassNaming
|
||||
*/
|
||||
private $attributeClassNaming;
|
||||
|
||||
public function __construct(AttributeClassNaming $attributeClassNaming)
|
||||
{
|
||||
$this->attributeClassNaming = $attributeClassNaming;
|
||||
}
|
||||
|
||||
public function createFromPhpDocParserNodeClass(string $nodeClass): Namespace_
|
||||
{
|
||||
if (Strings::contains($nodeClass, '\\Type\\')) {
|
||||
$namespace = Paths::NAMESPACE_TYPE_NODE_FACTORY;
|
||||
} else {
|
||||
$namespace = Paths::NAMESPACE_PHPDOC_NODE_FACTORY;
|
||||
}
|
||||
|
||||
$namespaceBuilder = new NamespaceBuilder($namespace);
|
||||
|
||||
$shortClassName = $this->attributeClassNaming->createAttributeAwareFactoryShortClassName($nodeClass);
|
||||
|
||||
$classBuilder = new ClassBuilder($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);
|
||||
|
||||
$paramBuilder = new ParamBuilder(self::NODE);
|
||||
$paramBuilder->setType(new FullyQualified(Node::class));
|
||||
|
||||
$classMethods[] = $this->createIsMatchClassMethod($nodeClass, $paramBuilder);
|
||||
$classMethods[] = $this->createCreateClassMethod($nodeClass);
|
||||
|
||||
return $classMethods;
|
||||
}
|
||||
|
||||
private function createGetOriginalNodeClass(string $nodeClass): ClassMethod
|
||||
{
|
||||
$methodBuilder = new MethodBuilder('getOriginalNodeClass');
|
||||
$methodBuilder->makePublic();
|
||||
$methodBuilder->setReturnType('string');
|
||||
|
||||
$classConstFetch = $this->createClassReference($nodeClass);
|
||||
$methodBuilder->addStmt(new Return_($classConstFetch));
|
||||
|
||||
return $methodBuilder->getNode();
|
||||
}
|
||||
|
||||
private function createIsMatchClassMethod(string $nodeClass, ParamBuilder $paramBuilder): ClassMethod
|
||||
{
|
||||
$methodBuilder = new MethodBuilder('isMatch');
|
||||
|
||||
$methodBuilder->addParam($paramBuilder);
|
||||
$methodBuilder->makePublic();
|
||||
$methodBuilder->setReturnType('bool');
|
||||
|
||||
$isAFuncCall = $this->createIsAFuncCall($nodeClass);
|
||||
$methodBuilder->addStmt(new Return_($isAFuncCall));
|
||||
|
||||
return $methodBuilder->getNode();
|
||||
}
|
||||
|
||||
private function createCreateClassMethod(string $nodeClass): ClassMethod
|
||||
{
|
||||
$methodBuilder = new MethodBuilder('create');
|
||||
|
||||
$paramBuilder = new ParamBuilder('docContent');
|
||||
$paramBuilder->setType('string');
|
||||
|
||||
$docContentParam = $paramBuilder->getNode();
|
||||
|
||||
$methodBuilder->addParam($paramBuilder);
|
||||
$methodBuilder->addParam($docContentParam);
|
||||
$methodBuilder->makePublic();
|
||||
|
||||
$methodBuilder->setReturnType(new FullyQualified(AttributeAwareNodeInterface::class));
|
||||
|
||||
// add @paramBuilder doc with more precise type
|
||||
$paramDocBlock = sprintf('/**%s * @paramBuilder \\%s%s */', PHP_EOL, $nodeClass, PHP_EOL);
|
||||
$methodBuilder->setDocComment($paramDocBlock);
|
||||
|
||||
$attributeAwareClassName = $this->attributeClassNaming->createAttributeAwareClassName($nodeClass);
|
||||
|
||||
$new = new New_(new FullyQualified($attributeAwareClassName));
|
||||
|
||||
// complete new args
|
||||
$this->completeNewArgs($new, $nodeClass);
|
||||
|
||||
$methodBuilder->addStmt(new Return_($new));
|
||||
|
||||
return $methodBuilder->getNode();
|
||||
}
|
||||
|
||||
private function createClassReference(string $nodeClass): ClassConstFetch
|
||||
{
|
||||
return new ClassConstFetch(new FullyQualified($nodeClass), 'class');
|
||||
}
|
||||
|
||||
private function createIsAFuncCall(string $nodeClass): FuncCall
|
||||
{
|
||||
$variable = new Variable(self::NODE);
|
||||
$constFetch = new ConstFetch(new Name('true'));
|
||||
|
||||
$args = [new Arg($variable), new Arg($this->createClassReference($nodeClass)), new Arg($constFetch)];
|
||||
|
||||
return new FuncCall(new Name('is_a'), $args);
|
||||
}
|
||||
|
||||
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(self::NODE);
|
||||
|
||||
foreach ($constructorReflectionMethod->getParameters() as $reflectionParameter) {
|
||||
$parameterName = $reflectionParameter->getName();
|
||||
|
||||
$new->args[] = new Arg(new PropertyFetch($phpDocParserNodeVariable, $parameterName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?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_TYPE_NODE = 'Rector\AttributeAwarePhpDoc\Ast\Type';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const NAMESPACE_PHPDOC_NODE_FACTORY = 'Rector\AttributeAwarePhpDoc\AttributeAwareNodeFactory\PhpDoc';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const NAMESPACE_TYPE_NODE_FACTORY = 'Rector\AttributeAwarePhpDoc\AttributeAwareNodeFactory\Type';
|
||||
}
|
Loading…
Reference in New Issue
Block a user