function enterNode(Node $node) { $class = get_class($node); if ($node instanceof Trait_) { return NodeTraverserInterface::DONT_TRAVERSE_CHILDREN; } if ($node instanceof Class_ || $node instanceof Trait_) { array_push($this->classStack, $node); } if ($node instanceof Node\FunctionLike) { // Typecast $this->pushFunctionScope($node); } if ($node instanceof Node\Expr\Assign || $node instanceof Node\Expr\AssignRef) { $this->handleAssignment($node); } if ($node instanceof Node\Stmt\StaticVar) { $this->setScopeExpression($node->name, $node->default); } if ($node instanceof Node\Stmt\Catch_) { $this->setScopeType(strval($node->var), strval($node->type)); } if ($node instanceof Node\Stmt\Global_) { foreach ($node->vars as $var) { if ($var instanceof Node\Expr\Variable && gettype($var->name) == "string") { $this->setScopeType(strval($var->name), Scope::MIXED_TYPE); } } } if ($node instanceof Node\Expr\MethodCall) { if (gettype($node->name) == "string") { $type = $this->typeInferrer->inferType(end($this->classStack) ?: null, $node->var, end($this->scopeStack)); if ($type && $type[0] != "!") { $method = $this->index->getAbstractedMethod($type, $node->name); if ($method) { /** @var FunctionLikeParameter[] $params */ $params = $method->getParameters(); $paramCount = count($params); foreach ($node->args as $index => $arg) { if (isset($params[$index]) && $params[$index]->isReference() || $index >= $paramCount && $paramCount > 0 && $params[$paramCount - 1]->isReference()) { if ($arg->value instanceof Node\Expr\Variable && gettype($arg->value->name) == "string") { $this->setScopeType($arg->value->name, Scope::MIXED_TYPE); } } } } } } } if ($node instanceof Node\Expr\StaticCall) { if ($node->class instanceof Node\Name && gettype($node->name) == "string") { $method = $this->index->getAbstractedMethod(strval($node->class), strval($node->name)); if ($method) { /** @var FunctionLikeParameter[] $params */ $params = $method->getParameters(); $paramCount = count($params); foreach ($node->args as $index => $arg) { if (isset($params[$index]) && $params[$index]->isReference() || $index >= $paramCount && $paramCount > 0 && $params[$paramCount - 1]->isReference()) { if ($arg->value instanceof Node\Expr\Variable && gettype($arg->value->name) == "string") { $this->setScopeType($arg->value->name, Scope::MIXED_TYPE); } } } } } } if ($node instanceof Node\Expr\FuncCall) { if ($node->name instanceof Node\Name) { $function = $this->index->getAbstractedFunction(strval($node->name)); if ($function) { $params = $function->getParameters(); $paramCount = count($params); foreach ($node->args as $index => $arg) { if ($arg->value instanceof Node\Expr\Variable && gettype($arg->value->name) == "string" && (isset($params[$index]) && $params[$index]->isReference() || $index >= $paramCount && $paramCount > 0 && $params[$paramCount - 1]->isReference())) { $this->setScopeType($arg->value->name, Scope::MIXED_TYPE); } } } } } if ($node instanceof Node\Stmt\Foreach_) { if ($node->keyVar instanceof Node\Expr\Variable && gettype($node->keyVar->name) == "string") { $this->setScopeType(strval($node->keyVar->name), Scope::MIXED_TYPE); } if ($node->valueVar instanceof Node\Expr\Variable && gettype($node->valueVar->name) == "string") { $type = $this->typeInferrer->inferType(end($this->classStack) ?: null, $node->expr, end($this->scopeStack)); if (substr($type, -2) == "[]") { $type = substr($type, 0, -2); } else { $type = Scope::MIXED_TYPE; } $this->setScopeType(strval($node->valueVar->name), $type); } else { if ($node->valueVar instanceof Node\Expr\List_) { foreach ($node->valueVar->vars as $var) { if ($var instanceof Node\Expr\Variable && gettype($var->name) == "string") { $this->setScopeType(strval($var->name), Scope::MIXED_TYPE); } } } } } if ($node instanceof Node\Stmt\If_ || $node instanceof Node\Stmt\ElseIf_) { if ($node instanceof Node\Stmt\ElseIf_) { // Pop the previous if's scope array_pop($this->scopeStack); } $this->pushIfScope($node); } if ($node instanceof Node\Stmt\Else_) { // The previous scope was only valid for the if side. array_pop($this->scopeStack); } if (isset($this->checks[$class])) { foreach ($this->checks[$class] as $check) { $check->run($this->file, $node, end($this->classStack) ?: null, end($this->scopeStack) ?: null); } } return null; }
/** * Do some simplistic checks to see if we can figure out object type. If we can, then we can check method calls * using that variable for correctness. * @param Node\Expr $expr * @param Scope $scope * @return string */ function inferType(Node\Stmt\ClassLike $inside = null, Node\Expr $expr = null, Scope $scope) { if ($expr instanceof Node\Expr\AssignOp) { return $this->inferType($inside, $expr->expr, $scope); } else { if ($expr instanceof Node\Scalar) { return Scope::SCALAR_TYPE; } else { if ($expr instanceof Node\Expr\New_ && $expr->class instanceof Node\Name) { $className = strval($expr->class); if (strcasecmp($className, "self") == 0) { $className = $inside ? strval($inside->namespacedName) : Scope::MIXED_TYPE; } else { if (strcasecmp($className, "static") == 0) { $className = Scope::MIXED_TYPE; } } return $className; } else { if ($expr instanceof Node\Expr\Variable && gettype($expr->name) == "string") { $varName = strval($expr->name); if ($varName == "this" && $inside) { return strval($inside->namespacedName); } $scopeType = $scope->getVarType($varName); if ($scopeType != Scope::UNDEFINED) { return $scopeType; } } else { if ($expr instanceof Node\Expr\Closure) { return "callable"; } else { if ($expr instanceof Node\Expr\FuncCall && $expr->name instanceof Node\Name) { $func = $this->index->getFunction($expr->name); /* if($func) { $namespacedReturn =$func->getAttribute("namespacedReturn"); return $namespacedReturn ?: Scope::MIXED_TYPE; }*/ } else { if ($expr instanceof Node\Expr\MethodCall && gettype($expr->name) == "string") { $class = $this->inferType($inside, $expr->var, $scope); if (!empty($class) && $class[0] != "!") { $method = $this->index->getAbstractedMethod($class, strval($expr->name)); if ($method) { $type = $method->getReturnType(); if ($type) { return $type; } /* $type = $method->getDocBlockReturnType(); if($type) { return $type; } */ } } } else { if ($expr instanceof Node\Expr\PropertyFetch) { return $this->inferPropertyFetch($expr, $inside, $scope); } else { if ($expr instanceof Node\Expr\ArrayDimFetch) { $type = $this->inferType($inside, $expr->var, $scope); if (substr($type, -2) == "[]") { return substr($type, 0, -2); } } } } } } } } } } return Scope::MIXED_TYPE; }