src/CompletionProvider.php
private function suggestVariablesAtNode(Node $node, string $namePrefix = ''): array
    {
        $vars = [];

        // Find variables in the node itself
        // When getting completion in the middle of a function, $node will be the function node
        // so we need to search it
        foreach ($this->findVariableDefinitionsInNode($node, $namePrefix) as $var) {
            // Only use the first definition
            if (!isset($vars[$var->name])) {
                $vars[$var->name] = $var;
            }
        }

        // Walk the AST upwards until a scope boundary is met
        $level = $node;
        while ($level && !($level instanceof PhpParser\FunctionLike)) {
            // Walk siblings before the node
            $sibling = $level;
            while ($sibling = $sibling->getPreviousSibling()) {
                // Collect all variables inside the sibling node
                foreach ($this->findVariableDefinitionsInNode($sibling, $namePrefix) as $var) {
                    $vars[$var->getName()] = $var;
                }
            }
            $level = $level->parent;
        }

        // If the traversal ended because a function was met,
        // also add its parameters and closure uses to the result list
        if ($level && $level instanceof PhpParser\FunctionLike && $level->parameters !== null) {
            foreach ($level->parameters->getValues() as $param) {
                $paramName = $param->getName();
                if (empty($namePrefix) || strpos($paramName, $namePrefix) !== false) {
                    $vars[$paramName] = $param;
                }
            }

            if ($level instanceof Node\Expression\AnonymousFunctionCreationExpression
                && $level->anonymousFunctionUseClause !== null
                && $level->anonymousFunctionUseClause->useVariableNameList !== null) {
                foreach ($level->anonymousFunctionUseClause->useVariableNameList->getValues() as $use) {
                    $useName = $use->getName();
                    if (empty($namePrefix) || strpos($useName, $namePrefix) !== false) {
                        $vars[$useName] = $use;
                    }
                }
            }
        }

        return array_values($vars);
    }
private function suggestVariablesAtNode(Node $node, string $namePrefix = ''): array
    {
        $vars = [];

        // Find variables in the node itself
        // When getting completion in the middle of a function, $node will be the function node
        // so we need to search it
        foreach ($this->findVariableDefinitionsInNode($node, $namePrefix) as $var) {
            // Only use the first definition
            if (!isset($vars[$var->name])) {
                $vars[$var->name] = $var;
            }
        }

        // Walk the AST upwards until a scope boundary is met
        $level = $node;
        while ($level && !($level instanceof PhpParser\FunctionLike)) {
            // Walk siblings before the node
            $sibling = $level;
            while ($sibling = $sibling->getPreviousSibling()) {
                // Collect all variables inside the sibling node
                foreach ($this->findVariableDefinitionsInNode($sibling, $namePrefix) as $var) {
                    $vars[$var->getName()] = $var;
                }
            }
            $level = $level->parent;
        }

        // If the traversal ended because a function was met,
        // also add its parameters and closure uses to the result list
        if ($level && $level instanceof PhpParser\FunctionLike && $level->parameters !== null) {
            foreach ($level->parameters->getValues() as $param) {
                $paramName = $param->getName();
                if (empty($namePrefix) || strpos($paramName, $namePrefix) !== false) {
                    $vars[$paramName] = $param;
                }
            }

            if ($level instanceof Node\Expression\AnonymousFunctionCreationExpression
                && $level->anonymousFunctionUseClause !== null
                && $level->anonymousFunctionUseClause->useVariableNameList !== null) {
                foreach ($level->anonymousFunctionUseClause->useVariableNameList->getValues() as $use) {
                    $useName = $use->getName();
                    if (empty($namePrefix) || strpos($useName, $namePrefix) !== false) {
                        $vars[$useName] = $use;
                    }
                }
            }
        }

        return array_values($vars);
    }
private function suggestVariablesAtNode ( Node $ node , string $ namePrefix = '' ) : array { $ vars = [ ] ; // Find variables in the node itself // When getting completion in the middle of a function, $node will be the function node // so we need to search it foreach ( $ this -> findVariableDefinitionsInNode ( $ node , $ namePrefix ) as $ var ) { // Only use the first definition if ( ! isset ( $ vars [ $ var -> name ] ) ) { $ vars [ $ var -> name ] = $ var ; } } // Walk the AST upwards until a scope boundary is met $ level = $ node ; while ( $ level && ! ( $ level instanceof PhpParser \ FunctionLike ) ) { // Walk siblings before the node $ sibling = $ level ; while ( $ sibling = $ sibling -> getPreviousSibling ( ) ) { // Collect all variables inside the sibling node foreach ( $ this -> findVariableDefinitionsInNode ( $ sibling , $ namePrefix ) as $ var ) { $ vars [ $ var -> getName ( ) ] = $ var ; } } $ level = $ level -> parent ; } // If the traversal ended because a function was met, // also add its parameters and closure uses to the result list if ( $ level && $ level instanceof PhpParser \ FunctionLike && $ level -> parameters !== null ) { foreach ( $ level -> parameters -> getValues ( ) as $ param ) { $ paramName = $ param -> getName ( ) ; if ( empty ( $ namePrefix ) || strpos ( $ paramName , $ namePrefix ) !== false ) { $ vars [ $ paramName ] = $ param ; } } if ( $ level instanceof Node \ Expression \ AnonymousFunctionCreationExpression && $ level -> anonymousFunctionUseClause !== null && $ level -> anonymousFunctionUseClause -> useVariableNameList !== null ) { foreach ( $ level -> anonymousFunctionUseClause -> useVariableNameList -> getValues ( ) as $ use ) { $ useName = $ use -> getName ( ) ; if ( empty ( $ namePrefix ) || strpos ( $ useName , $ namePrefix ) !== false ) { $ vars [ $ useName ] = $ use ; } } } } return array_values ( $ vars ) ; }
Will walk the AST upwards until a function-like node is met
and at each level walk all previous siblings and their children to search for definitions
of that variable

@param Node $node
@param string $namePrefix Prefix to filter
@return array <Node\Expr\Variable|Node\Param|Node\Expr\ClosureUse>
Will walk the AST upwards until a function - like node is met and at each level walk all previous siblings and their children to search for definitions of that variable