diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 5f301be474..0b8974823e 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -2500,16 +2500,13 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu private function getDynamicFunctionReturnType(FuncCall $normalizedNode, FunctionReflection $functionReflection): ?Type { - foreach ($this->dynamicReturnTypeExtensionRegistry->getDynamicFunctionReturnTypeExtensions() as $dynamicFunctionReturnTypeExtension) { - if (!$dynamicFunctionReturnTypeExtension->isFunctionSupported($functionReflection)) { - continue; - } - + foreach ($this->dynamicReturnTypeExtensionRegistry->getDynamicFunctionReturnTypeExtensions($functionReflection) as $dynamicFunctionReturnTypeExtension) { $resolvedType = $dynamicFunctionReturnTypeExtension->getTypeFromFunctionCall( $functionReflection, $normalizedNode, $this, ); + if ($resolvedType !== null) { return $resolvedType; } diff --git a/src/Type/DynamicReturnTypeExtensionRegistry.php b/src/Type/DynamicReturnTypeExtensionRegistry.php index e2a30437b3..bceef62997 100644 --- a/src/Type/DynamicReturnTypeExtensionRegistry.php +++ b/src/Type/DynamicReturnTypeExtensionRegistry.php @@ -2,6 +2,7 @@ namespace PHPStan\Type; +use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\ReflectionProvider; use function array_merge; use function strtolower; @@ -15,6 +16,9 @@ final class DynamicReturnTypeExtensionRegistry /** @var DynamicStaticMethodReturnTypeExtension[][]|null */ private ?array $dynamicStaticMethodReturnTypeExtensionsByClass = null; + /** @var array> */ + private array $dynamicReturnTypeExtensionsByFunction = []; + /** * @param DynamicMethodReturnTypeExtension[] $dynamicMethodReturnTypeExtensions * @param DynamicStaticMethodReturnTypeExtension[] $dynamicStaticMethodReturnTypeExtensions @@ -88,9 +92,23 @@ private function getDynamicExtensionsForType(array $extensions, string $classNam /** * @return DynamicFunctionReturnTypeExtension[] */ - public function getDynamicFunctionReturnTypeExtensions(): array + public function getDynamicFunctionReturnTypeExtensions(FunctionReflection $functionReflection): array { - return $this->dynamicFunctionReturnTypeExtensions; + $functionName = $functionReflection->getName(); + if (isset($this->dynamicReturnTypeExtensionsByFunction[$functionName])) { + return $this->dynamicReturnTypeExtensionsByFunction[$functionName]; + } + + $supportedFunctions = []; + foreach ($this->dynamicFunctionReturnTypeExtensions as $dynamicFunctionReturnTypeExtension) { + if (!$dynamicFunctionReturnTypeExtension->isFunctionSupported($functionReflection)) { + continue; + } + + $supportedFunctions[] = $dynamicFunctionReturnTypeExtension; + } + + return $this->dynamicReturnTypeExtensionsByFunction[$functionName] = $supportedFunctions; } }