diff --git a/src/Type/Php/ParseUrlFunctionDynamicReturnTypeExtension.php b/src/Type/Php/ParseUrlFunctionDynamicReturnTypeExtension.php index a3e2ec18b80..2a3d43c9c01 100644 --- a/src/Type/Php/ParseUrlFunctionDynamicReturnTypeExtension.php +++ b/src/Type/Php/ParseUrlFunctionDynamicReturnTypeExtension.php @@ -86,7 +86,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, } if ($componentType->getValue() === -1) { - return $this->createAllComponentsReturnType(); + return TypeCombinator::union($this->createComponentsArray(), new ConstantBooleanType(false)); } return $this->componentTypesPairedConstants[$componentType->getValue()] ?? new ConstantBooleanType(false); @@ -97,24 +97,31 @@ private function createAllComponentsReturnType(): Type if ($this->allComponentsTogetherType === null) { $returnTypes = [ new ConstantBooleanType(false), + new NullType(), + IntegerRangeType::fromInterval(0, 65535), + new StringType(), + $this->createComponentsArray(), ]; - $builder = ConstantArrayTypeBuilder::createEmpty(); + $this->allComponentsTogetherType = TypeCombinator::union(...$returnTypes); + } - if ($this->componentTypesPairedStrings === null) { - throw new ShouldNotHappenException(); - } + return $this->allComponentsTogetherType; + } - foreach ($this->componentTypesPairedStrings as $componentName => $componentValueType) { - $builder->setOffsetValueType(new ConstantStringType($componentName), $componentValueType, true); - } + private function createComponentsArray(): Type + { + $builder = ConstantArrayTypeBuilder::createEmpty(); - $returnTypes[] = $builder->getArray(); + if ($this->componentTypesPairedStrings === null) { + throw new ShouldNotHappenException(); + } - $this->allComponentsTogetherType = TypeCombinator::union(...$returnTypes); + foreach ($this->componentTypesPairedStrings as $componentName => $componentValueType) { + $builder->setOffsetValueType(new ConstantStringType($componentName), $componentValueType, true); } - return $this->allComponentsTogetherType; + return $builder->getArray(); } private function cacheReturnTypes(): void diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index d436aee9d21..3661f1908ba 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1431,6 +1431,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/assert-inheritance.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9123.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10037.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4754.php'); } /** diff --git a/tests/PHPStan/Analyser/data/bug-4754.php b/tests/PHPStan/Analyser/data/bug-4754.php new file mode 100644 index 00000000000..ffa98ee0f4a --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-4754.php @@ -0,0 +1,42 @@ +, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|false', $parsedComponentNotSpecified); + assertType('array{scheme?: string, host?: string, port?: int<0, 65535>, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|int<0, 65535>|string|false|null', $parsedNotConstant); + assertType('array{scheme?: string, host?: string, port?: int<0, 65535>, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|false', $parsedAllConstant); + assertType('string|false|null', $parsedSchemeConstant); + assertType('string|false|null', $parsedHostConstant); + assertType('int<0, 65535>|false|null', $parsedPortConstant); + assertType('string|false|null', $parsedUserConstant); + assertType('string|false|null', $parsedPassConstant); + assertType('string|false|null', $parsedPathConstant); + assertType('string|false|null', $parsedQueryConstant); + assertType('string|false|null', $parsedFragmentConstant); +}