diff --git a/src/Type/Php/ParseUrlFunctionDynamicReturnTypeExtension.php b/src/Type/Php/ParseUrlFunctionDynamicReturnTypeExtension.php index a3e2ec18b8..2a3d43c9c0 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/LegacyNodeScopeResolverTest.php b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php index 94cb5f86af..97036855b1 100644 --- a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php @@ -5500,7 +5500,7 @@ public function dataFunctions(): array '$parseUrlConstantUrlWithoutComponent2', ], [ - 'array{scheme?: string, host?: string, port?: int<0, 65535>, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|false', + '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', '$parseUrlConstantUrlUnknownComponent', ], [ diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 330dfb81bd..63f60450b0 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1485,6 +1485,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10952b.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/case-insensitive-parent.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10893.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 0000000000..ffa98ee0f4 --- /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); +}