/** * @return void */ public function addVariable(Variable $variable) { $variable_name = $variable->getName(); if (Variable::isHardcodedGlobalVariableWithName($variable_name)) { // Silently ignore globally replacing $_POST, $argv, runkit superglobals, etc. // with superglobals. // TODO: Add a warning for incompatible assignments in callers. return; } self::$global_variable_map[$variable->getName()] = $variable; }
/** * @return void */ public function addVariable(Variable $variable) { $this->variable_map[$variable->getName()] = $variable; }
/** * Visit a node with kind `\ast\AST_VAR` * * @param Node $node * A node of the type indicated by the method name that we'd * like to figure out the type that it produces. * * @return UnionType * The set of types that are possibly produced by the * given node */ public function visitVar(Node $node) : UnionType { // $$var or ${...} (whose idea was that anyway?) if ($node->children['name'] instanceof Node && ($node->children['name']->kind == \ast\AST_VAR || $node->children['name']->kind == \ast\AST_BINARY_OP)) { return MixedType::instance()->asUnionType(); } // This is nonsense. Give up. if ($node->children['name'] instanceof Node) { return new UnionType(); } $variable_name = $node->children['name']; if (!$this->context->getScope()->hasVariableWithName($variable_name)) { if (!Variable::isSuperglobalVariableWithName($variable_name)) { Log::err(Log::EVAR, "Variable \${$variable_name} is not defined", $this->context->getFile(), $node->lineno ?? 0); } } else { $variable = $this->context->getScope()->getVariableWithName($variable_name); return $variable->getUnionType(); } return new UnionType(); }
/** * @return void */ public function addVariable(Variable $variable) { self::$global_variable_map[$variable->getName()] = $variable; }
/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitVar(Node $node) : Context { $variable_name = (new ContextNode($this->code_base, $this->context, $node))->getVariableName(); // Check to see if the variable already exists if ($this->context->getScope()->hasVariableWithName($variable_name)) { $variable = $this->context->getScope()->getVariableWithName($variable_name); // If we're assigning to an array element then we don't // know what the constitutation of the parameter is // outside of the scope of this assignment, so we add to // its union type rather than replace it. if ($this->is_dim_assignment) { $variable->getUnionType()->addUnionType($this->right_type); } else { // If the variable isn't a pass-by-reference paramter // we clone it so as to not disturb its previous types // as we replace it. if ($variable instanceof Parameter) { if ($variable->isPassByReference()) { } else { $variable = clone $variable; } } else { $variable = clone $variable; } $variable->setUnionType($this->right_type); } $this->context->addScopeVariable($variable); return $this->context; } $variable = Variable::fromNodeInContext($this->assignment_node, $this->context, $this->code_base, false); // Set that type on the variable $variable->getUnionType()->addUnionType($this->right_type); // Note that we're not creating a new scope, just // adding variables to the existing scope $this->context->addScopeVariable($variable); return $this->context; }
/** * @return Variable * A variable in scope or a new variable * * @throws NodeException * An exception is thrown if we can't understand the node */ public function getOrCreateVariable() : Variable { try { return $this->getVariable(); } catch (IssueException $exception) { // Swallow it } // Create a new variable $variable = Variable::fromNodeInContext($this->node, $this->context, $this->code_base, false); $this->context->addScopeVariable($variable); return $variable; }
public function setUnionType(UnionType $type) { parent::setUnionType($type); }
public function isInternal() : bool { return $this->variable->isInternal(); }
/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitStatic(Node $node) : Context { $variable = Variable::fromNodeInContext($node->children['var'], $this->context, $this->code_base, false); // If the element has a default, set its type // on the variable if (isset($node->children['default'])) { $default_type = UnionType::fromNode($this->context, $this->code_base, $node->children['default']); $variable->setUnionType($default_type); } // Note that we're not creating a new scope, just // adding variables to the existing scope $this->context->addScopeVariable($variable); return $this->context; }
/** * @param Node $node * A node that has a reference to a variable * * @param Context $context * The context in which we found the reference * * @param CodeBase $code_base * * @return Variable * A variable in scope or a new variable * * @throws NodeException * An exception is thrown if we can't understand the node */ public static function getOrCreateVariableFromNodeInContext(Node $node, Context $context, CodeBase $code_base) : Variable { // Get the name of the variable $variable_name = self::variableName($node); if (empty($variable_name)) { throw new NodeException($node, "Variable name not found"); } // Check to see if the variable exists in this scope if ($context->getScope()->hasVariableWithName($variable_name)) { return $context->getScope()->getVariableWithName($variable_name); } // Create a new variable $variable = Variable::fromNodeInContext($node, $context, $code_base, false); $context->addScopeVariable($variable); return $variable; }
/** * If this parameter is variadic (e.g. `DateTime ...$args`), * then this returns the corresponding array type(s) of $args. (e.g. `DateTime[]`) * NOTE: For variadic arguments, this is a temporary variable. * Modifying this won't result in persistent changes. * (TODO(Issue #376) : We will probably want to be able to modify the underlying variable, * e.g. by creating `class UnionTypeGenericArrayView extends UnionType`. * Otherwise, type inference of `...$args` based on the function source * will be less effective without phpdoc types.) * * @override * TODO: Should the return value be set up in the constructor instead? */ public function getUnionType() : UnionType { return $this->isVariadic() ? parent::getUnionType()->asGenericArrayTypes() : parent::getUnionType(); }
/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitCatch(Node $node) : Context { try { $union_type = UnionTypeVisitor::unionTypeFromClassNode($this->code_base, $this->context, $node->children['class']); $class_list = (new ContextNode($this->code_base, $this->context, $node->children['class']))->getClassList(); } catch (CodeBaseException $exception) { Issue::emit(Issue::UndeclaredClassCatch, $this->context->getFile(), $node->lineno ?? 0, (string) $exception->getFQSEN()); } $variable_name = (new ContextNode($this->code_base, $this->context, $node->children['var']))->getVariableName(); if (!empty($variable_name)) { $variable = Variable::fromNodeInContext($node->children['var'], $this->context, $this->code_base, false); if (!$union_type->isEmpty()) { $variable->setUnionType($union_type); } $this->context->addScopeVariable($variable); } return $this->context; }
/** * @param Variable $global_variable * Any global variable * * @return null */ public function addGlobalVariable(Variable $global_variable) { $this->global_variable_map[$global_variable->getName()] = $global_variable; }
/** * Visit a node with kind `\ast\AST_VAR` * * @param Node $node * A node of the type indicated by the method name that we'd * like to figure out the type that it produces. * * @return UnionType * The set of types that are possibly produced by the * given node */ public function visitVar(Node $node) : UnionType { // $$var or ${...} (whose idea was that anyway?) if ($node->children['name'] instanceof Node && ($node->children['name']->kind == \ast\AST_VAR || $node->children['name']->kind == \ast\AST_BINARY_OP)) { return MixedType::instance()->asUnionType(); } // This is nonsense. Give up. if ($node->children['name'] instanceof Node) { return new UnionType(); } $variable_name = $node->children['name']; if (!$this->context->getScope()->hasVariableWithName($variable_name)) { if (!Variable::isSuperglobalVariableWithName($variable_name) && (!Config::get()->ignore_undeclared_variables_in_global_scope || !$this->context->isInGlobalScope())) { throw new IssueException(Issue::fromType(Issue::UndeclaredVariable)($this->context->getFile(), $node->lineno ?? 0, [$variable_name])); } } else { $variable = $this->context->getScope()->getVariableByName($variable_name); return $variable->getUnionType(); } return new UnionType(); }
/** * @param Variable $variable * A variable to add to the local scope * * @return Scope; */ public function withVariable(Variable $variable) : Scope { $scope = clone $this; $scope->variable_map[$variable->getName()] = $variable; return $scope; }
/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitCatch(Node $node) : Context { try { $union_type = UnionTypeVisitor::unionTypeFromClassNode($this->code_base, $this->context, $node->children['class']); $class_list = AST::classListFromNodeInContext($this->code_base, $this->context, $node->children['class']); } catch (CodeBaseException $exception) { Log::err(Log::EUNDEF, "catching undeclared class {$exception->getFQSEN()}", $this->context->getFile(), $node->lineno); } $variable_name = AST::variableName($node->children['var']); if (!empty($variable_name)) { $variable = Variable::fromNodeInContext($node->children['var'], $this->context, $this->code_base, false); if (!$union_type->isEmpty()) { $variable->setUnionType($union_type); } $this->context->addScopeVariable($variable); } return $this->context; }