Phan\Analysis\PreOrderAnalysisVisitor::visitClosure PHP Method

visitClosure() public method

Visit a node with kind \ast\AST_CLOSURE
public visitClosure ( ast\Node\Decl $node ) : Context
$node ast\Node\Decl A node to parse
return Phan\Language\Context A new or an unchanged context resulting from parsing the node
    public function visitClosure(Decl $node) : Context
    {
        $closure_fqsen = FullyQualifiedFunctionName::fromClosureInContext($this->context->withLineNumberStart($node->lineno ?? 0));
        $func = Func::fromNode($this->context, $this->code_base, $node, $closure_fqsen);
        // If we have a 'this' variable in our current scope,
        // pass it down into the closure
        if ($this->context->getScope()->hasVariableWithName('this')) {
            $func->getInternalScope()->addVariable($this->context->getScope()->getVariableByName('this'));
        }
        // Make the closure reachable by FQSEN from anywhere
        $this->code_base->addFunction($func);
        if (!empty($node->children['uses']) && $node->children['uses']->kind == \ast\AST_CLOSURE_USES) {
            $uses = $node->children['uses'];
            foreach ($uses->children as $use) {
                if ($use->kind != \ast\AST_CLOSURE_VAR) {
                    $this->emitIssue(Issue::VariableUseClause, $node->lineno ?? 0);
                    continue;
                }
                $variable_name = (new ContextNode($this->code_base, $this->context, $use->children['name']))->getVariableName();
                if (empty($variable_name)) {
                    continue;
                }
                $variable = null;
                // Check to see if the variable exists in this scope
                if (!$this->context->getScope()->hasVariableWithName($variable_name)) {
                    // If this is not pass-by-reference variable we
                    // have a problem
                    if (!($use->flags & \ast\flags\PARAM_REF)) {
                        $this->emitIssue(Issue::UndeclaredVariable, $node->lineno ?? 0, $variable_name);
                        continue;
                    } else {
                        // If the variable doesn't exist, but its
                        // a pass-by-reference variable, we can
                        // just create it
                        $variable = Variable::fromNodeInContext($use, $this->context, $this->code_base, false);
                    }
                } else {
                    $variable = $this->context->getScope()->getVariableByName($variable_name);
                    // If this isn't a pass-by-reference variable, we
                    // clone the variable so state within this scope
                    // doesn't update the outer scope
                    if (!($use->flags & \ast\flags\PARAM_REF)) {
                        $variable = clone $variable;
                    }
                }
                // Pass the variable into a new scope
                $func->getInternalScope()->addVariable($variable);
            }
        }
        // Add all parameters to the scope
        if (!empty($node->children['params']) && $node->children['params']->kind == \ast\AST_PARAM_LIST) {
            $params = $node->children['params'];
            foreach ($params->children as $param) {
                // Read the parameter
                $parameter = Parameter::fromNode($this->context, $this->code_base, $param);
                // Add it to the scope
                $func->getInternalScope()->addVariable($parameter);
            }
        }
        if ($this->analyzeFunctionLikeIsGenerator($node)) {
            $this->setReturnTypeOfGenerator($func, $node);
        }
        return $this->context->withScope($func->getInternalScope());
    }