/** * @param Node $node * A node to parse * * @return void */ public function visitClosure(Decl $node) { try { $method = (new ContextNode($this->code_base, $this->context->withLineNumberStart($node->lineno ?? 0), $node))->getClosure(); $method->addReference($this->context); } catch (\Exception $exception) { // Swallow it } }
/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitAssign(Node $node) : Context { // Get the type of the right side of the // assignment $right_type = UnionType::fromNode($this->context, $this->code_base, $node->children['expr']); assert($node->children['var'] instanceof Node, "Expected left side of assignment to be a var in {$this->context}"); // Handle the assignment based on the type of the // right side of the equation and the kind of item // on the left $context = (new AssignmentVisitor($this->code_base, $this->context, $node, $right_type))($node->children['var']); // Analyze the assignment for compatibility with some // breaking changes betweeen PHP5 and PHP7. (new ContextNode($this->code_base, $this->context, $node))->analyzeBackwardCompatibility(); if ($node->children['expr'] instanceof Node && $node->children['expr']->kind == \ast\AST_CLOSURE) { $closure_node = $node->children['expr']; $method = (new ContextNode($this->code_base, $this->context->withLineNumberStart($closure_node->lineno ?? 0), $closure_node))->getClosure(); $method->addReference($this->context); } return $context; }
/** * @param CodeBase $code_base * A code base needs to be passed in because we require * it to be initialized before any classes or files are * loaded. * * @param Context $context * The context in which this node exists * * @param Node $node * A node to parse and scan for errors * * @return Context * The context from within the node is returned */ public static function analyzeNodeInContext(CodeBase $code_base, Context $context, Node $node, Node $parent_node = null, int $depth = 0) : Context { // Visit the given node populating the code base // with anything we learn and get a new context // indicating the state of the world within the // given node $node_context = (new PreOrderAnalysisVisitor($code_base, $context->withLineNumberStart($node->lineno ?? 0)))($node); assert(!empty($context), 'Context cannot be null'); // We collect all child context so that the // PostOrderAnalysisVisitor can optionally operate on // them $child_context_list = []; $child_context = $node_context; // With a context that is inside of the node passed // to this method, we analyze all children of the // node. foreach ($node->children ?? [] as $child_node) { // Skip any non Node children. if (!$child_node instanceof Node) { continue; } if (!self::shouldVisit($child_node)) { $child_context->withLineNumberStart($child_node->lineno ?? 0); continue; } // All nodes but conditionals pass context to // their siblings. Child nodes of conditionals // operate in a context independent of eachother switch ($child_node->kind) { case \ast\AST_IF_ELEM: $child_context = $node_context; break; } // Step into each child node and get an // updated context for the node $child_context = self::analyzeNodeInContext($code_base, $child_context->withLineNumberStart($child_node->lineno ?? 0), $child_node, $node, $depth + 1); $child_context_list[] = $child_context; } // For if statements, we need to merge the contexts // of all child context into a single scope based // on any possible branching structure $node_context = (new ContextMergeVisitor($code_base, $node_context, $child_context_list))($node); // Now that we know all about our context (like what // 'self' means), we can analyze statements like // assignments and method calls. $node_context = (new PostOrderAnalysisVisitor($code_base, $node_context->withLineNumberStart($node->lineno ?? 0), $parent_node))($node); // When coming out of a scoped element, we pop the // context to be the incoming context. Otherwise, // we pass our new context up to our parent switch ($node->kind) { case \ast\AST_CLASS: case \ast\AST_METHOD: case \ast\AST_FUNC_DECL: case \ast\AST_CLOSURE: return $context; default: return $node_context; } }
/** * @param Node $node * A node to parse and scan for errors * * @param Context $context * The context in which this node exists * * @return Context * The context from within the node is returned */ public function analyzeNodeInContext(Node $node, Context $context, CodeBase $code_base, Node $parent_node = null, int $depth = 0) : Context { // Visit the given node populating the code base // with anything we learn and get a new context // indicating the state of the world within the // given node $child_context = (new Element($node))->acceptKindVisitor(new DepthFirstVisitor($context->withLineNumberStart($node->lineno ?? 0)->withLineNumberEnd($node->endLineno ?? 0), $code_base)); assert(!empty($context), 'Context cannot be null'); // Go depth first on that first set of analyses foreach ($node->children ?? [] as $child_node) { // Skip any non Node children. if (!$child_node instanceof Node) { continue; } // Step into each child node and get an // updated context for the node $child_context = $this->analyzeNodeInContext($child_node, $child_context->withLineNumberStart($child_node->lineno ?? 0)->withLineNumberEnd($child_node->endLineno ?? 0), $code_base, $node, $depth + 1); } $context = (new Element($node))->acceptKindVisitor(new BreadthFirstVisitor($context->withLineNumberStart($node->lineno ?? 0)->withLineNumberEnd($node->endLineno ?? 0), $code_base, $parent_node)); // Pass the context back up to our parent return $context; }