Skip to content

Commit

Permalink
support variable constant flags in preg_split
Browse files Browse the repository at this point in the history
  • Loading branch information
clxmstaab authored and ondrejmirtes committed Dec 20, 2021
1 parent 27b2c91 commit a16c7ae
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
23 changes: 19 additions & 4 deletions src/Type/Php/PregSplitDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace PHPStan\Type\Php;

use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\BitwiseOr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
Expand All @@ -15,6 +17,7 @@
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
Expand Down Expand Up @@ -46,7 +49,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
if ($this->hasFlag($this->getConstant('PREG_SPLIT_OFFSET_CAPTURE'), $flagsArg, $scope)) {
$type = new ArrayType(
new IntegerType(),
new ConstantArrayType([new ConstantIntegerType(0), new ConstantIntegerType(1)], [new StringType(), new IntegerType()]),
new ConstantArrayType([new ConstantIntegerType(0), new ConstantIntegerType(1)], [new StringType(), IntegerRangeType::fromInterval(0, null)]),
);
return TypeCombinator::union($type, new ConstantBooleanType(false));
}
Expand All @@ -55,13 +58,25 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
}


private function hasFlag(int $flag, ?Arg $expression, Scope $scope): bool
private function hasFlag(int $flag, ?Arg $arg, Scope $scope): bool
{
if ($expression === null) {
if ($arg === null) {
return false;
}

$type = $scope->getType($expression->value);
return $this->isConstantFlag($flag, $arg->value, $scope);
}

private function isConstantFlag(int $flag, Expr $expression, Scope $scope): bool
{
if ($expression instanceof BitwiseOr) {
$left = $expression->left;
$right = $expression->right;

return $this->isConstantFlag($flag, $left, $scope) || $this->isConstantFlag($flag, $right, $scope);
}

$type = $scope->getType($expression);
return $type instanceof ConstantIntegerType && ($type->getValue() & $flag) === $flag;
}

Expand Down
30 changes: 26 additions & 4 deletions tests/PHPStan/Analyser/data/preg_split.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,29 @@

use function PHPStan\Testing\assertType;

assertType('array<int, string>|false', preg_split('/-/', '1-2-3'));
assertType('array<int, string>|false', preg_split('/-/', '1-2-3', -1, PREG_SPLIT_NO_EMPTY));
assertType('array<int, array{string, int}>|false', preg_split('/-/', '1-2-3', -1, PREG_SPLIT_OFFSET_CAPTURE));
assertType('array<int, array{string, int}>|false', preg_split('/-/', '1-2-3', -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE));
class HelloWorld
{
public function doFoo()
{
assertType('array<int, string>|false', preg_split('/-/', '1-2-3'));
assertType('array<int, string>|false', preg_split('/-/', '1-2-3', -1, PREG_SPLIT_NO_EMPTY));
assertType('array<int, array{string, int<0, max>}>|false', preg_split('/-/', '1-2-3', -1, PREG_SPLIT_OFFSET_CAPTURE));
assertType('array<int, array{string, int<0, max>}>|false', preg_split('/-/', '1-2-3', -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE));
}

/**
* @param string $pattern
* @param string $subject
* @param int $limit
* @param int $flags PREG_SPLIT_NO_EMPTY or PREG_SPLIT_DELIM_CAPTURE
* @return list<array{string, int}>
* @phpstan-return list<array{string, int<0, max>}>
*/
public static function splitWithOffset($pattern, $subject, $limit = -1, $flags = 0)
{
assertType('array<int, array{string, int<0, max>}>|false', preg_split($pattern, $subject, $limit, $flags | PREG_SPLIT_OFFSET_CAPTURE));
assertType('array<int, array{string, int<0, max>}>|false', preg_split($pattern, $subject, $limit, PREG_SPLIT_OFFSET_CAPTURE | $flags));

assertType('array<int, array{string, int<0, max>}>|false', preg_split($pattern, $subject, $limit, PREG_SPLIT_OFFSET_CAPTURE | $flags | PREG_SPLIT_NO_EMPTY));
}
}

0 comments on commit a16c7ae

Please sign in to comment.