usedImportsResolver = $usedImportsResolver; $this->typeFactory = $typeFactory; } /** * @param Stmt[] $stmts * @param array $useImportTypes * @param array $constantUseImportTypes * @param array $functionUseImportTypes * @return Stmt[] */ public function addImportsToStmts(FileWithoutNamespace $fileWithoutNamespace, array $stmts, array $useImportTypes, array $constantUseImportTypes, array $functionUseImportTypes) : array { $usedImports = $this->usedImportsResolver->resolveForStmts($stmts); $existingUseImportTypes = $usedImports->getUseImports(); $existingConstantUseImports = $usedImports->getConstantImports(); $existingFunctionUseImports = $usedImports->getFunctionImports(); $useImportTypes = $this->diffFullyQualifiedObjectTypes($useImportTypes, $existingUseImportTypes); $constantUseImportTypes = $this->diffFullyQualifiedObjectTypes($constantUseImportTypes, $existingConstantUseImports); $functionUseImportTypes = $this->diffFullyQualifiedObjectTypes($functionUseImportTypes, $existingFunctionUseImports); $newUses = $this->createUses($useImportTypes, $constantUseImportTypes, $functionUseImportTypes, null); if ($newUses === []) { return [$fileWithoutNamespace]; } // place after declare strict_types foreach ($stmts as $key => $stmt) { if ($stmt instanceof Declare_) { if (isset($stmts[$key + 1]) && $stmts[$key + 1] instanceof Use_) { $nodesToAdd = $newUses; } else { // add extra space, if there are no new use imports to be added $nodesToAdd = \array_merge([new Nop()], $newUses); } $this->mirrorUseComments($stmts, $newUses, $key + 1); \array_splice($stmts, $key + 1, 0, $nodesToAdd); $fileWithoutNamespace->stmts = $stmts; $fileWithoutNamespace->stmts = \array_values($fileWithoutNamespace->stmts); return [$fileWithoutNamespace]; } } $this->mirrorUseComments($stmts, $newUses); // make use stmts first $fileWithoutNamespace->stmts = \array_merge($newUses, $stmts); $fileWithoutNamespace->stmts = \array_values($fileWithoutNamespace->stmts); return [$fileWithoutNamespace]; } /** * @param FullyQualifiedObjectType[] $useImportTypes * @param FullyQualifiedObjectType[] $constantUseImportTypes * @param FullyQualifiedObjectType[] $functionUseImportTypes */ public function addImportsToNamespace(Namespace_ $namespace, array $useImportTypes, array $constantUseImportTypes, array $functionUseImportTypes) : void { $namespaceName = $this->getNamespaceName($namespace); $existingUsedImports = $this->usedImportsResolver->resolveForStmts($namespace->stmts); $existingUseImportTypes = $existingUsedImports->getUseImports(); $existingConstantUseImportTypes = $existingUsedImports->getConstantImports(); $existingFunctionUseImportTypes = $existingUsedImports->getFunctionImports(); $existingUseImportTypes = $this->typeFactory->uniquateTypes($existingUseImportTypes); $useImportTypes = $this->diffFullyQualifiedObjectTypes($useImportTypes, $existingUseImportTypes); $constantUseImportTypes = $this->diffFullyQualifiedObjectTypes($constantUseImportTypes, $existingConstantUseImportTypes); $functionUseImportTypes = $this->diffFullyQualifiedObjectTypes($functionUseImportTypes, $existingFunctionUseImportTypes); $newUses = $this->createUses($useImportTypes, $constantUseImportTypes, $functionUseImportTypes, $namespaceName); if ($newUses === []) { return; } $this->mirrorUseComments($namespace->stmts, $newUses); $namespace->stmts = \array_merge($newUses, $namespace->stmts); $namespace->stmts = \array_values($namespace->stmts); } /** * @param Stmt[] $stmts * @param Use_[] $newUses */ private function mirrorUseComments(array $stmts, array $newUses, int $indexStmt = 0) : void { if ($stmts === []) { return; } if (isset($stmts[$indexStmt]) && $stmts[$indexStmt] instanceof Use_) { $comments = (array) $stmts[$indexStmt]->getAttribute(AttributeKey::COMMENTS); if ($comments !== []) { $newUses[0]->setAttribute(AttributeKey::COMMENTS, $stmts[$indexStmt]->getAttribute(AttributeKey::COMMENTS)); $stmts[$indexStmt]->setAttribute(AttributeKey::COMMENTS, null); } } } /** * @param array $mainTypes * @param array $typesToRemove * @return array */ 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); } /** * @param array $useImportTypes * @param array $constantUseImportTypes * @param array $functionUseImportTypes * @return Use_[] */ private function createUses(array $useImportTypes, array $constantUseImportTypes, array $functionUseImportTypes, ?string $namespaceName) : array { $newUses = []; /** @var array> $importsMapping */ $importsMapping = [Use_::TYPE_NORMAL => $useImportTypes, Use_::TYPE_CONSTANT => $constantUseImportTypes, Use_::TYPE_FUNCTION => $functionUseImportTypes]; foreach ($importsMapping as $type => $importTypes) { /** @var AliasedObjectType|FullyQualifiedObjectType $importType */ foreach ($importTypes as $importType) { if ($namespaceName !== null && $this->isCurrentNamespace($namespaceName, $importType)) { continue; } // already imported in previous cycle $newUses[] = $importType->getUseNode($type); } } return $newUses; } private function getNamespaceName(Namespace_ $namespace) : ?string { if (!$namespace->name instanceof Name) { return null; } return $namespace->name->toString(); } private function isCurrentNamespace(string $namespaceName, ObjectType $objectType) : bool { $afterCurrentNamespace = Strings::after($objectType->getClassName(), $namespaceName . '\\'); if ($afterCurrentNamespace === null) { return \false; } return \strpos($afterCurrentNamespace, '\\') === \false; } }