mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-21 10:32:24 +00:00
add ImportsInClassCollection
This commit is contained in:
parent
85ff969f30
commit
c5fc331857
8
packages/CodingStyle/config/config.yaml
Normal file
8
packages/CodingStyle/config/config.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
public: true
|
||||
|
||||
Rector\CodingStyle\:
|
||||
resource: '../src'
|
||||
exclude: '../src/{Rector/**/*Rector.php}'
|
|
@ -0,0 +1,60 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Imports;
|
||||
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
|
||||
final class ImportsInClassCollection
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $importsInClass = [];
|
||||
|
||||
/**
|
||||
* @var ClassNaming
|
||||
*/
|
||||
private $classNaming;
|
||||
|
||||
public function __construct(ClassNaming $classNaming)
|
||||
{
|
||||
$this->classNaming = $classNaming;
|
||||
}
|
||||
|
||||
public function addImport(string $import): void
|
||||
{
|
||||
$this->importsInClass[] = $import;
|
||||
}
|
||||
|
||||
public function hasImport(string $import): bool
|
||||
{
|
||||
return in_array($import, $this->importsInClass, true);
|
||||
}
|
||||
|
||||
public function canImportBeAdded(string $import): bool
|
||||
{
|
||||
$shortImport = $this->classNaming->getShortName($import);
|
||||
|
||||
foreach ($this->importsInClass as $importsInClass) {
|
||||
$shortImportInClass = $this->classNaming->getShortName($importsInClass);
|
||||
if ($importsInClass !== $import && $shortImportInClass === $shortImport) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->importsInClass = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
return $this->importsInClass;
|
||||
}
|
||||
}
|
13
packages/CodingStyle/src/Naming/ClassNaming.php
Normal file
13
packages/CodingStyle/src/Naming/ClassNaming.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Naming;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
final class ClassNaming
|
||||
{
|
||||
public function getShortName(string $fullyQualifiedName): string
|
||||
{
|
||||
return Strings::after($fullyQualifiedName, '\\', -1) ?: $fullyQualifiedName;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ use PhpParser\Node\Stmt\Class_;
|
|||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\Node\Stmt\UseUse;
|
||||
use Rector\CodingStyle\Imports\ImportsInClassCollection;
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
|
@ -28,11 +30,6 @@ final class ImportFullyQualifiedNamesRector extends AbstractRector
|
|||
*/
|
||||
private $newUseStatements = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $alreadyImportedUses = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
|
@ -43,10 +40,26 @@ final class ImportFullyQualifiedNamesRector extends AbstractRector
|
|||
*/
|
||||
private $docBlockManipulator;
|
||||
|
||||
public function __construct(CallableNodeTraverser $callableNodeTraverser, DocBlockManipulator $docBlockManipulator)
|
||||
{
|
||||
/**
|
||||
* @var ImportsInClassCollection
|
||||
*/
|
||||
private $importsInClassCollection;
|
||||
|
||||
/**
|
||||
* @var ClassNaming
|
||||
*/
|
||||
private $classNaming;
|
||||
|
||||
public function __construct(
|
||||
CallableNodeTraverser $callableNodeTraverser,
|
||||
DocBlockManipulator $docBlockManipulator,
|
||||
ImportsInClassCollection $importsInClassCollection,
|
||||
ClassNaming $classNaming
|
||||
) {
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
$this->importsInClassCollection = $importsInClassCollection;
|
||||
$this->classNaming = $classNaming;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
|
@ -91,8 +104,9 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$this->alreadyImportedUses = [];
|
||||
$this->newUseStatements = [];
|
||||
$this->importsInClassCollection->reset();
|
||||
$this->docBlockManipulator->resetImportedNames();
|
||||
|
||||
/** @var Class_|null $class */
|
||||
$class = $this->betterNodeFinder->findFirstInstanceOf($node, Class_::class);
|
||||
|
@ -110,6 +124,17 @@ CODE_SAMPLE
|
|||
|
||||
private function resolveAlreadyImportedUses(Namespace_ $namespace): void
|
||||
{
|
||||
/** @var Class_ $class */
|
||||
$class = $this->betterNodeFinder->findFirstInstanceOf($namespace->stmts, Class_::class);
|
||||
|
||||
// add class itself
|
||||
$className = $this->getName($class);
|
||||
if ($className === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->importsInClassCollection->addImport($className);
|
||||
|
||||
/** @var Use_[] $uses */
|
||||
$uses = $this->betterNodeFinder->find($namespace->stmts, function (Node $node) {
|
||||
if (! $node instanceof Use_) {
|
||||
|
@ -132,7 +157,7 @@ CODE_SAMPLE
|
|||
$this->aliasedUses[] = $name;
|
||||
}
|
||||
|
||||
$this->alreadyImportedUses[] = $name;
|
||||
$this->importsInClassCollection->addImport($name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +170,7 @@ CODE_SAMPLE
|
|||
return;
|
||||
}
|
||||
|
||||
$this->alreadyImportedUses[] = $className;
|
||||
$this->importsInClassCollection->addImport($className);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,14 +186,14 @@ CODE_SAMPLE
|
|||
|
||||
foreach ($newUseStatements as $newUseStatement) {
|
||||
// already imported in previous cycle
|
||||
if (in_array($newUseStatement, $this->alreadyImportedUses, true)) {
|
||||
if ($this->importsInClassCollection->hasImport($newUseStatement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$useUse = new UseUse(new Name($newUseStatement));
|
||||
$newUses[] = new Use_([$useUse]);
|
||||
|
||||
$this->alreadyImportedUses[] = $newUseStatement;
|
||||
$this->importsInClassCollection->addImport($newUseStatement);
|
||||
}
|
||||
|
||||
$namespace->stmts = array_merge($newUses, $namespace->stmts);
|
||||
|
@ -179,6 +204,7 @@ CODE_SAMPLE
|
|||
*/
|
||||
private function importNamesAndCollectNewUseStatements(Class_ $class): array
|
||||
{
|
||||
// probably anonymous class
|
||||
if ($class->name === null) {
|
||||
return [];
|
||||
}
|
||||
|
@ -213,7 +239,7 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
$shortName = $this->getShortName($fullyQualifiedName);
|
||||
$shortName = $this->classNaming->getShortName($fullyQualifiedName);
|
||||
if (isset($this->newUseStatements[$shortName])) {
|
||||
if ($fullyQualifiedName === $this->newUseStatements[$shortName]) {
|
||||
return new Name($shortName);
|
||||
|
@ -222,7 +248,7 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
if (! in_array($fullyQualifiedName, $this->alreadyImportedUses, true)) {
|
||||
if (! $this->importsInClassCollection->hasImport($fullyQualifiedName)) {
|
||||
$this->newUseStatements[$shortName] = $fullyQualifiedName;
|
||||
}
|
||||
|
||||
|
@ -237,18 +263,13 @@ CODE_SAMPLE
|
|||
|
||||
// for doc blocks
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable([$class], function (Node $node): void {
|
||||
$importedDocUseStatements = $this->docBlockManipulator->importNames($node, $this->alreadyImportedUses);
|
||||
$importedDocUseStatements = $this->docBlockManipulator->importNames($node);
|
||||
$this->newUseStatements = array_merge($this->newUseStatements, $importedDocUseStatements);
|
||||
});
|
||||
|
||||
return $this->newUseStatements;
|
||||
}
|
||||
|
||||
private function getShortName(string $fullyQualifiedName): string
|
||||
{
|
||||
return Strings::after($fullyQualifiedName, '\\', -1) ?: $fullyQualifiedName;
|
||||
}
|
||||
|
||||
// 1. name is fully qualified → import it
|
||||
private function shouldSkipName(string $fullyQualifiedName): bool
|
||||
{
|
||||
|
@ -257,20 +278,13 @@ CODE_SAMPLE
|
|||
return true;
|
||||
}
|
||||
|
||||
$shortName = $this->getShortName($fullyQualifiedName);
|
||||
$shortName = $this->classNaming->getShortName($fullyQualifiedName);
|
||||
|
||||
// nothing to change
|
||||
if ($shortName === $fullyQualifiedName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->alreadyImportedUses as $alreadyImportedUse) {
|
||||
$shortAlreadyImportedUsed = $this->getShortName($alreadyImportedUse);
|
||||
if ($alreadyImportedUse !== $fullyQualifiedName && $shortAlreadyImportedUsed === $shortName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->importsInClassCollection->canImportBeAdded($fullyQualifiedName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,33 @@ use Some\Trait_;
|
|||
final class SameEnd
|
||||
{
|
||||
/**
|
||||
* @param Another\Trait_ $phpStanScopeFactory
|
||||
* @param \Some\Trait_ $firstTrait
|
||||
* @param Another\Trait_ $secondTrait
|
||||
* @param \Some\Trait_ $thirdTrait
|
||||
*/
|
||||
public function __construct(Another\Trait_ $phpStanScopeFactory)
|
||||
public function __construct(\Some\Trait_ $firstTrait, Another\Trait_ $secondTrait, \Some\Trait_ $thirdTrait)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\Fixture;
|
||||
|
||||
use Some\Trait_;
|
||||
|
||||
final class SameEnd
|
||||
{
|
||||
/**
|
||||
* @param Trait_ $firstTrait
|
||||
* @param Another\Trait_ $secondTrait
|
||||
* @param Trait_ $thirdTrait
|
||||
*/
|
||||
public function __construct(Trait_ $firstTrait, Another\Trait_ $secondTrait, Trait_ $thirdTrait)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,97 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Elasticr\Inventory;
|
||||
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Elasticr\Inventory\Stock\Query;
|
||||
namespace Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source;
|
||||
|
||||
final class StockRepository
|
||||
{
|
||||
/** @var \Doctrine\Common\Persistence\ManagerRegistry */
|
||||
private $registry;
|
||||
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
public function filter(Stock\Query $query)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
public function clear(array $stockrooms): void
|
||||
{
|
||||
/** @var \Doctrine\DBAL\Connection $connection */
|
||||
$connection = $this->registry->getConnection();
|
||||
$query = $connection->createQueryBuilder();
|
||||
|
||||
$query->delete('elasticr_stocks')
|
||||
->andWhere('stockroom_id IN (:id)')
|
||||
->setParameter('id', $stockrooms, Connection::PARAM_INT_ARRAY);
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function add(InventoryItems $items): void
|
||||
{
|
||||
/** @var \Doctrine\DBAL\Connection $connection */
|
||||
$connection = $this->registry->getConnection();
|
||||
|
||||
$counter = 0;
|
||||
$inserts = [];
|
||||
|
||||
foreach ($items->items() as $item) {
|
||||
foreach ($item->stocks() as $stock) {
|
||||
$index = $counter / 1000;
|
||||
|
||||
$inserts[$index]['values'][] = '(:uuid'.$counter.', :stockroom'.$counter.', :quantity'.$counter.')';
|
||||
|
||||
$inserts[$index]['parameters'][':uuid'.$counter] = $item->uuid();
|
||||
$inserts[$index]['parameters'][':stockroom'.$counter] = $stock->stockroom()->id();
|
||||
$inserts[$index]['parameters'][':quantity'.$counter] = $stock->quantity();
|
||||
|
||||
++$counter;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($inserts as $insert) {
|
||||
$query = 'INSERT INTO elasticr_stocks (uuid, stockroom_id, quantity) VALUES'.implode(',', $insert['values']);
|
||||
|
||||
$connection->executeUpdate($query, $insert['parameters']);
|
||||
}
|
||||
}
|
||||
|
||||
public function filter(Stock\Query $query): InventoryItems
|
||||
{
|
||||
if (!($query instanceof Stock\Querying\Query)) {
|
||||
throw new \InvalidArgumentException('Wrong query provided');
|
||||
}
|
||||
/** @var Stock\Query $query */
|
||||
$query = 5;
|
||||
|
||||
/** @var Querying\Query $query */
|
||||
$builder = $this->createBuilder();
|
||||
$query->build($builder);
|
||||
|
||||
/** @var \Elasticr\Inventory\Doctrine\Stock[] $stocks */
|
||||
$stocks = $builder->getQuery()
|
||||
->useQueryCache(true)
|
||||
->useResultCache(true)
|
||||
->getResult();
|
||||
|
||||
$inventoryItems = new InventoryItems();
|
||||
|
||||
foreach ($stocks as $stock) {
|
||||
$inventoryItems->add($stock->uuid->toString(), $stock->stockroom, $stock->quantity);
|
||||
}
|
||||
|
||||
return $inventoryItems;
|
||||
}
|
||||
|
||||
private function createBuilder(): QueryBuilder
|
||||
{
|
||||
/** @var \Doctrine\ORM\EntityManager $manager */
|
||||
$manager = $this->registry->getManager();
|
||||
|
||||
return $manager->createQueryBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Stock\Query;
|
||||
final class StockRepository
|
||||
{
|
||||
public function filter(Query $query)
|
||||
{
|
||||
/** @var Query $query */
|
||||
$query = 5;
|
||||
|
||||
/** @var Querying\Query $query */
|
||||
$builder = $this->createBuilder();
|
||||
$query->build($builder);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Querying;
|
||||
|
||||
final class Query
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\Source\Stock;
|
||||
|
||||
final class Query
|
||||
{
|
||||
|
||||
}
|
|
@ -32,6 +32,7 @@ use Rector\BetterPhpDocParser\NodeDecorator\StringsTypePhpDocNodeDecorator;
|
|||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter;
|
||||
use Rector\CodingStyle\Imports\ImportsInClassCollection;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Exception\MissingTagException;
|
||||
use Rector\NodeTypeResolver\Php\ParamTypeInfo;
|
||||
|
@ -76,13 +77,19 @@ final class DocBlockManipulator
|
|||
*/
|
||||
private $importedNames = [];
|
||||
|
||||
/**
|
||||
* @var ImportsInClassCollection
|
||||
*/
|
||||
private $importsInClassCollection;
|
||||
|
||||
public function __construct(
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
PhpDocInfoPrinter $phpDocInfoPrinter,
|
||||
TypeAnalyzer $typeAnalyzer,
|
||||
AttributeAwareNodeFactory $attributeAwareNodeFactory,
|
||||
StringsTypePhpDocNodeDecorator $stringsTypePhpDocNodeDecorator,
|
||||
NodeTraverser $nodeTraverser
|
||||
NodeTraverser $nodeTraverser,
|
||||
ImportsInClassCollection $importsInClassCollection
|
||||
) {
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->phpDocInfoPrinter = $phpDocInfoPrinter;
|
||||
|
@ -90,6 +97,7 @@ final class DocBlockManipulator
|
|||
$this->attributeAwareNodeFactory = $attributeAwareNodeFactory;
|
||||
$this->stringsTypePhpDocNodeDecorator = $stringsTypePhpDocNodeDecorator;
|
||||
$this->nodeTraverser = $nodeTraverser;
|
||||
$this->importsInClassCollection = $importsInClassCollection;
|
||||
}
|
||||
|
||||
public function hasTag(Node $node, string $name): bool
|
||||
|
@ -391,27 +399,23 @@ final class DocBlockManipulator
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string[] $alreadyImportedUses
|
||||
* @return string[]
|
||||
*/
|
||||
public function importNames(Node $node, array $alreadyImportedUses): array
|
||||
public function importNames(Node $node): array
|
||||
{
|
||||
if ($node->getDocComment() === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->importedNames = [];
|
||||
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
$phpDocNode = $phpDocInfo->getPhpDocNode();
|
||||
|
||||
$this->nodeTraverser->traverseWithCallable($phpDocNode, function (AttributeAwareNodeInterface $node) use (
|
||||
$alreadyImportedUses
|
||||
) {
|
||||
$this->nodeTraverser->traverseWithCallable($phpDocNode, function (AttributeAwareNodeInterface $node) {
|
||||
if (! $node instanceof IdentifierTypeNode) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
// is class without namespaced name → skip
|
||||
$name = ltrim($node->name, '\\');
|
||||
if (! Strings::contains($name, '\\')) {
|
||||
return $node;
|
||||
|
@ -420,7 +424,7 @@ final class DocBlockManipulator
|
|||
$fullyQualifiedName = $this->getFullyQualifiedName($node);
|
||||
$shortName = $this->getShortName($name);
|
||||
|
||||
return $this->processFqnNameImport($node, $alreadyImportedUses, $shortName, $fullyQualifiedName);
|
||||
return $this->processFqnNameImport($node, $shortName, $fullyQualifiedName);
|
||||
});
|
||||
|
||||
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
|
||||
|
@ -468,6 +472,11 @@ final class DocBlockManipulator
|
|||
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
|
||||
}
|
||||
|
||||
public function resetImportedNames(): void
|
||||
{
|
||||
$this->importedNames = [];
|
||||
}
|
||||
|
||||
private function addTypeSpecificTag(Node $node, string $name, string $type): void
|
||||
{
|
||||
// there might be no phpdoc at all
|
||||
|
@ -607,15 +616,14 @@ final class DocBlockManipulator
|
|||
|
||||
/**
|
||||
* @param AttributeAwareIdentifierTypeNode $attributeAwareNode
|
||||
* @param string[] $alreadyImportedUses
|
||||
*/
|
||||
private function processFqnNameImport(
|
||||
AttributeAwareNodeInterface $attributeAwareNode,
|
||||
array $alreadyImportedUses,
|
||||
string $shortName,
|
||||
string $fullyQualifiedName
|
||||
): AttributeAwareNodeInterface {
|
||||
$alreadyImportedUses = array_merge($this->importedNames, $alreadyImportedUses);
|
||||
$alreadyImportedUses = array_merge($this->importedNames, $this->importsInClassCollection->get());
|
||||
$alreadyImportedUses = array_unique($alreadyImportedUses);
|
||||
|
||||
foreach ($alreadyImportedUses as $alreadyImportedUse) {
|
||||
$shortAlreadyImportedUsed = $this->getShortName($alreadyImportedUse);
|
||||
|
|
Loading…
Reference in New Issue
Block a user