Skip to content

Commit 48c714d

Browse files
committed
feat new feat for flag
1 parent 9c33a2a commit 48c714d

File tree

2 files changed

+82
-44
lines changed

2 files changed

+82
-44
lines changed

src/Type/Php/PregSplitDynamicReturnTypeExtension.php

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?php declare(strict_types = 1);
1+
<?php declare(strict_types=1);
22

33
namespace PHPStan\Type\Php;
44

@@ -7,6 +7,7 @@
77
use PHPStan\Reflection\FunctionReflection;
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\Accessory\AccessoryArrayListType;
10+
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
1011
use PHPStan\Type\ArrayType;
1112
use PHPStan\Type\BitwiseFlagHelper;
1213
use PHPStan\Type\Constant\ConstantArrayType;
@@ -18,9 +19,16 @@
1819
use PHPStan\Type\ErrorType;
1920
use PHPStan\Type\IntegerRangeType;
2021
use PHPStan\Type\IntegerType;
22+
use PHPStan\Type\MixedType;
2123
use PHPStan\Type\StringType;
2224
use PHPStan\Type\Type;
2325
use PHPStan\Type\TypeCombinator;
26+
use PHPStan\Type\TypeUtils;
27+
use function count;
28+
use function is_array;
29+
use function is_int;
30+
use function preg_match;
31+
use function preg_split;
2432
use function strtolower;
2533

2634
final class PregSplitDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
@@ -53,21 +61,12 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5361
$subjectConstantTypes = $subjectType->getConstantStrings();
5462

5563
if (
56-
count($patternConstantTypes) > 0 &&
57-
@preg_match($patternConstantTypes[0]->getValue(), "") === false
64+
count($patternConstantTypes) > 0
65+
&& @preg_match($patternConstantTypes[0]->getValue(), "") === false
5866
) {
59-
6067
return new ErrorType();
6168
}
6269

63-
if ($subjectArg !== null && $this->bitwiseFlagAnalyser->bitwiseOrContainsConstant($subjectArg->value, $scope, 'PREG_SPLIT_OFFSET_CAPTURE')->yes()) {
64-
$type = new ArrayType(
65-
new IntegerType(),
66-
new ConstantArrayType([new ConstantIntegerType(0), new ConstantIntegerType(1)], [new StringType(), IntegerRangeType::fromInterval(0, null)], [2], [], TrinaryLogic::createYes()),
67-
);
68-
return TypeCombinator::union(TypeCombinator::intersect($type, new AccessoryArrayListType()), new ConstantBooleanType(false));
69-
}
70-
7170
if ($limitArg === null) {
7271
$limits = [-1];
7372
} else {
@@ -82,15 +81,47 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
8281
$flags = $flagType->getConstantScalarValues();
8382
}
8483

85-
if (count($patternConstantTypes) === 0 || count($subjectConstantTypes) === 0 || count($flags) === 0) {
84+
if (count($patternConstantTypes) === 0 || count($subjectConstantTypes) === 0) {
85+
$returnNonEmptyStrings = $flagArg !== null && $this->bitwiseFlagAnalyser->bitwiseOrContainsConstant($flagArg->value, $scope, 'PREG_SPLIT_NO_EMPTY')->yes();
86+
if ($returnNonEmptyStrings) {
87+
$stringType = TypeCombinator::intersect(
88+
new StringType(),
89+
new AccessoryNonEmptyStringType()
90+
);
91+
} else {
92+
$stringType = new StringType();
93+
}
94+
95+
if ($flagArg !== null && $this->bitwiseFlagAnalyser->bitwiseOrContainsConstant($flagArg->value, $scope, 'PREG_SPLIT_OFFSET_CAPTURE')->yes()) {
96+
$type = new ArrayType(
97+
new IntegerType(),
98+
new ConstantArrayType([new ConstantIntegerType(0), new ConstantIntegerType(1)], [$stringType, IntegerRangeType::fromInterval(0, null)], [2], [], TrinaryLogic::createYes()),
99+
);
100+
return TypeUtils::toBenevolentUnion(
101+
TypeCombinator::union(
102+
TypeCombinator::intersect($type, new AccessoryArrayListType()),
103+
new ConstantBooleanType(false)
104+
)
105+
);
106+
}
107+
108+
if ($flagArg !== null && $this->bitwiseFlagAnalyser->bitwiseOrContainsConstant($flagArg->value, $scope, 'PREG_SPLIT_NO_EMPTY')->yes()) {
109+
return TypeUtils::toBenevolentUnion(
110+
TypeCombinator::union(
111+
TypeCombinator::intersect(new ArrayType(new MixedType(), $stringType), new AccessoryArrayListType()),
112+
new ConstantBooleanType(false)
113+
)
114+
);
115+
}
116+
86117
return null;
87118
}
88119

89120
$resultTypes = [];
90121
foreach ($patternConstantTypes as $patternConstantType) {
91122
foreach ($subjectConstantTypes as $subjectConstantType) {
92-
foreach ($flags as $flag) {
93-
foreach ($limits as $limit) {
123+
foreach ($limits as $limit) {
124+
foreach ($flags as $flag) {
94125
$result = @preg_split($patternConstantType->getValue(), $subjectConstantType->getValue(), $limit, $flag);
95126
if ($result !== false) {
96127
$constantArray = ConstantArrayTypeBuilder::createEmpty();

tests/PHPStan/Analyser/nsrt/preg_split.php

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,6 @@
66

77
class HelloWorld
88
{
9-
public function doFoo()
10-
{
11-
$aaa = '/[0-9a]';
12-
assertType('*ERROR*', preg_split($aaa, '1-2-3'));
13-
assertType("array{'1', '2', '3'}", preg_split('/-/', '1-2-3'));
14-
assertType("array{'1', '2', '3'}", preg_split('/-/', '1-2-3', -1, PREG_SPLIT_NO_EMPTY));
15-
assertType("array{'1', '3'}", preg_split('/-/', '1--3', -1, PREG_SPLIT_NO_EMPTY));
16-
assertType("array{array{'1', 0}, array{'2', 2}, array{'3', 4}}", preg_split('/-/', '1-2-3', -1, PREG_SPLIT_OFFSET_CAPTURE));
17-
assertType("array{array{'1', 0}, array{'2', 2}, array{'3', 4}}", preg_split('/-/', '1-2-3', -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE));
18-
assertType("array{array{'1', 0}, array{'', 2}, array{'3', 3}}", preg_split('/-/', '1--3', -1, PREG_SPLIT_OFFSET_CAPTURE));
19-
assertType("array{array{'1', 0}, array{'3', 3}}", preg_split('/-/', '1--3', -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE));
20-
}
21-
22-
public function doWithVariables(string $pattern, string $subject, int $offset, int $flags): void
23-
{
24-
assertType('(list<string>|false)', preg_split($pattern, $subject, $offset, $flags));
25-
assertType('(list<string>|false)', preg_split("//", $subject, $offset, $flags));
26-
assertType('(list<string>|false)', preg_split($pattern, "1-2-3", $offset, $flags));
27-
assertType('(list<string>|false)', preg_split($pattern, $subject, -1, $flags));
28-
assertType('(list<string>|false)', preg_split($pattern, $subject, $offset, PREG_SPLIT_NO_EMPTY));
29-
assertType('list<array{string, int<0, max>}>', preg_split($pattern, $subject, $offset, PREG_SPLIT_OFFSET_CAPTURE));
30-
}
31-
329
/**
3310
* @param string $pattern
3411
* @param string $subject
@@ -39,24 +16,54 @@ public function doWithVariables(string $pattern, string $subject, int $offset, i
3916
*/
4017
public static function splitWithOffset($pattern, $subject, $limit = -1, $flags = 0)
4118
{
42-
assertType('list<array{string, int<0, max>}>', preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE));
43-
assertType('list<array{string, int<0, max>}>', preg_split($pattern, $subject, $limit, PREG_SPLIT_OFFSET_CAPTURE | $flags));
44-
45-
assertType('list<array{string, int<0, max>}>', preg_split($pattern, $subject, $limit, PREG_SPLIT_OFFSET_CAPTURE | $flags | PREG_SPLIT_NO_EMPTY));
19+
assertType('(list<array{string, int<0, max>}>|false)', preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE));
20+
assertType('(list<array{string, int<0, max>}>|false)', preg_split($pattern, $subject, $limit, PREG_SPLIT_OFFSET_CAPTURE | $flags));
21+
assertType('(list<array{non-empty-string, int<0, max>}>|false)', preg_split($pattern, $subject, $limit, PREG_SPLIT_OFFSET_CAPTURE | $flags | PREG_SPLIT_NO_EMPTY));
4622
}
4723

4824
/**
4925
* @param string $pattern
5026
* @param string $subject
5127
* @param int $limit
5228
*/
53-
public static function dynamicFlags($pattern, $subject, $limit = -1) {
29+
public static function dynamicFlags($pattern, $subject, $limit = -1)
30+
{
5431
$flags = PREG_SPLIT_OFFSET_CAPTURE;
5532

5633
if ($subject === '1-2-3') {
5734
$flags |= PREG_SPLIT_NO_EMPTY;
5835
}
5936

60-
assertType('list<array{string, int<0, max>}>|false', preg_split($pattern, $subject, $limit, $flags));
37+
assertType('(list<array{string, int<0, max>}>|false)', preg_split($pattern, $subject, $limit, $flags));
38+
}
39+
40+
public function doFoo()
41+
{
42+
assertType('*ERROR*', preg_split('/[0-9a]', '1-2-3'));
43+
assertType("array{''}", preg_split('/-/', ''));
44+
assertType("array{'1', '2', '3'}", preg_split('/-/', '1-2-3'));
45+
assertType("array{'1', '2', '3'}", preg_split('/-/', '1-2-3', -1, PREG_SPLIT_NO_EMPTY));
46+
assertType("array{'1', '3'}", preg_split('/-/', '1--3', -1, PREG_SPLIT_NO_EMPTY));
47+
assertType("array{array{'1', 0}, array{'2', 2}, array{'3', 4}}", preg_split('/-/', '1-2-3', -1, PREG_SPLIT_OFFSET_CAPTURE));
48+
assertType("array{array{'1', 0}, array{'2', 2}, array{'3', 4}}", preg_split('/-/', '1-2-3', -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE));
49+
assertType("array{array{'1', 0}, array{'', 2}, array{'3', 3}}", preg_split('/-/', '1--3', -1, PREG_SPLIT_OFFSET_CAPTURE));
50+
assertType("array{array{'1', 0}, array{'3', 3}}", preg_split('/-/', '1--3', -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE));
51+
}
52+
53+
/**
54+
* @param non-empty-string $nonEmptySubject
55+
*/
56+
public function doWithVariables(string $pattern, string $subject, string $nonEmptySubject, int $offset, int $flags): void
57+
{
58+
assertType('(list<array{string, int<0, max>}|string>|false)', preg_split($pattern, $subject, $offset, $flags));
59+
assertType('(list<array{string, int<0, max>}|string>|false)', preg_split("//", $subject, $offset, $flags));
60+
61+
assertType('(list<array{string, int<0, max>}|string>|false)', preg_split($pattern, $nonEmptySubject, $offset, $flags));
62+
assertType('(list<array{string, int<0, max>}|string>|false)', preg_split("//", $nonEmptySubject, $offset, $flags));
63+
64+
assertType('(list<array{string, int<0, max>}|string>|false)', preg_split($pattern, "1-2-3", $offset, $flags));
65+
assertType('(list<array{string, int<0, max>}|string>|false)', preg_split($pattern, $subject, -1, $flags));
66+
assertType('(list<non-empty-string>|false)', preg_split($pattern, $subject, $offset, PREG_SPLIT_NO_EMPTY));
67+
assertType('(list<array{string, int<0, max>}>|false)', preg_split($pattern, $subject, $offset, PREG_SPLIT_OFFSET_CAPTURE));
6168
}
6269
}

0 commit comments

Comments
 (0)