rector/rules/CodingStyle/Node/NameImporter.php

181 lines
7.8 KiB
PHP
Raw Normal View History

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;
use PhpParser\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;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
2019-09-28 14:08:08 +02:00
use PhpParser\Node\Stmt\UseUse;
use PHPStan\Reflection\ReflectionProvider;
use Rector\CodingStyle\ClassNameImport\AliasUsesResolver;
use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper;
use Rector\Core\Configuration\Option;
use Rector\Core\ValueObject\Application\File;
use Rector\NodeNameResolver\NodeNameResolver;
2019-09-28 14:08:08 +02:00
use Rector\NodeTypeResolver\Node\AttributeKey;
2020-03-31 19:32:54 +02:00
use Rector\PostRector\Collector\UseNodesToAddCollector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use RectorPrefix20211120\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 \Rector\CodingStyle\ClassNameImport\AliasUsesResolver
2019-09-28 14:08:08 +02:00
*/
private $aliasUsesResolver;
/**
* @var \Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper
2019-09-28 14:08:08 +02:00
*/
private $classNameImportSkipper;
/**
* @var \Rector\NodeNameResolver\NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var \Symplify\PackageBuilder\Parameter\ParameterProvider
*/
private $parameterProvider;
2020-03-31 19:32:54 +02:00
/**
* @var \Rector\StaticTypeMapper\StaticTypeMapper
*/
private $staticTypeMapper;
/**
* @var \Rector\PostRector\Collector\UseNodesToAddCollector
2020-03-31 19:32:54 +02:00
*/
private $useNodesToAddCollector;
/**
* @var \PHPStan\Reflection\ReflectionProvider
*/
private $reflectionProvider;
public function __construct(\Rector\CodingStyle\ClassNameImport\AliasUsesResolver $aliasUsesResolver, \Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper $classNameImportSkipper, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \RectorPrefix20211120\Symplify\PackageBuilder\Parameter\ParameterProvider $parameterProvider, \Rector\StaticTypeMapper\StaticTypeMapper $staticTypeMapper, \Rector\PostRector\Collector\UseNodesToAddCollector $useNodesToAddCollector, \PHPStan\Reflection\ReflectionProvider $reflectionProvider)
{
2019-09-28 14:08:08 +02:00
$this->aliasUsesResolver = $aliasUsesResolver;
$this->classNameImportSkipper = $classNameImportSkipper;
$this->nodeNameResolver = $nodeNameResolver;
$this->parameterProvider = $parameterProvider;
$this->staticTypeMapper = $staticTypeMapper;
2020-03-31 19:32:54 +02:00
$this->useNodesToAddCollector = $useNodesToAddCollector;
$this->reflectionProvider = $reflectionProvider;
2019-09-28 14:08:08 +02:00
}
/**
* @param Use_[] $uses
*/
public function importName(\PhpParser\Node\Name $name, \Rector\Core\ValueObject\Application\File $file, array $uses) : ?\PhpParser\Node\Name
2019-09-28 14:08:08 +02:00
{
2019-11-08 22:29:15 +01:00
if ($this->shouldSkipName($name)) {
return null;
}
if ($this->classNameImportSkipper->isShortNameInUseStatement($name, $uses)) {
return null;
}
2019-09-28 14:08:08 +02:00
$staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($name);
if (!$staticType instanceof \Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType) {
2019-09-28 14:08:08 +02:00
return null;
}
$className = $staticType->getClassName();
// class has \, no need to search in aliases, mark aliasedUses as empty
$this->aliasedUses = \strpos($className, '\\') !== \false ? [] : $this->aliasUsesResolver->resolveFromStmts($uses);
return $this->importNameAndCollectNewUseStatement($file, $name, $staticType, $className);
2019-09-28 14:08:08 +02:00
}
private function shouldSkipName(\PhpParser\Node\Name $name) : bool
2019-09-28 14:08:08 +02:00
{
$virtualNode = $name->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::VIRTUAL_NODE);
if ($virtualNode) {
return \true;
2019-09-28 14:08:08 +02:00
}
2020-02-01 17:04:38 +01:00
// is scalar name?
if (\in_array($name->toLowerString(), ['true', 'false', 'bool'], \true)) {
return \true;
2020-02-01 17:04:38 +01:00
}
// namespace <name>
// use <name>;
2020-02-01 17:04:38 +01:00
if ($this->isNamespaceOrUseImportName($name)) {
return \true;
}
2020-02-01 17:04:38 +01:00
if ($this->isFunctionOrConstantImportWithSingleName($name)) {
return \true;
2020-02-01 17:04:38 +01:00
}
// Importing root namespace classes (like \DateTime) is optional
if (!$this->parameterProvider->provideBoolParameter(\Rector\Core\Configuration\Option::IMPORT_SHORT_CLASSES)) {
$name = $this->nodeNameResolver->getName($name);
if ($name !== null && \substr_count($name, '\\') === 0) {
return \true;
2020-02-01 17:04:38 +01:00
}
}
return \false;
}
private function importNameAndCollectNewUseStatement(\Rector\Core\ValueObject\Application\File $file, \PhpParser\Node\Name $name, \Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType $fullyQualifiedObjectType, string $className) : ?\PhpParser\Node\Name
{
2019-09-28 14:08:08 +02:00
// the same end is already imported → skip
if ($this->classNameImportSkipper->shouldSkipNameForFullyQualifiedObjectType($file, $name, $fullyQualifiedObjectType)) {
2019-09-28 14:08:08 +02:00
return null;
}
if ($this->useNodesToAddCollector->isShortImported($file, $fullyQualifiedObjectType)) {
if ($this->useNodesToAddCollector->isImportShortable($file, $fullyQualifiedObjectType)) {
2019-09-28 14:08:08 +02:00
return $fullyQualifiedObjectType->getShortNameNode();
}
return null;
}
$this->addUseImport($file, $name, $fullyQualifiedObjectType);
if ($this->aliasedUses === []) {
return $fullyQualifiedObjectType->getShortNameNode();
}
2019-09-28 14:08:08 +02:00
// possibly aliased
[Naming] Register RenameForeachValueVariableToMatchExprVariableRector to naming config set (#5696) * [Naming] Register RenameForeachValueVariableToMatchExprVariableRector to naming config set * fix property fetch not from this * [ci-review] Rector Rectify * [ci-review] Rector Rectify * [ci-review] Rector Rectify * fix * [ci-review] Rector Rectify * [ci-review] Rector Rectify * fixture fix * phpstan * [ci-review] Rector Rectify * phpstan * extract to separate method for collect assigns by name * adding InflectorSingularResolver service * skip single prefix and length >= 40 * add failing fixture to skip plural camel case * use regex to get camel cases * implemented singularize camel cased * [ci-review] Rector Rectify * [ci-review] Rector Rectify * [ci-review] Rector Rectify * phpstan * handle singular verb news -> new * [ci-review] Rector Rectify * fixture fix * handle has * [ci-review] Rector Rectify * [ci-review] Rector Rectify * [ci-review] Rector Rectify * phpstan * phpstan * handle left side By * [ci-review] Rector Rectify * re-use resolve call * [ci-review] Rector Rectify * phpstan * [ci-review] Rector Rectify * final touch * final touch * [ci-review] Rector Rectify * [ci-review] Rector Rectify * use previous By in the middle * update $childClassReflection->hasProperty($propertyName) * [ci-review] Rector Rectify * catchKeys * regex fix * fixture * [ci-review] Rector Rectify * try use check array * Revert "try use check array" This reverts commit adb9f767f20ea2544e5ccfc9cfe361ecc929912a. * use files Co-authored-by: kaizen-ci <info@kaizen-ci.org>
2021-03-05 17:55:40 +07:00
foreach ($this->aliasedUses as $aliasedUse) {
if ($className === $aliasedUse) {
2019-09-28 14:08:08 +02:00
return null;
}
}
return $fullyQualifiedObjectType->getShortNameNode();
}
2020-02-01 17:04:38 +01:00
/**
* Skip:
* - namespace name
* - use import name
*/
private function isNamespaceOrUseImportName(\PhpParser\Node\Name $name) : bool
2019-09-28 14:08:08 +02:00
{
$parentNode = $name->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
if ($parentNode instanceof \PhpParser\Node\Stmt\Namespace_) {
return \true;
2019-09-28 14:08:08 +02:00
}
return $parentNode instanceof \PhpParser\Node\Stmt\UseUse;
2019-09-28 14:08:08 +02:00
}
private function isFunctionOrConstantImportWithSingleName(\PhpParser\Node\Name $name) : bool
2019-11-08 14:38:30 +01:00
{
$parentNode = $name->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
$fullName = $name->toString();
$autoImportNames = $this->parameterProvider->provideParameter(\Rector\Core\Configuration\Option::AUTO_IMPORT_NAMES);
if ($autoImportNames && !$parentNode instanceof \PhpParser\Node && \strpos($fullName, '\\') === \false && $this->reflectionProvider->hasFunction(new \PhpParser\Node\Name($fullName), null)) {
return \true;
}
if ($parentNode instanceof \PhpParser\Node\Expr\ConstFetch) {
return \count($name->parts) === 1;
}
if ($parentNode instanceof \PhpParser\Node\Expr\FuncCall) {
return \count($name->parts) === 1;
}
return \false;
2020-02-01 17:04:38 +01:00
}
private function addUseImport(\Rector\Core\ValueObject\Application\File $file, \PhpParser\Node\Name $name, \Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType $fullyQualifiedObjectType) : void
{
if ($this->useNodesToAddCollector->hasImport($file, $name, $fullyQualifiedObjectType)) {
return;
}
$parentNode = $name->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
if ($parentNode instanceof \PhpParser\Node\Expr\FuncCall) {
$this->useNodesToAddCollector->addFunctionUseImport($fullyQualifiedObjectType);
} else {
$this->useNodesToAddCollector->addUseImport($fullyQualifiedObjectType);
}
}
2019-09-28 14:08:08 +02:00
}