public function getImportTablesForCurrentScope()

in src/Node.php [486:579]


    public function getImportTablesForCurrentScope() {
        $namespaceDefinition = $this->getNamespaceDefinition();

        // Use declarations can exist in either the global scope, or inside namespace declarations.
        // http://php.net/manual/en/language.namespaces.importing.php#language.namespaces.importing.scope
        //
        // The only code allowed before a namespace declaration is a declare statement, and sub-namespaces are
        // additionally unaffected by by import rules of higher-level namespaces. Therefore, we can make the assumption
        // that we need not travel up the spine any further once we've found the current namespace.
        // http://php.net/manual/en/language.namespaces.definition.php
        if ($namespaceDefinition instanceof NamespaceDefinition) {
            $topLevelNamespaceStatements = $namespaceDefinition->compoundStatementOrSemicolon instanceof Token
                ? $namespaceDefinition->parent->statementList // we need to start from the namespace definition.
                : $namespaceDefinition->compoundStatementOrSemicolon->statements;
            $namespaceFullStart = $namespaceDefinition->getFullStartPosition();
        } else {
            $topLevelNamespaceStatements = $this->getRoot()->statementList;
            $namespaceFullStart = 0;
        }

        $nodeFullStart = $this->getFullStartPosition();

        // TODO optimize performance
        // Currently we rebuild the import tables on every call (and therefore every name resolution operation)
        // It is likely that a consumer will attempt many consecutive name resolution requests within the same file.
        // Therefore, we can consider optimizing on the basis of the "most recently used" import table set.
        // The idea: Keep a single set of import tables cached based on a unique root node id, and invalidate
        // cache whenever we attempt to resolve a qualified name with a different root node.
        //
        // In order to make this work, it will probably make sense to change the way we parse namespace definitions.
        // https://github.com/Microsoft/tolerant-php-parser/issues/81
        //
        // Currently the namespace definition only includes a compound statement or semicolon token as one if it's children.
        // Instead, we should move to a model where we parse future statements as a child rather than as a separate
        // statement. This would enable us to retrieve all the information we would need to find the fully qualified
        // name by simply traveling up the spine to find the first ancestor of type NamespaceDefinition.
        $namespaceImportTable = $functionImportTable = $constImportTable = [];
        $contents = $this->getFileContents();

        foreach ($topLevelNamespaceStatements as $useDeclaration) {
            if ($useDeclaration->getFullStartPosition() <= $namespaceFullStart) {
                continue;
            }
            if ($useDeclaration->getFullStartPosition() > $nodeFullStart) {
                break;
            } elseif (!($useDeclaration instanceof NamespaceUseDeclaration)) {
                continue;
            }

            // TODO fix getValues
            foreach ((isset($useDeclaration->useClauses) ? $useDeclaration->useClauses->getValues() : []) as $useClause) {
                $namespaceNamePartsPrefix = $useClause->namespaceName !== null ? $useClause->namespaceName->nameParts : [];

                if ($useClause->groupClauses !== null && $useClause instanceof NamespaceUseClause) {
                    // use A\B\C\{D\E};                 namespace import: ["E" => [A,B,C,D,E]]
                    // use A\B\C\{D\E as F};            namespace import: ["F" => [A,B,C,D,E]]
                    // use function A\B\C\{A, B}        function import: ["A" => [A,B,C,A], "B" => [A,B,C]]
                    // use function A\B\C\{const A}     const import: ["A" => [A,B,C,A]]
                    foreach ($useClause->groupClauses->children as $groupClause) {
                        if (!($groupClause instanceof NamespaceUseGroupClause)) {
                            continue;
                        }
                        $namespaceNameParts = \array_merge($namespaceNamePartsPrefix, $groupClause->namespaceName->nameParts);
                        $functionOrConst = $groupClause->functionOrConst ?? $useDeclaration->functionOrConst;
                        $alias = $groupClause->namespaceAliasingClause === null
                            ? $groupClause->namespaceName->getLastNamePart()->getText($contents)
                            : $groupClause->namespaceAliasingClause->name->getText($contents);

                        $this->addToImportTable(
                            $alias, $functionOrConst, $namespaceNameParts, $contents,
                            $namespaceImportTable, $functionImportTable, $constImportTable
                        );
                    }
                } else {
                    // use A\B\C;               namespace import: ["C" => [A,B,C]]
                    // use A\B\C as D;          namespace import: ["D" => [A,B,C]]
                    // use function A\B\C as D  function import: ["D" => [A,B,C]]
                    // use A\B, C\D;            namespace import: ["B" => [A,B], "D" => [C,D]]
                    $alias = $useClause->namespaceAliasingClause === null
                        ? $useClause->namespaceName->getLastNamePart()->getText($contents)
                        : $useClause->namespaceAliasingClause->name->getText($contents);
                    $functionOrConst = $useDeclaration->functionOrConst;
                    $namespaceNameParts = $namespaceNamePartsPrefix;

                    $this->addToImportTable(
                        $alias, $functionOrConst, $namespaceNameParts, $contents,
                        $namespaceImportTable, $functionImportTable, $constImportTable
                    );
                }
            }
        }

        return [$namespaceImportTable, $functionImportTable, $constImportTable];
    }