Skip to content

Commit 4a8ccc6

Browse files
authored
Support more dynamics $flags arg in preg_split
1 parent 7688036 commit 4a8ccc6

File tree

2 files changed

+20
-46
lines changed

2 files changed

+20
-46
lines changed

src/Type/Php/PregSplitDynamicReturnTypeExtension.php

Lines changed: 5 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,12 @@
22

33
namespace PHPStan\Type\Php;
44

5-
use PhpParser\Node\Arg;
6-
use PhpParser\Node\Expr;
7-
use PhpParser\Node\Expr\BinaryOp\BitwiseOr;
85
use PhpParser\Node\Expr\FuncCall;
9-
use PhpParser\Node\Name;
106
use PHPStan\Analyser\Scope;
117
use PHPStan\Reflection\FunctionReflection;
128
use PHPStan\Reflection\ParametersAcceptorSelector;
13-
use PHPStan\Reflection\ReflectionProvider;
14-
use PHPStan\ShouldNotHappenException;
159
use PHPStan\Type\ArrayType;
10+
use PHPStan\Type\BitwiseFlagHelper;
1611
use PHPStan\Type\Constant\ConstantArrayType;
1712
use PHPStan\Type\Constant\ConstantBooleanType;
1813
use PHPStan\Type\Constant\ConstantIntegerType;
@@ -22,28 +17,27 @@
2217
use PHPStan\Type\StringType;
2318
use PHPStan\Type\Type;
2419
use PHPStan\Type\TypeCombinator;
25-
use function sprintf;
2620
use function strtolower;
2721

2822
class PregSplitDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
2923
{
3024

31-
public function __construct(private ReflectionProvider $reflectionProvider)
25+
public function __construct(
26+
private BitwiseFlagHelper $bitwiseFlagAnalyser,
27+
)
3228
{
3329
}
3430

35-
3631
public function isFunctionSupported(FunctionReflection $functionReflection): bool
3732
{
3833
return strtolower($functionReflection->getName()) === 'preg_split';
3934
}
4035

41-
4236
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
4337
{
4438
$flagsArg = $functionCall->getArgs()[3] ?? null;
4539

46-
if ($this->hasFlag($this->getConstant('PREG_SPLIT_OFFSET_CAPTURE'), $flagsArg, $scope)) {
40+
if ($flagsArg !== null && $this->bitwiseFlagAnalyser->bitwiseOrContainsConstant($flagsArg->value, $scope, 'PREG_SPLIT_OFFSET_CAPTURE')->yes()) {
4741
$type = new ArrayType(
4842
new IntegerType(),
4943
new ConstantArrayType([new ConstantIntegerType(0), new ConstantIntegerType(1)], [new StringType(), IntegerRangeType::fromInterval(0, null)]),
@@ -54,39 +48,4 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5448
return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
5549
}
5650

57-
58-
private function hasFlag(int $flag, ?Arg $arg, Scope $scope): bool
59-
{
60-
if ($arg === null) {
61-
return false;
62-
}
63-
64-
return $this->isConstantFlag($flag, $arg->value, $scope);
65-
}
66-
67-
private function isConstantFlag(int $flag, Expr $expression, Scope $scope): bool
68-
{
69-
if ($expression instanceof BitwiseOr) {
70-
$left = $expression->left;
71-
$right = $expression->right;
72-
73-
return $this->isConstantFlag($flag, $left, $scope) || $this->isConstantFlag($flag, $right, $scope);
74-
}
75-
76-
$type = $scope->getType($expression);
77-
return $type instanceof ConstantIntegerType && ($type->getValue() & $flag) === $flag;
78-
}
79-
80-
81-
private function getConstant(string $constantName): int
82-
{
83-
$constant = $this->reflectionProvider->getConstant(new Name($constantName), null);
84-
$valueType = $constant->getValueType();
85-
if (!$valueType instanceof ConstantIntegerType) {
86-
throw new ShouldNotHappenException(sprintf('Constant %s does not have integer type.', $constantName));
87-
}
88-
89-
return $valueType->getValue();
90-
}
91-
9251
}

tests/PHPStan/Analyser/data/preg_split.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,19 @@ public static function splitWithOffset($pattern, $subject, $limit = -1, $flags =
2727

2828
assertType('array<int, array{string, int<0, max>}>|false', preg_split($pattern, $subject, $limit, PREG_SPLIT_OFFSET_CAPTURE | $flags | PREG_SPLIT_NO_EMPTY));
2929
}
30+
31+
/**
32+
* @param string $pattern
33+
* @param string $subject
34+
* @param int $limit
35+
*/
36+
public static function dynamicFlags($pattern, $subject, $limit = -1) {
37+
$flags = PREG_SPLIT_OFFSET_CAPTURE;
38+
39+
if ($subject === '1-2-3') {
40+
$flags |= PREG_SPLIT_NO_EMPTY;
41+
}
42+
43+
assertType('array<int, array{string, int<0, max>}>|false', preg_split($pattern, $subject, $limit, $flags));
44+
}
3045
}

0 commit comments

Comments
 (0)