/** * Perform some backwards compatibility checks on a node * * @param Context $context * The context in which the node appears * * @param Node $node * The node we'd like to check * * @return null * * @see \Phan\Deprecated::bc_check * Formerly `function bc_check` */ public static function backwardCompatibilityCheck(Context $context, Node $node) { if (empty($node->children['expr'])) { return; } if ($node->kind !== \ast\AST_DIM) { if (!$node->children['expr'] instanceof Node) { return; } if ($node->children['expr']->kind !== \ast\AST_DIM) { AST::backwardCompatibilityCheck($context, $node->children['expr']); return; } $temp = $node->children['expr']->children['expr']; $lnode = $temp; } else { $temp = $node->children['expr']; $lnode = $temp; } if (!($temp->kind == \ast\AST_PROP || $temp->kind == \ast\AST_STATIC_PROP)) { return; } while ($temp instanceof Node && ($temp->kind == \ast\AST_PROP || $temp->kind == \ast\AST_STATIC_PROP)) { $lnode = $temp; // Lets just hope the 0th is the expression // we want $temp = array_values($temp->children)[0]; } if (!$temp instanceof Node) { return; } // Foo::$bar['baz'](); is a problem // Foo::$bar['baz'] is not if ($lnode->kind === \ast\AST_STATIC_PROP && $node->kind !== \ast\AST_CALL) { return; } if (($lnode->children['prop'] instanceof Node && $lnode->children['prop']->kind == \ast\AST_VAR || !empty($lnode->children['class']) && $lnode->children['class'] instanceof Node && ($lnode->children['class']->kind == \ast\AST_VAR || $lnode->children['class']->kind == \ast\AST_NAME)) && ($temp->kind == \ast\AST_VAR || $temp->kind == \ast\AST_NAME)) { $ftemp = new \SplFileObject($context->getFile()); $ftemp->seek($node->lineno - 1); $line = $ftemp->current(); unset($ftemp); if (strpos($line, '}[') === false || strpos($line, ']}') === false || strpos($line, '>{') === false) { Log::err(Log::ECOMPAT, "expression may not be PHP 7 compatible", $context->getFile(), $node->lineno ?? 0); } } }
/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitCall(Node $node) : Context { $expression = $node->children['expr']; if (Config::get()->backward_compatibility_checks) { AST::backwardCompatibilityCheck($this->context, $node); foreach ($node->children['args']->children as $arg_node) { if ($arg_node instanceof Node) { AST::backwardCompatibilityCheck($this->context, $arg_node); } } } if ($expression->kind == \ast\AST_NAME) { try { $method = AST::functionFromNameInContext($expression->children['name'], $this->context, $this->code_base); } catch (CodeBaseException $exception) { Log::err(Log::EUNDEF, $exception->getMessage(), $this->context->getFile(), $node->lineno); return $this->context; } // Check the call for paraemter and argument types $this->analyzeCallToMethod($this->code_base, $method, $node); } else { if ($expression->kind == \ast\AST_VAR) { $variable_name = AST::variableName($expression); if (empty($variable_name)) { return $this->context; } // $var() - hopefully a closure, otherwise we don't know if ($this->context->getScope()->hasVariableWithName($variable_name)) { $variable = $this->context->getScope()->getVariableWithName($variable_name); $union_type = $variable->getUnionType(); if ($union_type->isEmpty()) { return $this->context; } $type = $union_type->head(); if (!$type instanceof CallableType) { return $this->context; } $closure_fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString((string) $type->asFQSEN()); if ($this->code_base->hasMethod($closure_fqsen)) { // Get the closure $method = $this->code_base->getMethod($closure_fqsen); // Check the call for paraemter and argument types $this->analyzeCallToMethod($this->code_base, $method, $node); } } } } return $this->context; }
/** * Visit a node with kind `\ast\AST_RETURN` * * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitReturn(Node $node) : Context { if (Config::get()->backward_compatibility_checks) { AST::backwardCompatibilityCheck($this->context, $node); } return $this->context; }