diff --git a/src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php b/src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php index 1da17b48d8..797ee59bb4 100644 --- a/src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php +++ b/src/Type/Php/ReplaceFunctionsDynamicReturnTypeExtension.php @@ -52,15 +52,7 @@ public function getTypeFromFunctionCall( { $type = $this->getPreliminarilyResolvedTypeFromFunctionCall($functionReflection, $functionCall, $scope); - $possibleTypes = ParametersAcceptorSelector::selectFromArgs( - $scope, - $functionCall->getArgs(), - $functionReflection->getVariants(), - )->getReturnType(); - // resolve conditional return types - $possibleTypes = TypeUtils::resolveLateResolvableTypes($possibleTypes); - - if (TypeCombinator::containsNull($possibleTypes)) { + if ($this->canReturnNull($functionReflection, $functionCall, $scope)) { $type = TypeCombinator::addNull($type); } @@ -73,18 +65,17 @@ private function getPreliminarilyResolvedTypeFromFunctionCall( Scope $scope, ): Type { - $argumentPosition = self::FUNCTIONS_SUBJECT_POSITION[$functionReflection->getName()]; + $subjectArgumentType = $this->getSubjectType($functionReflection, $functionCall, $scope); $defaultReturnType = ParametersAcceptorSelector::selectFromArgs( $scope, $functionCall->getArgs(), $functionReflection->getVariants(), )->getReturnType(); - if (count($functionCall->getArgs()) <= $argumentPosition) { + if ($subjectArgumentType === null) { return $defaultReturnType; } - $subjectArgumentType = $scope->getType($functionCall->getArgs()[$argumentPosition]->value); if ($subjectArgumentType instanceof MixedType) { return TypeUtils::toBenevolentUnion($defaultReturnType); } @@ -124,4 +115,35 @@ private function getPreliminarilyResolvedTypeFromFunctionCall( return $defaultReturnType; } + private function getSubjectType( + FunctionReflection $functionReflection, + FuncCall $functionCall, + Scope $scope, + ): ?Type + { + $argumentPosition = self::FUNCTIONS_SUBJECT_POSITION[$functionReflection->getName()]; + if (count($functionCall->getArgs()) <= $argumentPosition) { + return null; + } + return $scope->getType($functionCall->getArgs()[$argumentPosition]->value); + } + + private function canReturnNull( + FunctionReflection $functionReflection, + FuncCall $functionCall, + Scope $scope, + ): bool + { + $possibleTypes = ParametersAcceptorSelector::selectFromArgs( + $scope, + $functionCall->getArgs(), + $functionReflection->getVariants(), + )->getReturnType(); + + // resolve conditional return types + $possibleTypes = TypeUtils::resolveLateResolvableTypes($possibleTypes); + + return TypeCombinator::containsNull($possibleTypes); + } + }