PHPStan\Analyser\Scope::getType PHP Method

getType() public method

public getType ( PhpParser\Node $node ) : PHPStan\Type\Type
$node PhpParser\Node
return PHPStan\Type\Type
    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) {
                if ($this->getType($node->items[0]->value) instanceof ObjectType && $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);
                    $constants = $constantClassReflection->getNativeReflection()->getConstants();
                    if (array_key_exists($constantName, $constants)) {
                        $constantValue = $constants[$constantName];
                        $typeFromValue = $this->getTypeFromValue($constantValue);
                        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->getAnonymousFunctionType($closure->returnType, $closure->returnType === null);
                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);
    }

Usage Example

Example #1
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 [];
 }
All Usage Examples Of PHPStan\Analyser\Scope::getType