2019-05-19 23:44:38 +02:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace Rector\CodingStyle\Application;
|
|
|
|
|
|
|
|
use Nette\Utils\Strings;
|
|
|
|
use PhpParser\Node\Stmt;
|
|
|
|
use PhpParser\Node\Stmt\Namespace_;
|
|
|
|
use PhpParser\Node\Stmt\Use_;
|
|
|
|
use Rector\CodingStyle\Imports\UsedImportsResolver;
|
2019-09-06 12:30:58 +02:00
|
|
|
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
2019-05-19 23:44:38 +02:00
|
|
|
|
|
|
|
final class UseImportsAdder
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var UsedImportsResolver
|
|
|
|
*/
|
|
|
|
private $usedImportsResolver;
|
|
|
|
|
|
|
|
public function __construct(UsedImportsResolver $usedImportsResolver)
|
|
|
|
{
|
|
|
|
$this->usedImportsResolver = $usedImportsResolver;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Stmt[] $stmts
|
2019-09-06 12:30:58 +02:00
|
|
|
* @param FullyQualifiedObjectType[] $useImportTypes
|
|
|
|
* @param FullyQualifiedObjectType[] $functionUseImportTypes
|
2019-05-19 23:44:38 +02:00
|
|
|
* @return Stmt[]
|
|
|
|
*/
|
2019-09-06 12:30:58 +02:00
|
|
|
public function addImportsToStmts(array $stmts, array $useImportTypes, array $functionUseImportTypes): array
|
2019-05-19 23:44:38 +02:00
|
|
|
{
|
2019-09-06 12:30:58 +02:00
|
|
|
$existingUseImportTypes = $this->usedImportsResolver->resolveForStmts($stmts);
|
2019-05-19 23:44:38 +02:00
|
|
|
$existingFunctionUseImports = $this->usedImportsResolver->resolveFunctionImportsForStmts($stmts);
|
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
$useImportTypes = $this->diffFullyQualifiedObjectTypes($useImportTypes, $existingUseImportTypes);
|
|
|
|
$functionUseImportTypes = $this->diffFullyQualifiedObjectTypes(
|
|
|
|
$functionUseImportTypes,
|
|
|
|
$existingFunctionUseImports
|
|
|
|
);
|
2019-05-19 23:44:38 +02:00
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
$newUses = $this->createUses($useImportTypes, $functionUseImportTypes, null);
|
2019-05-19 23:44:38 +02:00
|
|
|
|
|
|
|
return array_merge($newUses, $stmts);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-09-06 12:30:58 +02:00
|
|
|
* @param FullyQualifiedObjectType[] $useImportTypes
|
|
|
|
* @param FullyQualifiedObjectType[] $functionUseImportTypes
|
2019-05-19 23:44:38 +02:00
|
|
|
*/
|
2019-09-06 12:30:58 +02:00
|
|
|
public function addImportsToNamespace(
|
|
|
|
Namespace_ $namespace,
|
|
|
|
array $useImportTypes,
|
|
|
|
array $functionUseImportTypes
|
|
|
|
): void {
|
2019-05-19 23:44:38 +02:00
|
|
|
$namespaceName = $this->getNamespaceName($namespace);
|
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
$existingUseImportTypes = $this->usedImportsResolver->resolveForNode($namespace);
|
|
|
|
$existingFunctionUseImportTypes = $this->usedImportsResolver->resolveFunctionImportsForStmts($namespace->stmts);
|
2019-05-19 23:44:38 +02:00
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
$useImportTypes = $this->diffFullyQualifiedObjectTypes($useImportTypes, $existingUseImportTypes);
|
|
|
|
$functionUseImportTypes = $this->diffFullyQualifiedObjectTypes(
|
|
|
|
$functionUseImportTypes,
|
|
|
|
$existingFunctionUseImportTypes
|
|
|
|
);
|
2019-05-19 23:44:38 +02:00
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
$newUses = $this->createUses($useImportTypes, $functionUseImportTypes, $namespaceName);
|
2019-05-19 23:44:38 +02:00
|
|
|
$namespace->stmts = array_merge($newUses, $namespace->stmts);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getNamespaceName(Namespace_ $namespace): ?string
|
|
|
|
{
|
|
|
|
if ($namespace->name === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $namespace->name->toString();
|
|
|
|
}
|
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
private function isCurrentNamespace(
|
|
|
|
string $namespaceName,
|
|
|
|
FullyQualifiedObjectType $fullyQualifiedObjectType
|
|
|
|
): bool {
|
2019-05-19 23:44:38 +02:00
|
|
|
if ($namespaceName === null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
$afterCurrentNamespace = Strings::after($fullyQualifiedObjectType->getClassName(), $namespaceName . '\\');
|
2019-05-19 23:44:38 +02:00
|
|
|
if (! $afterCurrentNamespace) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ! Strings::contains($afterCurrentNamespace, '\\');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-09-06 12:30:58 +02:00
|
|
|
* @param FullyQualifiedObjectType[] $useImportTypes
|
|
|
|
* @param FullyQualifiedObjectType[] $functionUseImportTypes
|
2019-05-19 23:44:38 +02:00
|
|
|
* @return Use_[]
|
|
|
|
*/
|
2019-09-06 12:30:58 +02:00
|
|
|
private function createUses(array $useImportTypes, array $functionUseImportTypes, ?string $namespaceName): array
|
2019-05-19 23:44:38 +02:00
|
|
|
{
|
2019-09-06 12:30:58 +02:00
|
|
|
if ($namespaceName === null) {
|
2019-09-10 08:21:20 +02:00
|
|
|
// not supported yet
|
2019-09-06 12:30:58 +02:00
|
|
|
return [];
|
|
|
|
}
|
2019-05-19 23:44:38 +02:00
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
$newUses = [];
|
|
|
|
foreach ($useImportTypes as $useImportType) {
|
|
|
|
if ($this->isCurrentNamespace($namespaceName, $useImportType)) {
|
2019-05-19 23:44:38 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// already imported in previous cycle
|
2019-09-06 12:30:58 +02:00
|
|
|
$newUses[] = $useImportType->getUseNode();
|
2019-05-19 23:44:38 +02:00
|
|
|
}
|
|
|
|
|
2019-09-06 12:30:58 +02:00
|
|
|
foreach ($functionUseImportTypes as $functionUseImportType) {
|
|
|
|
if ($this->isCurrentNamespace($namespaceName, $functionUseImportType)) {
|
2019-05-19 23:44:38 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// already imported in previous cycle
|
2019-09-06 12:30:58 +02:00
|
|
|
$newUses[] = $functionUseImportType->getFunctionUseNode();
|
2019-05-19 23:44:38 +02:00
|
|
|
}
|
2019-09-06 12:30:58 +02:00
|
|
|
|
2019-05-19 23:44:38 +02:00
|
|
|
return $newUses;
|
|
|
|
}
|
2019-09-06 12:30:58 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param FullyQualifiedObjectType[] $mainTypes
|
|
|
|
* @param FullyQualifiedObjectType[] $typesToRemove
|
|
|
|
* @return FullyQualifiedObjectType[]
|
|
|
|
*/
|
|
|
|
private function diffFullyQualifiedObjectTypes(array $mainTypes, array $typesToRemove): array
|
|
|
|
{
|
|
|
|
foreach ($mainTypes as $key => $mainType) {
|
|
|
|
foreach ($typesToRemove as $typeToRemove) {
|
|
|
|
if ($mainType->equals($typeToRemove)) {
|
|
|
|
unset($mainTypes[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_values($mainTypes);
|
|
|
|
}
|
2019-05-19 23:44:38 +02:00
|
|
|
}
|