2019-10-13 07:59:52 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
2019-09-28 14:08:08 +02:00
|
|
|
|
|
|
|
namespace Rector\CodingStyle\Node;
|
|
|
|
|
2019-10-03 08:53:23 +02:00
|
|
|
use PhpParser\Node\Expr\ConstFetch;
|
2019-09-28 14:08:08 +02:00
|
|
|
use PhpParser\Node\Expr\FuncCall;
|
|
|
|
use PhpParser\Node\Name;
|
2020-03-27 15:59:09 +01:00
|
|
|
use PhpParser\Node\Name\FullyQualified;
|
2019-09-28 14:08:08 +02:00
|
|
|
use PhpParser\Node\Stmt\Namespace_;
|
|
|
|
use PhpParser\Node\Stmt\UseUse;
|
|
|
|
use Rector\CodingStyle\Imports\AliasUsesResolver;
|
|
|
|
use Rector\CodingStyle\Imports\ImportSkipper;
|
2020-02-06 22:48:18 +01:00
|
|
|
use Rector\Core\Configuration\Option;
|
2020-02-09 23:47:00 +01:00
|
|
|
use Rector\NodeNameResolver\NodeNameResolver;
|
2020-03-27 15:59:09 +01:00
|
|
|
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
2019-09-28 14:08:08 +02:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
|
|
|
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
2020-03-31 19:32:54 +02:00
|
|
|
use Rector\PostRector\Collector\UseNodesToAddCollector;
|
2020-06-19 11:11:25 +02:00
|
|
|
use Rector\PSR4\Collector\RenamedClassesCollector;
|
2020-02-10 10:17:05 +01:00
|
|
|
use Rector\StaticTypeMapper\StaticTypeMapper;
|
2019-11-08 22:52:23 +01:00
|
|
|
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
2019-09-28 14:08:08 +02:00
|
|
|
|
|
|
|
final class NameImporter
|
|
|
|
{
|
2020-02-01 17:04:38 +01:00
|
|
|
/**
|
|
|
|
* @var string[]
|
|
|
|
*/
|
|
|
|
private $aliasedUses = [];
|
|
|
|
|
2019-09-28 14:08:08 +02:00
|
|
|
/**
|
|
|
|
* @var StaticTypeMapper
|
|
|
|
*/
|
|
|
|
private $staticTypeMapper;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var AliasUsesResolver
|
|
|
|
*/
|
|
|
|
private $aliasUsesResolver;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ImportSkipper
|
|
|
|
*/
|
|
|
|
private $importSkipper;
|
|
|
|
|
2019-11-08 22:52:23 +01:00
|
|
|
/**
|
2020-02-09 12:31:31 +01:00
|
|
|
* @var NodeNameResolver
|
2019-11-08 22:52:23 +01:00
|
|
|
*/
|
2020-02-09 12:31:31 +01:00
|
|
|
private $nodeNameResolver;
|
2019-11-08 22:52:23 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ParameterProvider
|
|
|
|
*/
|
|
|
|
private $parameterProvider;
|
|
|
|
|
2020-03-31 19:32:54 +02:00
|
|
|
/**
|
|
|
|
* @var UseNodesToAddCollector
|
|
|
|
*/
|
|
|
|
private $useNodesToAddCollector;
|
|
|
|
|
2020-06-19 11:11:25 +02:00
|
|
|
/**
|
|
|
|
* @var RenamedClassesCollector
|
|
|
|
*/
|
|
|
|
private $renamedClassesCollector;
|
|
|
|
|
2019-09-28 14:08:08 +02:00
|
|
|
public function __construct(
|
|
|
|
AliasUsesResolver $aliasUsesResolver,
|
2019-11-08 22:52:23 +01:00
|
|
|
ImportSkipper $importSkipper,
|
2020-02-09 12:31:31 +01:00
|
|
|
NodeNameResolver $nodeNameResolver,
|
2020-03-31 19:32:54 +02:00
|
|
|
ParameterProvider $parameterProvider,
|
2020-07-26 09:49:22 +02:00
|
|
|
RenamedClassesCollector $renamedClassesCollector,
|
|
|
|
StaticTypeMapper $staticTypeMapper,
|
|
|
|
UseNodesToAddCollector $useNodesToAddCollector
|
2019-09-28 14:08:08 +02:00
|
|
|
) {
|
|
|
|
$this->staticTypeMapper = $staticTypeMapper;
|
|
|
|
$this->aliasUsesResolver = $aliasUsesResolver;
|
|
|
|
$this->importSkipper = $importSkipper;
|
2020-02-09 12:31:31 +01:00
|
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
2019-11-08 22:52:23 +01:00
|
|
|
$this->parameterProvider = $parameterProvider;
|
2020-03-31 19:32:54 +02:00
|
|
|
$this->useNodesToAddCollector = $useNodesToAddCollector;
|
2020-06-19 11:11:25 +02:00
|
|
|
$this->renamedClassesCollector = $renamedClassesCollector;
|
2019-09-28 14:08:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function importName(Name $name): ?Name
|
|
|
|
{
|
2019-11-08 22:29:15 +01:00
|
|
|
if ($this->shouldSkipName($name)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-09-28 14:08:08 +02:00
|
|
|
$staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($name);
|
|
|
|
if (! $staticType instanceof FullyQualifiedObjectType) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->aliasedUses = $this->aliasUsesResolver->resolveForNode($name);
|
|
|
|
|
|
|
|
return $this->importNameAndCollectNewUseStatement($name, $staticType);
|
|
|
|
}
|
|
|
|
|
2020-02-01 17:04:38 +01:00
|
|
|
private function shouldSkipName(Name $name): bool
|
2019-09-28 14:08:08 +02:00
|
|
|
{
|
2020-06-19 01:35:11 +02:00
|
|
|
if ($name->getAttribute(AttributeKey::VIRTUAL_NODE)) {
|
2019-09-28 14:08:08 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-01 17:04:38 +01:00
|
|
|
// is scalar name?
|
2020-03-27 15:59:09 +01:00
|
|
|
if (in_array($name->toLowerString(), ['true', 'false', 'bool'], true)) {
|
2020-02-01 17:04:38 +01:00
|
|
|
return true;
|
|
|
|
}
|
2019-09-28 14:08:08 +02:00
|
|
|
|
2020-05-13 09:57:21 +02:00
|
|
|
// namespace <name>
|
|
|
|
// use <name>;
|
2020-02-01 17:04:38 +01:00
|
|
|
if ($this->isNamespaceOrUseImportName($name)) {
|
|
|
|
return true;
|
2019-10-30 10:49:07 +01:00
|
|
|
}
|
|
|
|
|
2020-02-01 17:04:38 +01:00
|
|
|
if ($this->isFunctionOrConstantImportWithSingleName($name)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-27 16:42:58 +01:00
|
|
|
if ($this->isNonExistingClassLikeAndFunctionFullyQualifiedName($name)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-01 17:04:38 +01:00
|
|
|
// Importing root namespace classes (like \DateTime) is optional
|
2020-07-25 18:07:13 +02:00
|
|
|
$importShortClasses = $this->parameterProvider->provideParameter(Option::IMPORT_SHORT_CLASSES);
|
2020-02-01 17:04:38 +01:00
|
|
|
if (! $importShortClasses) {
|
2020-02-09 12:31:31 +01:00
|
|
|
$name = $this->nodeNameResolver->getName($name);
|
2020-02-01 17:04:38 +01:00
|
|
|
if ($name !== null && substr_count($name, '\\') === 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2019-10-30 10:49:07 +01:00
|
|
|
}
|
|
|
|
|
2019-09-28 14:08:08 +02:00
|
|
|
private function importNameAndCollectNewUseStatement(
|
|
|
|
Name $name,
|
|
|
|
FullyQualifiedObjectType $fullyQualifiedObjectType
|
|
|
|
): ?Name {
|
|
|
|
// the same end is already imported → skip
|
2019-11-08 14:38:30 +01:00
|
|
|
if ($this->importSkipper->shouldSkipNameForFullyQualifiedObjectType($name, $fullyQualifiedObjectType)) {
|
2019-09-28 14:08:08 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:32:54 +02:00
|
|
|
if ($this->useNodesToAddCollector->isShortImported($name, $fullyQualifiedObjectType)) {
|
|
|
|
if ($this->useNodesToAddCollector->isImportShortable($name, $fullyQualifiedObjectType)) {
|
2019-09-28 14:08:08 +02:00
|
|
|
return $fullyQualifiedObjectType->getShortNameNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->addUseImport($name, $fullyQualifiedObjectType);
|
|
|
|
|
|
|
|
// possibly aliased
|
|
|
|
foreach ($this->aliasedUses as $aliasUse) {
|
|
|
|
if ($fullyQualifiedObjectType->getClassName() === $aliasUse) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $fullyQualifiedObjectType->getShortNameNode();
|
|
|
|
}
|
|
|
|
|
2020-02-01 17:04:38 +01:00
|
|
|
/**
|
|
|
|
* Skip:
|
|
|
|
* - namespace name
|
|
|
|
* - use import name
|
|
|
|
*/
|
|
|
|
private function isNamespaceOrUseImportName(Name $name): bool
|
2019-09-28 14:08:08 +02:00
|
|
|
{
|
|
|
|
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
|
2020-02-01 17:04:38 +01:00
|
|
|
if ($parentNode instanceof Namespace_) {
|
|
|
|
return true;
|
2019-09-28 14:08:08 +02:00
|
|
|
}
|
2020-02-01 17:04:38 +01:00
|
|
|
|
|
|
|
return $parentNode instanceof UseUse;
|
2019-09-28 14:08:08 +02:00
|
|
|
}
|
2019-11-08 14:38:30 +01:00
|
|
|
|
2020-02-01 17:04:38 +01:00
|
|
|
private function isFunctionOrConstantImportWithSingleName(Name $name): bool
|
2019-11-08 14:38:30 +01:00
|
|
|
{
|
2020-02-01 17:04:38 +01:00
|
|
|
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
|
|
|
|
if (! $parentNode instanceof ConstFetch && ! $parentNode instanceof FuncCall) {
|
|
|
|
return false;
|
2019-11-08 22:52:23 +01:00
|
|
|
}
|
|
|
|
|
2020-02-01 17:04:38 +01:00
|
|
|
return count($name->parts) === 1;
|
|
|
|
}
|
2019-11-08 14:38:30 +01:00
|
|
|
|
2020-03-27 15:59:09 +01:00
|
|
|
private function isNonExistingClassLikeAndFunctionFullyQualifiedName(Name $name): bool
|
|
|
|
{
|
|
|
|
if (! $name instanceof FullyQualified) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-06-19 11:11:25 +02:00
|
|
|
// can be also in to be renamed classes
|
|
|
|
$classOrFunctionName = $name->toString();
|
|
|
|
|
2020-07-19 20:52:42 +02:00
|
|
|
$oldToNewClasses = $this->renamedClassesCollector->getOldToNewClasses();
|
|
|
|
if (in_array($classOrFunctionName, $oldToNewClasses, true)) {
|
2020-06-19 11:11:25 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-27 15:59:09 +01:00
|
|
|
// skip-non existing class-likes and functions
|
2020-06-19 11:11:25 +02:00
|
|
|
if (ClassExistenceStaticHelper::doesClassLikeExist($classOrFunctionName)) {
|
2020-03-27 15:59:09 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-06-19 11:11:25 +02:00
|
|
|
return ! function_exists($classOrFunctionName);
|
2020-03-27 15:59:09 +01:00
|
|
|
}
|
2020-04-26 02:57:47 +02:00
|
|
|
|
|
|
|
private function addUseImport(Name $name, FullyQualifiedObjectType $fullyQualifiedObjectType): void
|
|
|
|
{
|
|
|
|
if ($this->useNodesToAddCollector->hasImport($name, $fullyQualifiedObjectType)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
|
|
|
|
if ($parentNode instanceof FuncCall) {
|
|
|
|
$this->useNodesToAddCollector->addFunctionUseImport($name, $fullyQualifiedObjectType);
|
|
|
|
} else {
|
|
|
|
$this->useNodesToAddCollector->addUseImport($name, $fullyQualifiedObjectType);
|
|
|
|
}
|
|
|
|
}
|
2019-09-28 14:08:08 +02:00
|
|
|
}
|