getFunction() public method

public getFunction ( Name $nameNode, Scope $scope = null ) : FunctionReflection
$nameNode PhpParser\Node\Name
$scope PHPStan\Analyser\Scope
return PHPStan\Reflection\FunctionReflection
Exemplo n.º 1
0
 /**
  * @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 \PhpParser\Node\Expr\FuncCall $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!$node->name instanceof \PhpParser\Node\Name) {
         return [];
     }
     if (!$this->broker->hasFunction($node->name, $scope)) {
         return [];
     }
     $function = $this->broker->getFunction($node->name, $scope);
     return $this->check->check($function, $node, ['Function ' . $function->getName() . ' invoked with %d parameter, %d required.', 'Function ' . $function->getName() . ' invoked with %d parameters, %d required.', 'Function ' . $function->getName() . ' invoked with %d parameter, at least %d required.', 'Function ' . $function->getName() . ' invoked with %d parameters, at least %d required.', 'Function ' . $function->getName() . ' invoked with %d parameter, %d-%d required.', 'Function ' . $function->getName() . ' invoked with %d parameters, %d-%d required.']);
 }
 /**
  * @param \PhpParser\Node\Expr\FuncCall $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!$node->name instanceof \PhpParser\Node\Name) {
         return [];
     }
     if (!$this->broker->hasFunction($node->name, $scope)) {
         return [sprintf('Function %s not found.', (string) $node->name)];
     }
     $function = $this->broker->getFunction($node->name, $scope);
     $name = (string) $node->name;
     if ($function->getName() !== $this->broker->resolveFunctionName($node->name, $scope)) {
         return [sprintf('Call to function %s() with incorrect case: %s', $function->getName(), $name)];
     }
     return [];
 }
Exemplo n.º 4
0
 /**
  * @param \PhpParser\Node\Expr $functionCall
  * @param \PHPStan\Analyser\Scope $scope
  * @return null|\PHPStan\Reflection\ParametersAcceptor
  */
 private function findParametersAcceptorInFunctionCall(Expr $functionCall, Scope $scope)
 {
     if ($functionCall instanceof FuncCall && $functionCall->name instanceof Name) {
         if ($this->broker->hasFunction($functionCall->name, $scope)) {
             return $this->broker->getFunction($functionCall->name, $scope);
         }
     } elseif ($functionCall instanceof MethodCall && is_string($functionCall->name)) {
         $type = $scope->getType($functionCall->var);
         if ($type->getClass() !== null && $this->broker->hasClass($type->getClass())) {
             $classReflection = $this->broker->getClass($type->getClass());
             $methodName = $functionCall->name;
             if ($classReflection->hasMethod((string) $methodName)) {
                 return $classReflection->getMethod((string) $methodName);
             }
         }
     } elseif ($functionCall instanceof Expr\StaticCall && $functionCall->class instanceof Name && is_string($functionCall->name)) {
         $className = (string) $functionCall->class;
         if ($this->broker->hasClass($className)) {
             $classReflection = $this->broker->getClass($className);
             if ($classReflection->hasMethod($functionCall->name)) {
                 return $classReflection->getMethod($functionCall->name);
             }
         }
     }
     return null;
 }
Exemplo n.º 5
0
 public function getType(Node $node) : Type
 {
     if ($node instanceof \PhpParser\Node\Expr\BinaryOp\BooleanAnd || $node instanceof \PhpParser\Node\Expr\BinaryOp\BooleanOr || $node instanceof \PhpParser\Node\Expr\BooleanNot || $node instanceof \PhpParser\Node\Expr\BinaryOp\LogicalXor) {
         return new BooleanType(false);
     }
     if ($node instanceof Node\Expr\UnaryMinus || $node instanceof Node\Expr\UnaryPlus) {
         return $this->getType($node->expr);
     }
     if ($node instanceof Node\Expr\BinaryOp\Mod) {
         return new IntegerType(false);
     }
     if ($node instanceof Expr\BinaryOp\Concat) {
         return new StringType(false);
     }
     if ($node instanceof Expr\BinaryOp\Spaceship) {
         return new IntegerType(false);
     }
     if ($node instanceof Node\Expr\BinaryOp\Plus || $node instanceof Node\Expr\BinaryOp\Minus || $node instanceof Node\Expr\BinaryOp\Mul || $node instanceof Node\Expr\BinaryOp\Pow || $node instanceof Node\Expr\BinaryOp\Div || $node instanceof Node\Expr\AssignOp) {
         if ($node instanceof Node\Expr\AssignOp) {
             $left = $node->var;
             $right = $node->expr;
         } elseif ($node instanceof Node\Expr\BinaryOp) {
             $left = $node->left;
             $right = $node->right;
         } else {
             throw new \PHPStan\ShouldNotHappenException();
         }
         $leftType = $this->getType($left);
         $rightType = $this->getType($right);
         if ($leftType instanceof BooleanType) {
             $leftType = new IntegerType($leftType->isNullable());
         }
         if ($rightType instanceof BooleanType) {
             $rightType = new IntegerType($rightType->isNullable());
         }
         if ($node instanceof Expr\AssignOp\Div || $node instanceof Expr\BinaryOp\Div) {
             if (!$leftType instanceof MixedType && !$rightType instanceof MixedType) {
                 return new FloatType(false);
             }
         }
         if ($leftType instanceof FloatType && !$rightType instanceof MixedType || $rightType instanceof FloatType && !$leftType instanceof MixedType) {
             return new FloatType(false);
         }
         if ($leftType instanceof IntegerType && $rightType instanceof IntegerType) {
             return new IntegerType(false);
         }
     }
     if ($node instanceof LNumber) {
         return new IntegerType(false);
     } elseif ($node instanceof ConstFetch) {
         $constName = strtolower((string) $node->name);
         if (in_array($constName, ['true', 'false'], true)) {
             return new BooleanType(false);
         }
         if ($constName === 'null') {
             return new NullType();
         }
     } elseif ($node instanceof String_) {
         return new StringType(false);
     } elseif ($node instanceof DNumber) {
         return new FloatType(false);
     } elseif ($node instanceof Expr\Closure) {
         return new ObjectType('Closure', false);
     } elseif ($node instanceof New_) {
         if ($node->class instanceof Name) {
             if (count($node->class->parts) === 1) {
                 if ($node->class->parts[0] === 'static') {
                     return new MixedType(false);
                 } elseif ($node->class->parts[0] === 'self') {
                     return new ObjectType($this->getClass(), false);
                 }
             }
             return new ObjectType((string) $node->class, false);
         }
     } elseif ($node instanceof Array_) {
         $possiblyCallable = false;
         if (count($node->items) === 2) {
             $firstItem = $node->items[0]->value;
             if (($this->getType($firstItem) instanceof ObjectType || $this->getType($firstItem) instanceof StringType) && $this->getType($node->items[1]->value) instanceof StringType) {
                 $possiblyCallable = true;
             }
         }
         return new ArrayType($this->getCombinedType(array_map(function (Expr\ArrayItem $item) : Type {
             return $this->getType($item->value);
         }, $node->items)), false, true, $possiblyCallable);
     } elseif ($node instanceof Int_) {
         return new IntegerType(false);
     } elseif ($node instanceof Bool_) {
         return new BooleanType(false);
     } elseif ($node instanceof Double) {
         return new FloatType(false);
     } elseif ($node instanceof \PhpParser\Node\Expr\Cast\String_) {
         return new StringType(false);
     } elseif ($node instanceof \PhpParser\Node\Expr\Cast\Array_) {
         return new ArrayType(new MixedType(true), false);
     } elseif ($node instanceof Object_) {
         return new ObjectType('stdClass', false);
     } elseif ($node instanceof Unset_) {
         return new NullType();
     } elseif ($node instanceof Node\Expr\ClassConstFetch) {
         if ($node->class instanceof Name) {
             $constantClass = (string) $node->class;
             if ($constantClass === 'self') {
                 $constantClass = $this->getClass();
             }
         } else {
             $constantClassType = $this->getType($node->class);
             if ($constantClassType->getClass() !== null) {
                 $constantClass = $constantClassType->getClass();
             }
         }
         if (isset($constantClass)) {
             $constantName = $node->name;
             if ($this->broker->hasClass($constantClass)) {
                 $constantClassReflection = $this->broker->getClass($constantClass);
                 if ($constantClassReflection->hasConstant($constantName)) {
                     $constant = $constantClassReflection->getConstant($constantName);
                     $typeFromValue = $this->getTypeFromValue($constant->getValue());
                     if ($typeFromValue !== null) {
                         return $typeFromValue;
                     }
                 }
             }
         }
     }
     $exprString = $this->printer->prettyPrintExpr($node);
     if (isset($this->moreSpecificTypes[$exprString])) {
         return $this->moreSpecificTypes[$exprString];
     }
     if ($node instanceof Variable && is_string($node->name)) {
         if (!$this->hasVariableType($node->name)) {
             return new MixedType(true);
         }
         return $this->getVariableType($node->name);
     }
     if ($node instanceof Expr\ArrayDimFetch && $node->dim !== null) {
         $arrayType = $this->getType($node->var);
         if ($arrayType instanceof ArrayType) {
             return $arrayType->getItemType();
         }
     }
     if ($node instanceof MethodCall && is_string($node->name)) {
         $methodCalledOnType = $this->getType($node->var);
         if ($methodCalledOnType->getClass() !== null && $this->broker->hasClass($methodCalledOnType->getClass())) {
             $methodClassReflection = $this->broker->getClass($methodCalledOnType->getClass());
             if (!$methodClassReflection->hasMethod($node->name)) {
                 return new MixedType(true);
             }
             $methodReflection = $methodClassReflection->getMethod($node->name);
             foreach ($this->broker->getDynamicMethodReturnTypeExtensionsForClass($methodCalledOnType->getClass()) as $dynamicMethodReturnTypeExtension) {
                 if (!$dynamicMethodReturnTypeExtension->isMethodSupported($methodReflection)) {
                     continue;
                 }
                 return $dynamicMethodReturnTypeExtension->getTypeFromMethodCall($methodReflection, $node, $this);
             }
             if ($methodReflection->getReturnType() instanceof StaticType) {
                 if ($methodReflection->getReturnType()->isNullable()) {
                     return $methodCalledOnType->makeNullable();
                 }
                 return $methodCalledOnType;
             }
             return $methodReflection->getReturnType();
         }
     }
     if ($node instanceof Expr\StaticCall && is_string($node->name) && $node->class instanceof Name) {
         $calleeClass = $this->resolveName($node->class);
         if ($calleeClass !== null && $this->broker->hasClass($calleeClass)) {
             $staticMethodClassReflection = $this->broker->getClass($calleeClass);
             if (!$staticMethodClassReflection->hasMethod($node->name)) {
                 return new MixedType(true);
             }
             $staticMethodReflection = $staticMethodClassReflection->getMethod($node->name);
             if ($staticMethodReflection->getReturnType() instanceof StaticType) {
                 $nodeClassString = (string) $node->class;
                 if ($nodeClassString === 'parent' && $this->getClass() !== null) {
                     return new StaticType($this->getClass(), $staticMethodReflection->getReturnType()->isNullable());
                 }
                 $calleeType = new ObjectType($calleeClass, false);
                 if ($staticMethodReflection->getReturnType()->isNullable()) {
                     return $calleeType->makeNullable();
                 }
                 return $calleeType;
             }
             return $staticMethodReflection->getReturnType();
         }
     }
     if ($node instanceof PropertyFetch && is_string($node->name)) {
         $propertyFetchedOnType = $this->getType($node->var);
         if ($propertyFetchedOnType->getClass() !== null && $this->broker->hasClass($propertyFetchedOnType->getClass())) {
             $propertyClassReflection = $this->broker->getClass($propertyFetchedOnType->getClass());
             if (!$propertyClassReflection->hasProperty($node->name)) {
                 return new MixedType(true);
             }
             return $propertyClassReflection->getProperty($node->name, $this)->getType();
         }
     }
     if ($node instanceof Expr\StaticPropertyFetch && is_string($node->name) && $node->class instanceof Name) {
         $staticPropertyHolderClass = $this->resolveName($node->class);
         if ($staticPropertyHolderClass !== null && $this->broker->hasClass($staticPropertyHolderClass)) {
             $staticPropertyClassReflection = $this->broker->getClass($staticPropertyHolderClass);
             if (!$staticPropertyClassReflection->hasProperty($node->name)) {
                 return new MixedType(true);
             }
             return $staticPropertyClassReflection->getProperty($node->name, $this)->getType();
         }
     }
     if ($node instanceof FuncCall && $node->name instanceof Name) {
         $arrayFunctionsThatDependOnClosureReturnType = ['array_map' => 0, 'array_reduce' => 1];
         $functionName = (string) $node->name;
         if (isset($arrayFunctionsThatDependOnClosureReturnType[$functionName]) && isset($node->args[$arrayFunctionsThatDependOnClosureReturnType[$functionName]]) && $node->args[$arrayFunctionsThatDependOnClosureReturnType[$functionName]]->value instanceof Expr\Closure) {
             $closure = $node->args[$arrayFunctionsThatDependOnClosureReturnType[$functionName]]->value;
             $anonymousFunctionType = $this->getFunctionType($closure->returnType, $closure->returnType === null, false);
             if ($functionName === 'array_reduce') {
                 return $anonymousFunctionType;
             }
             return new ArrayType($anonymousFunctionType, false);
         }
         $arrayFunctionsThatDependOnArgumentType = ['array_filter' => 0, 'array_unique' => 0, 'array_reverse' => 0];
         if (isset($arrayFunctionsThatDependOnArgumentType[$functionName]) && isset($node->args[$arrayFunctionsThatDependOnArgumentType[$functionName]])) {
             $argumentValue = $node->args[$arrayFunctionsThatDependOnArgumentType[$functionName]]->value;
             return $this->getType($argumentValue);
         }
         if (!$this->broker->hasFunction($node->name, $this)) {
             return new MixedType(true);
         }
         return $this->broker->getFunction($node->name, $this)->getReturnType();
     }
     return new MixedType(false);
 }