Skip to content

Commit 1ebcae0

Browse files
committed
Fixes parse_url return type when $component is -1 returning more types than were valid. Adds missing types when $component is not a constant value.
1 parent 5422c7a commit 1ebcae0

File tree

4 files changed

+62
-12
lines changed

4 files changed

+62
-12
lines changed

src/Type/Php/ParseUrlFunctionDynamicReturnTypeExtension.php

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
8686
}
8787

8888
if ($componentType->getValue() === -1) {
89-
return $this->createAllComponentsReturnType();
89+
return TypeCombinator::union($this->createComponentsArray(), new ConstantBooleanType(false));
9090
}
9191

9292
return $this->componentTypesPairedConstants[$componentType->getValue()] ?? new ConstantBooleanType(false);
@@ -97,24 +97,31 @@ private function createAllComponentsReturnType(): Type
9797
if ($this->allComponentsTogetherType === null) {
9898
$returnTypes = [
9999
new ConstantBooleanType(false),
100+
new NullType(),
101+
IntegerRangeType::fromInterval(0, 65535),
102+
new StringType(),
103+
$this->createComponentsArray(),
100104
];
101105

102-
$builder = ConstantArrayTypeBuilder::createEmpty();
106+
$this->allComponentsTogetherType = TypeCombinator::union(...$returnTypes);
107+
}
103108

104-
if ($this->componentTypesPairedStrings === null) {
105-
throw new ShouldNotHappenException();
106-
}
109+
return $this->allComponentsTogetherType;
110+
}
107111

108-
foreach ($this->componentTypesPairedStrings as $componentName => $componentValueType) {
109-
$builder->setOffsetValueType(new ConstantStringType($componentName), $componentValueType, true);
110-
}
112+
private function createComponentsArray(): Type
113+
{
114+
$builder = ConstantArrayTypeBuilder::createEmpty();
111115

112-
$returnTypes[] = $builder->getArray();
116+
if ($this->componentTypesPairedStrings === null) {
117+
throw new ShouldNotHappenException();
118+
}
113119

114-
$this->allComponentsTogetherType = TypeCombinator::union(...$returnTypes);
120+
foreach ($this->componentTypesPairedStrings as $componentName => $componentValueType) {
121+
$builder->setOffsetValueType(new ConstantStringType($componentName), $componentValueType, true);
115122
}
116123

117-
return $this->allComponentsTogetherType;
124+
return $builder->getArray();
118125
}
119126

120127
private function cacheReturnTypes(): void

tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5500,7 +5500,7 @@ public function dataFunctions(): array
55005500
'$parseUrlConstantUrlWithoutComponent2',
55015501
],
55025502
[
5503-
'array{scheme?: string, host?: string, port?: int<0, 65535>, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|false',
5503+
'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',
55045504
'$parseUrlConstantUrlUnknownComponent',
55055505
],
55065506
[

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,7 @@ public function dataFileAsserts(): iterable
14851485
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10952b.php');
14861486
yield from $this->gatherAssertTypes(__DIR__ . '/data/case-insensitive-parent.php');
14871487
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10893.php');
1488+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4754.php');
14881489
}
14891490

14901491
/**
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Bug4754;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* The standard PHP parse_url() function doesn't work with relative URI's only absolute URL's
9+
* so this function works around that by adding a http://domain prefix
10+
* if needed and passing through the results
11+
*
12+
* @param string $url
13+
* @param int $component
14+
*
15+
* @return string|int|array|bool|null
16+
*/
17+
function parseUrl($url, $component = -1)
18+
{
19+
$parsedComponentNotSpecified = parse_url($url);
20+
$parsedNotConstant = parse_url($url, $component);
21+
$parsedAllConstant = parse_url($url, -1);
22+
$parsedSchemeConstant = parse_url($url, PHP_URL_SCHEME);
23+
$parsedHostConstant = parse_url($url, PHP_URL_HOST);
24+
$parsedPortConstant = parse_url($url, PHP_URL_PORT);
25+
$parsedUserConstant = parse_url($url, PHP_URL_USER);
26+
$parsedPassConstant = parse_url($url, PHP_URL_PASS);
27+
$parsedPathConstant = parse_url($url, PHP_URL_PATH);
28+
$parsedQueryConstant = parse_url($url, PHP_URL_QUERY);
29+
$parsedFragmentConstant = parse_url($url, PHP_URL_FRAGMENT);
30+
31+
assertType('array{scheme?: string, host?: string, port?: int<0, 65535>, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|false', $parsedComponentNotSpecified);
32+
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);
33+
assertType('array{scheme?: string, host?: string, port?: int<0, 65535>, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|false', $parsedAllConstant);
34+
assertType('string|false|null', $parsedSchemeConstant);
35+
assertType('string|false|null', $parsedHostConstant);
36+
assertType('int<0, 65535>|false|null', $parsedPortConstant);
37+
assertType('string|false|null', $parsedUserConstant);
38+
assertType('string|false|null', $parsedPassConstant);
39+
assertType('string|false|null', $parsedPathConstant);
40+
assertType('string|false|null', $parsedQueryConstant);
41+
assertType('string|false|null', $parsedFragmentConstant);
42+
}

0 commit comments

Comments
 (0)