diff --git a/src/Reflection/ParametersAcceptorSelector.php b/src/Reflection/ParametersAcceptorSelector.php index 6b47480d73..bc60c25c2d 100644 --- a/src/Reflection/ParametersAcceptorSelector.php +++ b/src/Reflection/ParametersAcceptorSelector.php @@ -50,6 +50,7 @@ use function constant; use function count; use function defined; +use function is_int; use function is_string; use function sprintf; use const ARRAY_FILTER_USE_BOTH; @@ -126,33 +127,46 @@ public static function selectFromArgs( if (count($args) >= 3 && (bool) $args[0]->getAttribute(CurlSetOptArgVisitor::ATTRIBUTE_NAME)) { $optType = $scope->getType($args[1]->value); - if ($optType instanceof ConstantIntegerType) { - $optValueType = self::getCurlOptValueType($optType->getValue()); - - if ($optValueType !== null) { - $acceptor = $parametersAcceptors[0]; - $parameters = $acceptor->getParameters(); - - $parameters[2] = new NativeParameterReflection( - $parameters[2]->getName(), - $parameters[2]->isOptional(), - $optValueType, - $parameters[2]->passedByReference(), - $parameters[2]->isVariadic(), - $parameters[2]->getDefaultValue(), - ); - - $parametersAcceptors = [ - new FunctionVariant( - $acceptor->getTemplateTypeMap(), - $acceptor->getResolvedTemplateTypeMap(), - array_values($parameters), - $acceptor->isVariadic(), - $acceptor->getReturnType(), - $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), - ), - ]; + + $valueTypes = []; + foreach ($optType->getConstantScalarValues() as $scalarValue) { + if (!is_int($scalarValue)) { + $valueTypes = []; + break; + } + + $valueType = self::getCurlOptValueType($scalarValue); + if ($valueType === null) { + $valueTypes = []; + break; } + + $valueTypes[] = $valueType; + } + + if (count($valueTypes) !== 0) { + $acceptor = $parametersAcceptors[0]; + $parameters = $acceptor->getParameters(); + + $parameters[2] = new NativeParameterReflection( + $parameters[2]->getName(), + $parameters[2]->isOptional(), + TypeCombinator::union(...$valueTypes), + $parameters[2]->passedByReference(), + $parameters[2]->isVariadic(), + $parameters[2]->getDefaultValue(), + ); + + $parametersAcceptors = [ + new FunctionVariant( + $acceptor->getTemplateTypeMap(), + $acceptor->getResolvedTemplateTypeMap(), + array_values($parameters), + $acceptor->isVariadic(), + $acceptor->getReturnType(), + $acceptor instanceof ExtendedParametersAcceptor ? $acceptor->getCallSiteVarianceMap() : TemplateTypeVarianceMap::createEmpty(), + ), + ]; } } diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index c3dc4df606..f49cc7f15c 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1375,6 +1375,10 @@ public function testCurlSetOpt(): void 'Parameter #3 $value of function curl_setopt expects array, array given.', 77, ], + [ + 'Parameter #3 $value of function curl_setopt expects bool|int, int|string given.', + 96, + ], ]); } diff --git a/tests/PHPStan/Rules/Functions/data/curl_setopt.php b/tests/PHPStan/Rules/Functions/data/curl_setopt.php index bc5b8f6cea..7cff79c7ac 100644 --- a/tests/PHPStan/Rules/Functions/data/curl_setopt.php +++ b/tests/PHPStan/Rules/Functions/data/curl_setopt.php @@ -81,4 +81,18 @@ public function bug9263() { ]; curl_setopt($curl, CURLOPT_HTTPHEADER, $header_list); } + + public function unionType() { + $curl = curl_init(); + + if (rand(0,1)) { + $var = CURLOPT_AUTOREFERER; + $value = 'yes'; // invalid, should be bool + } else { + $var = CURLOPT_TIMEOUT; + $value = 1; + } + + curl_setopt($curl, $var, $value); + } }