public function visitBranchedContext(Node $node) : Context { $context = $this->context->withLineNumberStart($node->lineno ?? 0); // 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 $context = (new PreOrderAnalysisVisitor($this->code_base, $context))($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 = []; // With a context that is inside of the node passed // to this method, we analyze all children of the // node. foreach ($node->children ?? [] as $node_key => $child_node) { // Skip any non Node children. if (!$child_node instanceof Node) { continue; } if (!Analysis::shouldVisit($child_node)) { continue; } // The conditions need to communicate to the outter // scope for things like assigning veriables. if ($child_node->kind != \ast\AST_IF_ELEM) { $child_context = $context->withScope(new BranchScope($context->getScope())); } else { $child_context = $context; } $child_context->withLineNumberStart($child_node->lineno ?? 0); // Step into each child node and get an // updated context for the node $child_context = (new BlockAnalysisVisitor($this->code_base, $child_context, $node, $this->depth + 1))($child_node); $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 $context = (new ContextMergeVisitor($this->code_base, $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. $context = (new PostOrderAnalysisVisitor($this->code_base, $context->withLineNumberStart($node->lineno ?? 0), $this->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 return $context; }