Skip to content

Commit

Permalink
Merge branch refs/heads/1.11.x into 1.12.x
Browse files Browse the repository at this point in the history
  • Loading branch information
phpstan-bot authored Aug 8, 2024
2 parents d6146f1 + 5a728f9 commit e6ff365
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 13 deletions.
23 changes: 15 additions & 8 deletions src/Type/Php/RegexArrayShapeMatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,8 @@ private function buildArrayType(
$i = 0;
foreach ($captureGroups as $captureGroup) {
$isTrailingOptional = $i >= $countGroups - $trailingOptionals;
$groupValueType = $this->createGroupValueType($captureGroup, $wasMatched, $flags, $isTrailingOptional, $matchesAll);
$isLastGroup = $i === $countGroups - 1;
$groupValueType = $this->createGroupValueType($captureGroup, $wasMatched, $flags, $isTrailingOptional, $isLastGroup, $matchesAll);
$optional = $this->isGroupOptional($captureGroup, $wasMatched, $flags, $isTrailingOptional, $matchesAll);

if ($captureGroup->isNamed()) {
Expand Down Expand Up @@ -390,11 +391,11 @@ private function isGroupOptional(RegexCapturingGroup $captureGroup, TrinaryLogic
return $optional;
}

private function createGroupValueType(RegexCapturingGroup $captureGroup, TrinaryLogic $wasMatched, int $flags, bool $isTrailingOptional, bool $matchesAll): Type
private function createGroupValueType(RegexCapturingGroup $captureGroup, TrinaryLogic $wasMatched, int $flags, bool $isTrailingOptional, bool $isLastGroup, bool $matchesAll): Type
{
$groupValueType = $this->getValueType($captureGroup->getType(), $flags, $matchesAll);

if ($matchesAll) {
$groupValueType = $this->getValueType($captureGroup->getType(), $flags, $matchesAll);

if (!$isTrailingOptional && $this->containsUnmatchedAsNull($flags, $matchesAll) && !$captureGroup->isOptional()) {
$groupValueType = TypeCombinator::removeNull($groupValueType);
}
Expand All @@ -411,16 +412,22 @@ private function createGroupValueType(RegexCapturingGroup $captureGroup, Trinary
return $groupValueType;
}

if (!$isLastGroup && !$this->containsUnmatchedAsNull($flags, $matchesAll) && $captureGroup->isOptional()) {
$groupValueType = $this->getValueType(
TypeCombinator::union($captureGroup->getType(), new ConstantStringType('')),
$flags,
$matchesAll,
);
} else {
$groupValueType = $this->getValueType($captureGroup->getType(), $flags, $matchesAll);
}

if ($wasMatched->yes()) {
if (!$isTrailingOptional && $this->containsUnmatchedAsNull($flags, $matchesAll) && !$captureGroup->isOptional()) {
$groupValueType = TypeCombinator::removeNull($groupValueType);
}
}

if (!$isTrailingOptional && !$this->containsUnmatchedAsNull($flags, $matchesAll) && $captureGroup->isOptional()) {
$groupValueType = TypeCombinator::union($groupValueType, new ConstantStringType(''));
}

return $groupValueType;
}

Expand Down
4 changes: 2 additions & 2 deletions tests/PHPStan/Analyser/nsrt/bug-11311-php72.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ function doFoo(string $s) {

function doUnmatchedAsNull(string $s): void {
if (preg_match('/(foo)?(bar)?(baz)?/', $s, $matches, PREG_UNMATCHED_AS_NULL)) {
assertType("array{0: string, 1?: 'foo', 2?: 'bar', 3?: 'baz'}", $matches);
assertType("array{0: string, 1?: ''|'foo', 2?: ''|'bar', 3?: 'baz'}", $matches);
}
assertType("array{}|array{0: string, 1?: 'foo', 2?: 'bar', 3?: 'baz'}", $matches);
assertType("array{}|array{0: string, 1?: ''|'foo', 2?: ''|'bar', 3?: 'baz'}", $matches);
}

// see https://3v4l.org/VeDob#veol
Expand Down
25 changes: 23 additions & 2 deletions tests/PHPStan/Analyser/nsrt/preg_match_shapes.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ function (string $size): void {
if (preg_match('~^a\.(b)?(c)?d~', $size, $matches) !== 1) {
throw new InvalidArgumentException(sprintf('Invalid size "%s"', $size));
}
assertType("array{0: string, 1?: 'b', 2?: 'c'}", $matches);
assertType("array{0: string, 1?: ''|'b', 2?: 'c'}", $matches);
};

function (string $size): void {
Expand Down Expand Up @@ -567,7 +567,7 @@ function (string $s): void {
}

if (preg_match($p, $s, $matches)) {
assertType("array{0: string, 1: non-empty-string, 2?: numeric-string, 3?: 'x'}", $matches);
assertType("array{0: string, 1: non-empty-string, 2?: ''|numeric-string, 3?: 'x'}", $matches);
}
};

Expand Down Expand Up @@ -662,3 +662,24 @@ function (string $value): void
assertType("array{0: array{string, int<0, max>}, 1?: array{non-empty-string, int<0, max>}, 2?: array{non-empty-string, int<0, max>}}", $matches);
}
};

class Bug11479
{
static public function sayHello(string $source): void
{
$pattern = "~^(?P<dateFrom>\d)?\-?(?P<dateTo>\d)?$~";

preg_match($pattern, $source, $matches);

// for $source = "-1" in $matches is
// array (
// 0 => '-1',
// 'dateFrom' => '',
// 1 => '',
// 'dateTo' => '1',
// 2 => '1',
//)

assertType("array{0?: string, dateFrom?: ''|numeric-string, 1?: ''|numeric-string, dateTo?: numeric-string, 2?: numeric-string}", $matches);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function (string $s): void {
preg_replace_callback(
'/(foo)?(bar)?(baz)?/',
function ($matches) {
assertType("array{0: array{string, int<0, max>}, 1?: array{'foo', int<0, max>}, 2?: array{'bar', int<0, max>}, 3?: array{'baz', int<0, max>}}", $matches);
assertType("array{0: array{string, int<0, max>}, 1?: array{''|'foo', int<0, max>}, 2?: array{''|'bar', int<0, max>}, 3?: array{'baz', int<0, max>}}", $matches);
return '';
},
$s,
Expand Down

0 comments on commit e6ff365

Please sign in to comment.