/** * @param \PhpParser\Node\FunctionLike $function * @param \PHPStan\Analyser\Scope $scope * @param string $parameterMessage * @param string $returnMessage * @return string[] */ public function checkFunction(FunctionLike $function, Scope $scope, string $parameterMessage, string $returnMessage) : array { if ($function instanceof ClassMethod && $scope->getClass() !== null) { return $this->checkParametersAcceptor($this->broker->getClass($scope->getClass())->getMethod($function->name), $parameterMessage, $returnMessage); } if ($function instanceof Function_) { $functionName = $function->name; if (isset($function->namespacedName)) { $functionName = $function->namespacedName; } return $this->checkParametersAcceptor($this->broker->getFunction(new Name($functionName)), $parameterMessage, $returnMessage); } $errors = []; foreach ($function->getParams() as $param) { $class = (string) $param->type; if ($class && !in_array($class, self::VALID_TYPEHINTS, true) && !$this->broker->hasClass($class)) { $errors[] = sprintf($parameterMessage, $param->name, $class); } } $returnType = (string) $function->getReturnType(); if ($returnType && !in_array($returnType, self::VALID_TYPEHINTS, true) && !$this->broker->hasClass($returnType)) { $errors[] = sprintf($returnMessage, $returnType); } return $errors; }
/** * @param FunctionLike $function * * @return string[] */ private function getByRefParameters(FunctionLike $function) : array { $byRefParams = []; foreach ($function->getParams() as $param) { if ($param->byRef) { $byRefParams[] = $param->name; } } return $byRefParams; }
/** * @param FunctionLike $node A PHP-Parser Node function-like object to resolve return types of * * @return array A array of the return types, or empty array if no return comment exists */ public static function parseNode(FunctionLike $node) { /** @var Comment[] $returnComments */ $returnComments = self::filterComments($node->getAttribute("comments", [])); if (empty($returnComments)) { return []; // TODO: void? mixed? Maybe verify that comments tag exists? } /** @var DocBlock\Tag $lastReturnKeyword If multiple "@return" tags, only the last one in last DocBlock used. */ $lastReturnKeyword = last((new DocBlock(last($returnComments)->getReformattedText()))->getTagsByName("return")); // Everything after the tag (after the appending space), is tag content. return self::parse(DocTypeNormaliser::sanitise($lastReturnKeyword->getContent())); }
/** * @param FunctionLike $node A PHP-Parser Node function-like object to resolve param types of * * @return array A array of the param arguments and types (if possible), or empty array if no arguments accepted */ public static function parseNode(FunctionLike $node) { /** @var Comment[] $paramComments */ $paramComments = self::filterComments($node->getAttribute("comments", [])); if (empty($paramComments)) { return []; } $posCount = 0; // Used for positional @param tags e.g. without $varName $params = array_unique(array_map(function (DocBlock\Tag $param) use(&$posCount) { $param = DocTypeNormaliser::sanitise($param->getContent()); $param = self::parse($param, $posCount++); // Below code can be used to selectively increment posCount - remove ++ above // $posCount = ($param["name"]{0} === "#") ? $posCount + 1 : $posCount; return $param; }, array_flatten(array_map(function (Comment $comment) { return (new DocBlock($comment->getReformattedText()))->getTagsByName("param"); }, $paramComments))), SORT_REGULAR); // ["$var" => ["pos" => 0, "name" => "$var", "type" => ["bool", "\Class"]]] // ["#1" => ["pos" => 1, "name" => "#1", "type" => ["bool", "\Class"]]] return array_combine(array_map(function ($param) { return $param["name"]; }, $params), $params); }
function pushFunctionScope(Node\FunctionLike $func) { $isStatic = true; if ($func instanceof Node\Stmt\ClassMethod) { $isStatic = $func->isStatic(); } $scope = new Scope($isStatic); foreach ($func->getParams() as $param) { $scope->setVarType(strval($param->name), strval($param->type)); } if ($func instanceof Node\Expr\Closure) { $oldScope = end($this->scopeStack); foreach ($func->uses as $variable) { $type = $oldScope->getVarType($variable->var); if ($type == Scope::UNDEFINED) { $type = Scope::MIXED_TYPE; } $scope->setVarType($variable->var, $type); } } array_push($this->scopeStack, $scope); }
/** * {@inheritDoc} */ public function returnsReference() { return $this->functionLikeNode->returnsByRef(); }