Skip to content

Commit ebd50a3

Browse files
committed
add extension files from @VincentLanglet
Copied from 22f0cab and 38151b7
1 parent d4056d1 commit ebd50a3

File tree

5 files changed

+123
-28
lines changed

5 files changed

+123
-28
lines changed

conf/config.neon

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,11 @@ services:
15641564
tags:
15651565
- phpstan.dynamicFunctionThrowTypeExtension
15661566

1567+
-
1568+
class: PHPStan\Type\Php\ParseStrParameterOutTypeExtension
1569+
tags:
1570+
- phpstan.functionParameterOutTypeExtension
1571+
15671572
-
15681573
class: PHPStan\Type\Php\PregMatchTypeSpecifyingExtension
15691574
tags:
@@ -1774,6 +1779,11 @@ services:
17741779
tags:
17751780
- phpstan.broker.dynamicFunctionReturnTypeExtension
17761781

1782+
-
1783+
class: PHPStan\Type\Php\TrimFunctionDynamicReturnTypeExtension
1784+
tags:
1785+
- phpstan.broker.dynamicFunctionReturnTypeExtension
1786+
17771787
-
17781788
class: PHPStan\Type\Php\VersionCompareFunctionDynamicReturnTypeExtension
17791789
tags:
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\FunctionReflection;
8+
use PHPStan\Reflection\ParameterReflection;
9+
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
10+
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
11+
use PHPStan\Type\ArrayType;
12+
use PHPStan\Type\FunctionParameterOutTypeExtension;
13+
use PHPStan\Type\IntegerType;
14+
use PHPStan\Type\IntersectionType;
15+
use PHPStan\Type\MixedType;
16+
use PHPStan\Type\StringType;
17+
use PHPStan\Type\Type;
18+
use PHPStan\Type\UnionType;
19+
20+
use function in_array;
21+
use function strtolower;
22+
23+
final class ParseStrParameterOutTypeExtension implements FunctionParameterOutTypeExtension
24+
{
25+
26+
public function isFunctionSupported(FunctionReflection $functionReflection, ParameterReflection $parameter): bool
27+
{
28+
return in_array(strtolower($functionReflection->getName()), ['parse_str', 'mb_parse_str'], true)
29+
&& $parameter->getName() === 'result';
30+
}
31+
32+
public function getParameterOutTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $funcCall, ParameterReflection $parameter, Scope $scope): ?Type
33+
{
34+
$args = $funcCall->getArgs();
35+
if (count($args) < 1) {
36+
return null;
37+
}
38+
39+
$stringType = $scope->getType($args[0]->value);
40+
$accessory = [];
41+
if ($stringType->isLowercaseString()->yes()) {
42+
$accessory[] = new AccessoryLowercaseStringType();
43+
}
44+
if ($stringType->isUppercaseString()->yes()) {
45+
$accessory[] = new AccessoryUppercaseStringType();
46+
}
47+
if (count($accessory) > 0) {
48+
$valueType = new IntersectionType([new StringType(), ...$accessory]);
49+
} else {
50+
$valueType = new StringType();
51+
}
52+
53+
return new ArrayType(
54+
new UnionType([new StringType(), new IntegerType()]),
55+
new UnionType([new ArrayType(new MixedType(), new MixedType()), $valueType]),
56+
);
57+
}
58+
59+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\FunctionReflection;
8+
use PHPStan\Type\Accessory\AccessoryLowercaseStringType;
9+
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
10+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
11+
use PHPStan\Type\IntersectionType;
12+
use PHPStan\Type\StringType;
13+
use PHPStan\Type\Type;
14+
use function count;
15+
16+
final class TrimFunctionDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
17+
{
18+
19+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
20+
{
21+
return \in_array($functionReflection->getName(), ['trim', 'rtrim', 'ltrim'], true);
22+
}
23+
24+
public function getTypeFromFunctionCall(
25+
FunctionReflection $functionReflection,
26+
FuncCall $functionCall,
27+
Scope $scope,
28+
): ?Type
29+
{
30+
$args = $functionCall->getArgs();
31+
if (count($args) < 1) {
32+
return null;
33+
}
34+
35+
$stringType = $scope->getType($args[0]->value);
36+
$accessory = [];
37+
if ($stringType->isLowercaseString()->yes()) {
38+
$accessory[] = new AccessoryLowercaseStringType();
39+
}
40+
if ($stringType->isUppercaseString()->yes()) {
41+
$accessory[] = new AccessoryUppercaseStringType();
42+
}
43+
if (count($accessory) > 0) {
44+
return new IntersectionType([new StringType(), ...$accessory]);
45+
}
46+
47+
return new StringType();
48+
}
49+
50+
}

stubs/core.stub

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,13 @@ function str_shuffle(string $string): string {}
6969

7070
/**
7171
* @param array<mixed> $result
72-
* @param-out ($string is lowercase-string&uppercase-string
73-
* ? array<int|string, array<mixed>|(lowercase-string&uppercase-string)>
74-
* : ($string is lowercase-string
75-
* ? array<int|string, array<mixed>|lowercase-string>
76-
* : ($string is uppercase-string
77-
* ? array<int|string, array<mixed>|uppercase-string>
78-
* : array<int|string, array<mixed>|string>
79-
* )
80-
* )
81-
* ) $result
72+
* @param-out array<int|string, array<mixed>|string> $result
8273
*/
8374
function parse_str(string $string, array &$result): void {}
8475

8576
/**
8677
* @param array<mixed> $result
87-
* @param-out array<string, array<mixed>|string> $result
78+
* @param-out array<int|string, array<mixed>|string> $result
8879
*/
8980
function mb_parse_str(string $string, array &$result): bool {}
9081

@@ -323,21 +314,6 @@ function is_callable(mixed $value, bool $syntax_only = false, ?string &$callable
323314
*/
324315
function abs($num) {}
325316

326-
/**
327-
* @return ($string is lowercase-string&uppercase-string ? lowercase-string&uppercase-string : ($string is lowercase-string ? lowercase-string : ($string is uppercase-string ? uppercase-string : string)))
328-
*/
329-
function trim(string $string, string $characters = " \n\r\t\v\x00"): string {}
330-
331-
/**
332-
* @return ($string is lowercase-string&uppercase-string ? lowercase-string&uppercase-string : ($string is lowercase-string ? lowercase-string : ($string is uppercase-string ? uppercase-string : string)))
333-
*/
334-
function ltrim(string $string, string $characters = " \n\r\t\v\x00"): string {}
335-
336-
/**
337-
* @return ($string is lowercase-string&uppercase-string ? lowercase-string&uppercase-string : ($string is lowercase-string ? lowercase-string : ($string is uppercase-string ? uppercase-string : string)))
338-
*/
339-
function rtrim(string $string, string $characters = " \n\r\t\v\x00"): string {}
340-
341317
/**
342318
* @return ($categorize is true ? array<string, array<string, mixed>> : array<string, mixed>)
343319
*/

tests/PHPStan/Analyser/data/param-out.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,10 @@ function fooHeadersSent() {
392392

393393
function fooMbParseStr() {
394394
mb_parse_str("foo=bar", $output);
395-
assertType('array<string, array|string>', $output);
395+
assertType('array<int|string, array|lowercase-string>', $output);
396396

397397
mb_parse_str('[email protected]&city=town&x=1&y[g]=3&f=1.23', $output);
398-
assertType('array<string, array|string>', $output);
398+
assertType('array<int|string, array|lowercase-string>', $output);
399399
}
400400

401401
function fooPreg()

0 commit comments

Comments
 (0)