PHPStan\Broker\Broker::getDynamicMethodReturnTypeExtensionsForClass PHP Method

getDynamicMethodReturnTypeExtensionsForClass() public method

public getDynamicMethodReturnTypeExtensionsForClass ( string $className ) : array
$className string
return array
    public function getDynamicMethodReturnTypeExtensionsForClass(string $className) : array
    {
        $extensions = [];
        $class = $this->getClass($className);
        foreach (array_merge([$className], $class->getParentClassesNames()) as $extensionClassName) {
            if (!isset($this->dynamicMethodReturnTypeExtensions[$extensionClassName])) {
                continue;
            }
            $extensions = array_merge($extensions, $this->dynamicMethodReturnTypeExtensions[$extensionClassName]);
        }
        return $extensions;
    }

Usage Example

Beispiel #1
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);
 }