예제 #1
0
 /**
  * @param \PhpParser\Node\Stmt\Return_ $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!$scope->isInAnonymousFunction()) {
         return [];
     }
     return $this->returnTypeCheck->checkReturnType($scope, $scope->getAnonymousFunctionReturnType(), $node->expr, 'Anonymous function should return %s but empty return statement found.', 'Anonymous function with return type void returns %s but should not return anything.', 'Anonymous function should return %s but returns %s.');
 }
 /**
  * @param \PhpParser\Node\Stmt\ClassMethod $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if ($node->name !== '__construct') {
         return [];
     }
     $className = $scope->getClass();
     if ($className === null) {
         return [];
         // anonymous class
     }
     $classReflection = $this->broker->getClass($className);
     if ($classReflection->isInterface()) {
         return [];
     }
     if ($this->callsParentConstruct($node)) {
         if ($classReflection->getParentClass() === false) {
             return [sprintf('%s::__construct() calls parent constructor but does not extend any class.', $className)];
         }
         if ($this->getParentConstructorClass($classReflection) === false) {
             return [sprintf('%s::__construct() calls parent constructor but parent does not have one.', $className)];
         }
     } else {
         $parentClass = $this->getParentConstructorClass($classReflection);
         if ($parentClass !== false) {
             return [sprintf('%s::__construct() does not call parent constructor from %s.', $className, $parentClass->getName())];
         }
     }
     return [];
 }
예제 #3
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;
 }
예제 #4
0
 /**
  * @param \PhpParser\Node\Expr\New_ $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!$node->class instanceof \PhpParser\Node\Name) {
         return [];
     }
     $class = (string) $node->class;
     if ($class === 'static') {
         return [];
     }
     if ($class === 'self') {
         $class = $scope->getClass();
         if ($class === null) {
             return [];
         }
     }
     if (!$this->broker->hasClass($class)) {
         return [sprintf('Instantiated class %s not found.', $class)];
     }
     $classReflection = $this->broker->getClass($class);
     if ($classReflection->isInterface()) {
         return [sprintf('Cannot instantiate interface %s.', $classReflection->getName())];
     }
     if ($classReflection->isAbstract()) {
         return [sprintf('Instantiated class %s is abstract.', $classReflection->getName())];
     }
     if (!$classReflection->hasMethod('__construct') && !$classReflection->hasMethod($class)) {
         if (count($node->args) > 0) {
             return [sprintf('Class %s does not have a constructor and must be instantiated without any parameters.', $classReflection->getName())];
         }
         return [];
     }
     return $this->check->check($classReflection->hasMethod('__construct') ? $classReflection->getMethod('__construct') : $classReflection->getMethod($class), $node, ['Class ' . $classReflection->getName() . ' constructor invoked with %d parameter, %d required.', 'Class ' . $classReflection->getName() . ' constructor invoked with %d parameters, %d required.', 'Class ' . $classReflection->getName() . ' constructor invoked with %d parameter, at least %d required.', 'Class ' . $classReflection->getName() . ' constructor invoked with %d parameters, at least %d required.', 'Class ' . $classReflection->getName() . ' constructor invoked with %d parameter, %d-%d required.', 'Class ' . $classReflection->getName() . ' constructor invoked with %d parameters, %d-%d required.']);
 }
 /**
  * @param \PhpParser\Node\Stmt\ClassMethod $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if ($node->name !== '__construct') {
         return [];
     }
     $unusedParameters = [];
     foreach ($node->params as $parameter) {
         $unusedParameters[$parameter->name] = true;
     }
     foreach ($this->getUsedVariables($node) as $variableName) {
         if (isset($unusedParameters[$variableName])) {
             unset($unusedParameters[$variableName]);
         }
     }
     $errors = [];
     if ($scope->getClass() !== null) {
         $message = sprintf('Constructor of class %s has an unused parameter $%%s.', $scope->getClass());
     } else {
         $message = 'Constructor of an anonymous class has an unused parameter $%s.';
     }
     foreach ($unusedParameters as $name => $bool) {
         $errors[] = sprintf($message, $name);
     }
     return $errors;
 }
예제 #6
0
 /**
  * @param \PhpParser\Node\Expr\StaticCall $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!is_string($node->name)) {
         return [];
     }
     $name = $node->name;
     $currentClass = $scope->getClass();
     if ($currentClass === null) {
         return [];
     }
     $currentClassReflection = $this->broker->getClass($currentClass);
     if (!$node->class instanceof Name) {
         return [];
     }
     $class = (string) $node->class;
     if ($class === 'self' || $class === 'static') {
         $class = $currentClass;
     }
     if ($class === 'parent') {
         if ($currentClassReflection->getParentClass() === false) {
             return [sprintf('%s::%s() calls to parent::%s() but %s does not extend any class.', $currentClass, $scope->getFunctionName(), $name, $currentClass)];
         }
         $currentMethodReflection = $currentClassReflection->getMethod($scope->getFunctionName());
         if (!$currentMethodReflection->isStatic()) {
             if ($name === '__construct' && $currentClassReflection->getParentClass()->hasMethod('__construct')) {
                 return $this->check->check($currentClassReflection->getParentClass()->getMethod('__construct'), $node, ['Parent constructor invoked with %d parameter, %d required.', 'Parent constructor invoked with %d parameters, %d required.', 'Parent constructor invoked with %d parameter, at least %d required.', 'Parent constructor invoked with %d parameters, at least %d required.', 'Parent constructor invoked with %d parameter, %d-%d required.', 'Parent constructor invoked with %d parameters, %d-%d required.']);
             }
             return [];
         }
         $class = $currentClassReflection->getParentClass()->getName();
     }
     $classReflection = $this->broker->getClass($class);
     if (!$classReflection->hasMethod($name)) {
         return [sprintf('Call to an undefined static method %s::%s().', $class, $name)];
     }
     $method = $classReflection->getMethod($name);
     if (!$method->isStatic()) {
         return [sprintf('Static call to instance method %s::%s().', $class, $method->getName())];
     }
     if ($currentClass !== null && $method->getDeclaringClass()->getName() !== $currentClass) {
         if (in_array($method->getDeclaringClass()->getName(), $currentClassReflection->getParentClassesNames(), true)) {
             if ($method->isPrivate()) {
                 return [sprintf('Call to private static method %s() of class %s.', $method->getName(), $method->getDeclaringClass()->getName())];
             }
         } else {
             if (!$method->isPublic()) {
                 return [sprintf('Call to %s static method %s() of class %s.', $method->isPrivate() ? 'private' : 'protected', $method->getName(), $method->getDeclaringClass()->getName())];
             }
         }
     }
     $methodName = $class . '::' . $method->getName() . '()';
     $errors = $this->check->check($method, $node, ['Static method ' . $methodName . ' invoked with %d parameter, %d required.', 'Static method ' . $methodName . ' invoked with %d parameters, %d required.', 'Static method ' . $methodName . ' invoked with %d parameter, at least %d required.', 'Static method ' . $methodName . ' invoked with %d parameters, at least %d required.', 'Static method ' . $methodName . ' invoked with %d parameter, %d-%d required.', 'Static method ' . $methodName . ' invoked with %d parameters, %d-%d required.']);
     if ($method->getName() !== $name) {
         $errors[] = sprintf('Call to static method %s with incorrect case: %s', $methodName, $name);
     }
     return $errors;
 }
 /**
  * @param \PhpParser\Node\Expr\ClosureUse $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if ($node->byRef) {
         return [];
     }
     if (!$scope->hasVariableType($node->var)) {
         return [sprintf('Undefined variable: $%s', $node->var)];
     }
     return [];
 }
예제 #8
0
 /**
  * @param \PhpParser\Node\Stmt\Return_ $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if ($scope->getFunction() === null) {
         return [];
     }
     if ($scope->isInAnonymousFunction()) {
         return [];
     }
     $method = $scope->getFunction();
     if (!$method instanceof MethodReflection) {
         return [];
     }
     return $this->returnTypeCheck->checkReturnType($scope, $method->getReturnType(), $node->expr, sprintf('Method %s::%s() should return %%s but empty return statement found.', $method->getDeclaringClass()->getName(), $method->getName()), sprintf('Method %s::%s() with return type void returns %%s but should not return anything.', $method->getDeclaringClass()->getName(), $method->getName()), sprintf('Method %s::%s() should return %%s but returns %%s.', $method->getDeclaringClass()->getName(), $method->getName()));
 }
 /**
  * @param \PhpParser\Node\Expr\BinaryOp $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[] errors
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!$node instanceof Node\Expr\BinaryOp\Identical && !$node instanceof Node\Expr\BinaryOp\NotIdentical) {
         return [];
     }
     $leftType = $scope->getType($node->left);
     $rightType = $scope->getType($node->right);
     if ($leftType instanceof MixedType || $rightType instanceof MixedType || $leftType instanceof NullType || $rightType instanceof NullType) {
         return [];
     }
     if (get_class($leftType) !== get_class($rightType)) {
         return [sprintf('Strict comparison using %s between %s and %s will always evaluate to false.', $node instanceof Node\Expr\BinaryOp\Identical ? '===' : '!==', $leftType->describe(), $rightType->describe())];
     }
     return [];
 }
예제 #10
0
 /**
  * @param \PhpParser\Node\Expr\Cast $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[] errors
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if ($node instanceof Object_) {
         return [];
     }
     $expressionType = $scope->getType($node->expr);
     if ($expressionType->isNullable()) {
         return [];
     }
     $castType = $scope->getType($node);
     if (get_class($expressionType) === get_class($castType)) {
         return [sprintf('Casting to %s something that\'s already %s.', $castType->describe(), $expressionType->describe())];
     }
     return [];
 }
예제 #11
0
 /**
  * @param \PhpParser\Node\Expr\Variable $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!is_string($node->name)) {
         return [];
     }
     if (in_array($node->name, ['GLOBALS', '_SERVER', '_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_REQUEST', '_ENV'], true)) {
         return [];
     }
     if ($scope->isInVariableAssign($node->name)) {
         return [];
     }
     if (!$scope->hasVariableType($node->name)) {
         return [sprintf('Undefined variable: $%s', $node->name)];
     }
     return [];
 }
예제 #12
0
 /**
  * @param \PHPStan\Analyser\Scope $scope
  * @param \PHPStan\Type\Type $returnType
  * @param \PhpParser\Node\Expr|null $returnValue
  * @param string $emptyReturnStatementMessage
  * @param string $voidMessage
  * @param string $typeMismatchMessage
  * @return string[]
  */
 public function checkReturnType(Scope $scope, Type $returnType, Expr $returnValue = null, string $emptyReturnStatementMessage, string $voidMessage, string $typeMismatchMessage) : array
 {
     if ($returnValue === null) {
         if ($returnType instanceof VoidType || $returnType instanceof MixedType) {
             return [];
         }
         return [sprintf($emptyReturnStatementMessage, $returnType->describe())];
     }
     $returnValueType = $scope->getType($returnValue);
     if ($returnType instanceof VoidType) {
         return [sprintf($voidMessage, $returnValueType->describe())];
     }
     if (!$returnType->accepts($returnValueType)) {
         return [sprintf($typeMismatchMessage, $returnType->describe(), $returnValueType->describe())];
     }
     return [];
 }
예제 #13
0
 /**
  * @param \PhpParser\Node\Expr\Assign $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(\PhpParser\Node $node, Scope $scope) : array
 {
     if (!$node->var instanceof ArrayDimFetch) {
         return [];
     }
     $assignedToType = $scope->getType($node->var->var);
     if (!$assignedToType instanceof ArrayType) {
         return [];
     }
     if ($assignedToType->isItemTypeInferredFromLiteralArray()) {
         return [];
     }
     $assignedValueType = $scope->getType($node->expr);
     if (!$assignedToType->getItemType()->accepts($assignedValueType)) {
         return [sprintf('Array (%s) does not accept %s.', $assignedToType->describe(), $assignedValueType->describe())];
     }
     return [];
 }
 /**
  * @param \PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\StaticPropertyFetch $propertyFetch
  * @param \PHPStan\Analyser\Scope $scope
  * @return string|null
  */
 private function describeProperty($propertyFetch, Scope $scope)
 {
     if ($propertyFetch instanceof Node\Expr\PropertyFetch) {
         if (!is_string($propertyFetch->name)) {
             return null;
         }
         $propertyHolderType = $scope->getType($propertyFetch->var);
         if ($propertyHolderType->getClass() === null) {
             return null;
         }
         return sprintf('Property %s::$%s', $propertyHolderType->getClass(), $propertyFetch->name);
     } elseif ($propertyFetch instanceof Node\Expr\StaticPropertyFetch) {
         if (!$propertyFetch->class instanceof Node\Name || !is_string($propertyFetch->name)) {
             return null;
         }
         return sprintf('Static property %s::$%s', $scope->resolveName($propertyFetch->class), $propertyFetch->name);
     }
     return null;
 }
예제 #15
0
 /**
  * @param \PhpParser\Node\Expr\ClassConstFetch $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     $class = $node->class;
     if ($class instanceof \PhpParser\Node\Name) {
         $className = (string) $class;
     } else {
         $classType = $scope->getType($class);
         if ($classType->getClass() !== null) {
             $className = $classType->getClass();
         } else {
             return [];
         }
     }
     if ($className === 'self' || $className === 'static') {
         if ($scope->getClass() === null && !$scope->isInAnonymousClass()) {
             return [sprintf('Using %s outside of class scope.', $className)];
         }
         if ($className === 'static') {
             return [];
         }
         if ($className === 'self') {
             $className = $scope->getClass();
         }
     }
     $constantName = $node->name;
     if ($scope->getClass() !== null && $className === 'parent') {
         $currentClassReflection = $this->broker->getClass($scope->getClass());
         if ($currentClassReflection->getParentClass() === false) {
             return [sprintf('Access to parent::%s but %s does not extend any class.', $constantName, $scope->getClass())];
         }
         $className = $currentClassReflection->getParentClass()->getName();
     }
     if (!$this->broker->hasClass($className)) {
         return [sprintf('Class %s not found.', $className)];
     }
     if ($constantName === 'class') {
         return [];
     }
     $classReflection = $this->broker->getClass($className);
     if (!$classReflection->hasConstant($constantName)) {
         return [sprintf('Access to undefined constant %s::%s.', $classReflection->getName(), $constantName)];
     }
     $constantReflection = $classReflection->getConstant($constantName);
     if (!$scope->canAccessConstant($constantReflection)) {
         return [sprintf('Cannot access constant %s::%s from current scope.', $constantReflection->getDeclaringClass()->getName(), $constantName)];
     }
     return [];
 }
예제 #16
0
 /**
  * @param \PhpParser\Node\Expr\StaticPropertyFetch $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!is_string($node->name) || !$node->class instanceof Node\Name) {
         return [];
     }
     $name = $node->name;
     $currentClass = $scope->getClass();
     if ($currentClass === null) {
         return [];
     }
     $currentClassReflection = $this->broker->getClass($currentClass);
     $class = (string) $node->class;
     if ($class === 'self' || $class === 'static') {
         $class = $currentClass;
     }
     if ($class === 'parent') {
         if ($currentClassReflection->getParentClass() === false) {
             return [sprintf('%s::%s() accesses parent::$%s but %s does not extend any class.', $currentClass, $scope->getFunctionName(), $name, $currentClass)];
         }
         $currentMethodReflection = $currentClassReflection->getMethod($scope->getFunctionName());
         if (!$currentMethodReflection->isStatic()) {
             // calling parent::method() from instance method
             return [];
         }
         $class = $currentClassReflection->getParentClass()->getName();
     }
     if (!$this->broker->hasClass($class)) {
         return [sprintf('Access to static property $%s on an unknown class %s.', $name, $class)];
     }
     $classReflection = $this->broker->getClass($class);
     if (!$classReflection->hasProperty($name)) {
         return [sprintf('Access to an undefined static property %s::$%s.', $classReflection->getName(), $name)];
     }
     $property = $classReflection->getProperty($name, $scope);
     if (!$property->isStatic()) {
         return [sprintf('Static access to instance property %s::$%s.', $property->getDeclaringClass()->getName(), $name)];
     }
     if (!$scope->canAccessProperty($property)) {
         return [sprintf('Cannot access property %s::$%s from current scope.', $property->getDeclaringClass()->getName(), $name)];
     }
     return [];
 }
예제 #17
0
 /**
  * @param \PhpParser\Node\Expr\MethodCall $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if (!is_string($node->name)) {
         return [];
     }
     if ($this->checkThisOnly && !$this->ruleLevelHelper->isThis($node->var)) {
         return [];
     }
     $type = $scope->getType($node->var);
     if (!$type->canCallMethods()) {
         return [sprintf('Cannot call method %s() on %s.', $node->name, $type->describe())];
     }
     $methodClass = $type->getClass();
     if ($methodClass === null) {
         return [];
     }
     $name = (string) $node->name;
     if (!$this->broker->hasClass($methodClass)) {
         return [sprintf('Call to method %s() on an unknown class %s.', $name, $methodClass)];
     }
     $methodClassReflection = $this->broker->getClass($methodClass);
     if (!$methodClassReflection->hasMethod($name)) {
         $parentClassReflection = $methodClassReflection->getParentClass();
         while ($parentClassReflection !== false) {
             if ($parentClassReflection->hasMethod($name)) {
                 return [sprintf('Call to private method %s() of parent class %s.', $parentClassReflection->getMethod($name)->getName(), $parentClassReflection->getName())];
             }
             $parentClassReflection = $parentClassReflection->getParentClass();
         }
         return [sprintf('Call to an undefined method %s::%s().', $methodClassReflection->getName(), $name)];
     }
     $methodReflection = $methodClassReflection->getMethod($name);
     $messagesMethodName = $methodReflection->getDeclaringClass()->getName() . '::' . $methodReflection->getName() . '()';
     if (!$scope->canCallMethod($methodReflection)) {
         return [sprintf('Cannot call method %s from current scope.', $messagesMethodName)];
     }
     $errors = $this->check->check($methodReflection, $node, ['Method ' . $messagesMethodName . ' invoked with %d parameter, %d required.', 'Method ' . $messagesMethodName . ' invoked with %d parameters, %d required.', 'Method ' . $messagesMethodName . ' invoked with %d parameter, at least %d required.', 'Method ' . $messagesMethodName . ' invoked with %d parameters, at least %d required.', 'Method ' . $messagesMethodName . ' invoked with %d parameter, %d-%d required.', 'Method ' . $messagesMethodName . ' invoked with %d parameters, %d-%d required.']);
     if ($methodReflection->getName() !== $name) {
         $errors[] = sprintf('Call to method %s with incorrect case: %s', $messagesMethodName, $name);
     }
     return $errors;
 }
예제 #18
0
 /**
  * @param \PhpParser\Node\Expr\PropertyFetch $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(\PhpParser\Node $node, Scope $scope) : array
 {
     if (!is_string($node->name)) {
         return [];
     }
     if ($this->checkThisOnly && !$this->ruleLevelHelper->isThis($node->var)) {
         return [];
     }
     $type = $scope->getType($node->var);
     if (!$type->canAccessProperties()) {
         return [sprintf('Cannot access property $%s on %s.', $node->name, $type->describe())];
     }
     $propertyClass = $type->getClass();
     if ($propertyClass === null) {
         return [];
     }
     $name = (string) $node->name;
     if (!$this->broker->hasClass($propertyClass)) {
         return [sprintf('Access to property $%s on an unknown class %s.', $name, $propertyClass)];
     }
     $propertyClassReflection = $this->broker->getClass($propertyClass);
     if (!$propertyClassReflection->hasProperty($name)) {
         if ($scope->isSpecified($node)) {
             return [];
         }
         $parentClassReflection = $propertyClassReflection->getParentClass();
         while ($parentClassReflection !== false) {
             if ($parentClassReflection->hasProperty($name)) {
                 return [sprintf('Access to private property $%s of parent class %s.', $name, $parentClassReflection->getName())];
             }
             $parentClassReflection = $parentClassReflection->getParentClass();
         }
         return [sprintf('Access to an undefined property %s::$%s.', $propertyClass, $name)];
     }
     $propertyReflection = $propertyClassReflection->getProperty($name, $scope);
     if (!$scope->canAccessProperty($propertyReflection)) {
         return [sprintf('Cannot access property %s::$%s from current scope.', $propertyReflection->getDeclaringClass()->getName(), $name)];
     }
     return [];
 }
 /**
  * @param \PhpParser\Node\Stmt\PropertyProperty $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     $className = $scope->getClass();
     if ($className === null) {
         return [];
     }
     $classReflection = $this->broker->getClass($className);
     $propertyType = $classReflection->getProperty($node->name, $scope)->getType();
     if ($propertyType instanceof ArrayType) {
         $nestedItemType = $propertyType->getNestedItemType();
         if ($nestedItemType->getItemType()->getClass() !== null && !$this->broker->hasClass($nestedItemType->getItemType()->getClass())) {
             return [sprintf('Property %s::$%s has unknown class %s as its array type.', $className, $node->name, $propertyType->describe())];
         }
     }
     if ($propertyType->getClass() === null) {
         return [];
     }
     if (!$this->broker->hasClass($propertyType->getClass())) {
         return [sprintf('Property %s::$%s has unknown class %s as its type.', $className, $node->name, $propertyType->getClass())];
     }
     return [];
 }
 /**
  * @param \PhpParser\Node\Expr\Instanceof_ $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     $class = $node->class;
     if (!$class instanceof \PhpParser\Node\Name) {
         return [];
     }
     $name = (string) $class;
     if ($name === 'self' || $name === 'static') {
         if ($scope->getClass() === null && !$scope->isInAnonymousClass()) {
             return [sprintf('Using %s outside of class scope.', $name)];
         }
         if ($name === 'static') {
             return [];
         }
         if ($name === 'self') {
             $name = $scope->getClass();
         }
     }
     if (!$this->broker->hasClass($name)) {
         return [sprintf('Class %s not found.', $name)];
     }
     return [];
 }
예제 #21
0
 /**
  * @param \PhpParser\Node\Expr\ClassConstFetch $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     $class = $node->class;
     if ($class instanceof \PhpParser\Node\Name) {
         $className = (string) $class;
     } else {
         $classType = $scope->getType($class);
         if ($classType->getClass() !== null) {
             $className = $classType->getClass();
         } else {
             return [];
         }
     }
     if ($className === 'self' || $className === 'static') {
         if ($scope->getClass() === null && !$scope->isInAnonymousClass()) {
             return [sprintf('Using %s outside of class scope.', $className)];
         }
         if ($className === 'static') {
             return [];
         }
         if ($className === 'self') {
             $className = $scope->getClass();
         }
     }
     if (!$this->broker->hasClass($className)) {
         return [sprintf('Class %s not found.', $className)];
     }
     $constantName = $node->name;
     if ($constantName === 'class') {
         return [];
     }
     $classReflection = $this->broker->getClass($className);
     if (!$classReflection->hasConstant($constantName)) {
         return [sprintf('Access to undefined constant %s::%s.', $className, $constantName)];
     }
     return [];
 }
예제 #22
0
 public function getProperty(string $propertyName, Scope $scope = null) : PropertyReflection
 {
     if (!isset($this->properties[$propertyName])) {
         $privateProperty = null;
         $publicProperty = null;
         foreach ($this->propertiesClassReflectionExtensions as $extension) {
             if ($extension->hasProperty($this, $propertyName)) {
                 $property = $extension->getProperty($this, $propertyName);
                 if ($scope !== null && $scope->canAccessProperty($property)) {
                     return $this->properties[$propertyName] = $property;
                 }
                 $this->properties[$propertyName] = $property;
             }
         }
     }
     if (!isset($this->properties[$propertyName])) {
         throw new \PHPStan\Reflection\MissingPropertyFromReflectionException($this->getName(), $propertyName);
     }
     return $this->properties[$propertyName];
 }
 /**
  * @param \PhpParser\Node\Stmt\Property $node
  * @param \PHPStan\Analyser\Scope $scope
  * @return string[]
  */
 public function processNode(Node $node, Scope $scope) : array
 {
     if ($scope->getClass() === null || !$this->broker->hasClass($scope->getClass())) {
         return [];
     }
     $classReflection = $this->broker->getClass($scope->getClass());
     $errors = [];
     foreach ($node->props as $property) {
         if ($property->default === null) {
             continue;
         }
         $propertyReflection = $classReflection->getProperty($property->name);
         $propertyType = $propertyReflection->getType();
         $defaultValueType = $scope->getType($property->default);
         if ($propertyType->accepts($defaultValueType)) {
             continue;
         }
         $errors[] = sprintf('%s %s::$%s (%s) does not accept default value of type %s.', $node->isStatic() ? 'Static property' : 'Property', $scope->getClass(), $property->name, $propertyType->describe(), $defaultValueType->describe());
     }
     return $errors;
 }
예제 #24
0
파일: Broker.php 프로젝트: phpstan/phpstan
 /**
  * @param \PhpParser\Node\Name $nameNode
  * @param \PHPStan\Analyser\Scope|null $scope
  * @return string|null
  */
 public function resolveFunctionName(\PhpParser\Node\Name $nameNode, Scope $scope = null)
 {
     $name = (string) $nameNode;
     if ($scope !== null && $scope->getNamespace() !== null && !$nameNode->isFullyQualified()) {
         $namespacedName = sprintf('%s\\%s', $scope->getNamespace(), $name);
         if (function_exists($namespacedName)) {
             return $namespacedName;
         }
     }
     if (function_exists($name)) {
         return $name;
     }
     return null;
 }
예제 #25
0
 public function specifyTypesInCondition(SpecifiedTypes $types, Scope $scope, Node $expr, bool $negated = false, int $source = self::SOURCE_UNKNOWN) : SpecifiedTypes
 {
     if ($expr instanceof Instanceof_ && $expr->class instanceof Name) {
         $class = (string) $expr->class;
         if ($class === 'static') {
             return $types;
         }
         if ($class === 'self' && $scope->getClass() !== null) {
             $class = $scope->getClass();
         }
         $printedExpr = $this->printer->prettyPrintExpr($expr->expr);
         $objectType = new ObjectType($class, false);
         if ($negated) {
             if ($source === self::SOURCE_FROM_AND) {
                 return $types;
             }
             return $types->addSureNotType($expr->expr, $printedExpr, $objectType);
         }
         return $types->addSureType($expr->expr, $printedExpr, $objectType);
     } elseif ($expr instanceof FuncCall && $expr->name instanceof Name && isset($expr->args[0])) {
         $functionName = (string) $expr->name;
         $argumentExpression = $expr->args[0]->value;
         $specifiedType = null;
         if (in_array($functionName, ['is_int', 'is_integer', 'is_long'], true)) {
             $specifiedType = new IntegerType(false);
         } elseif (in_array($functionName, ['is_float', 'is_double', 'is_real'], true)) {
             $specifiedType = new FloatType(false);
         } elseif ($functionName === 'is_null') {
             $specifiedType = new NullType();
         } elseif ($functionName === 'is_array') {
             $specifiedType = new ArrayType(new MixedType(true), false);
         } elseif ($functionName === 'is_bool') {
             $specifiedType = new BooleanType(false);
         } elseif ($functionName === 'is_callable') {
             $specifiedType = new CallableType(false);
         } elseif ($functionName === 'is_resource') {
             $specifiedType = new ResourceType(false);
         } elseif ($functionName === 'is_iterable') {
             $specifiedType = new IterableIterableType(new MixedType(true), false);
         }
         if ($specifiedType !== null) {
             $printedExpr = $this->printer->prettyPrintExpr($argumentExpression);
             if ($negated) {
                 return $types->addSureNotType($argumentExpression, $printedExpr, $specifiedType);
             }
             return $types->addSureType($argumentExpression, $printedExpr, $specifiedType);
         }
     } elseif ($expr instanceof BooleanAnd) {
         if ($source !== self::SOURCE_UNKNOWN && $source !== self::SOURCE_FROM_AND) {
             return $types;
         }
         $types = $this->specifyTypesInCondition($types, $scope, $expr->left, $negated, self::SOURCE_FROM_AND);
         $types = $this->specifyTypesInCondition($types, $scope, $expr->right, $negated, self::SOURCE_FROM_AND);
     } elseif ($expr instanceof BooleanOr) {
         if ($negated) {
             return $types;
         }
         $types = $this->specifyTypesInCondition($types, $scope, $expr->left, $negated, self::SOURCE_FROM_OR);
         $types = $this->specifyTypesInCondition($types, $scope, $expr->right, $negated, self::SOURCE_FROM_OR);
     } elseif ($expr instanceof Node\Expr\BooleanNot) {
         if ($source === self::SOURCE_FROM_AND) {
             return $types;
         }
         $types = $this->specifyTypesInCondition($types, $scope, $expr->expr, !$negated, $source);
     }
     return $types;
 }
예제 #26
0
 private function hasStatementEarlyTermination(Node $statement, Scope $scope) : bool
 {
     if ($statement instanceof Throw_ || $statement instanceof Return_ || $statement instanceof Continue_ || $statement instanceof Break_ || $statement instanceof Exit_) {
         return true;
     } elseif ($statement instanceof MethodCall && count($this->earlyTerminatingMethodCalls) > 0) {
         if (!is_string($statement->name)) {
             return false;
         }
         $methodCalledOnType = $scope->getType($statement->var);
         if ($methodCalledOnType->getClass() === null) {
             return false;
         }
         if (!$this->broker->hasClass($methodCalledOnType->getClass())) {
             return false;
         }
         $classReflection = $this->broker->getClass($methodCalledOnType->getClass());
         foreach (array_merge([$methodCalledOnType->getClass()], $classReflection->getParentClassesNames()) as $className) {
             if (!isset($this->earlyTerminatingMethodCalls[$className])) {
                 continue;
             }
             return in_array($statement->name, $this->earlyTerminatingMethodCalls[$className], true);
         }
         return false;
     }
     return false;
 }
예제 #27
0
 private function enterClassMethod(Scope $scope, Node\Stmt\ClassMethod $classMethod) : Scope
 {
     $fileTypeMap = $this->fileTypeMapper->getTypeMap($scope->getFile());
     $phpDocParameterTypes = [];
     $phpDocReturnType = null;
     if ($classMethod->getDocComment() !== null) {
         $docComment = $classMethod->getDocComment()->getText();
         $phpDocParameterTypes = TypehintHelper::getPhpDocParameterTypesFromMethod($fileTypeMap, array_map(function (Param $parameter) : string {
             return $parameter->name;
         }, $classMethod->params), $docComment);
         $phpDocReturnType = TypehintHelper::getPhpDocReturnTypeFromMethod($fileTypeMap, $docComment);
     }
     return $scope->enterClassMethod($classMethod, $phpDocParameterTypes, $phpDocReturnType);
 }
예제 #28
0
파일: Scope.php 프로젝트: phpstan/phpstan
 public function addVariables(Scope $otherScope) : self
 {
     $variableTypes = $this->getVariableTypes();
     foreach ($otherScope->getVariableTypes() as $name => $variableType) {
         if ($this->hasVariableType($name)) {
             $variableType = $this->getVariableType($name)->combineWith($variableType);
         }
         $variableTypes[$name] = $variableType;
     }
     return new self($this->broker, $this->printer, $this->getFile(), $this->getAnalysedContextFile(), $this->isDeclareStrictTypes(), $this->getClass(), $this->getFunction(), $this->getNamespace(), $variableTypes, $this->inClosureBindScopeClass, $this->getAnonymousFunctionReturnType(), $this->isInAnonymousClass() ? $this->getAnonymousClass() : null, $this->getInFunctionCall(), $this->isNegated(), $this->moreSpecificTypes);
 }