Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions tests/PHPStanTests/nsrt/preg-match-php7.2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php // lint < 7.4

namespace PregMatchShapes;

use Composer\Pcre\Preg;
use Composer\Pcre\Regex;
use function PHPStan\Testing\assertType;

function doMatch(string $s): void
{
if (Preg::match('/Price: /i', $s, $matches)) {
assertType('array{string}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{string}', $matches);

if (Preg::match('/Price: (£|€)\d+/', $s, $matches)) {
assertType('array{string, \'£\'|\'€\'}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{string, \'£\'|\'€\'}', $matches);

if (Preg::match('/Price: (£|€)?\d+/', $s, $matches)) {
assertType('array{string, \'£\'|\'€\'|null}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{string, \'£\'|\'€\'|null}', $matches);

// passing the PREG_UNMATCHED_AS_NULL should change nothing compared to above as it is always set
if (Preg::match('/Price: (£|€)?\d+/', $s, $matches, PREG_UNMATCHED_AS_NULL)) {
assertType('array{string, \'£\'|\'€\'|null}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{string, \'£\'|\'€\'|null}', $matches);

if (Preg::isMatch('/Price: (?<currency>£|€)\d+/', $s, $matches)) {
assertType('array{0: string, currency: \'£\'|\'€\', 1: \'£\'|\'€\'}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{0: string, currency: \'£\'|\'€\', 1: \'£\'|\'€\'}', $matches);
}

function doMatchStrictGroups(string $s): void
{
if (Preg::matchStrictGroups('/Price: /i', $s, $matches)) {
assertType('array{string}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{string}', $matches);

if (Preg::matchStrictGroups('/Price: (£|€)\d+/', $s, $matches)) {
assertType('array{string, \'£\'|\'€\'}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{string, \'£\'|\'€\'}', $matches);

if (Preg::isMatchStrictGroups('/Price: (?<test>£|€)\d+/', $s, $matches)) {
assertType('array{0: string, test: \'£\'|\'€\', 1: \'£\'|\'€\'}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{0: string, test: \'£\'|\'€\', 1: \'£\'|\'€\'}', $matches);
}

function doMatchStrictGroupsUnsafe(string $s): void
{
if (Preg::isMatchStrictGroups('{Configure Command(?: *</td><td class="v">| *=> *)(.*)(?:</td>|$)}m', $s, $matches)) {
// does not error because the match group might be empty but is not optional
assertType('array{string, string}', $matches);
}

// should error as it is unsafe due to the optional group 1
Regex::matchStrictGroups('{Configure Command(?: *</td><td class="v">| *=> *)(.*)?(?:</td>|$)}m', $s);

if (Preg::matchAllStrictGroups('{((?<foo>.)?z)}m', $s, $matches)) {
// should error as it is unsafe due to the optional group foo/2
}

if (Preg::isMatchStrictGroups('{'.$s.'}', $s, $matches)) {
// should error as it is unsafe due not being introspectable with the dynamic string
}
}

function doMatchAllStrictGroups(string $s): void
{
if (Preg::matchAllStrictGroups('/Price: /i', $s, $matches)) {
assertType('array{list<string>}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{list<string>}', $matches);

if (Preg::matchAllStrictGroups('/Price: (£|€)\d+/', $s, $matches)) {
assertType('array{list<string>, list<\'£\'|\'€\'>}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{list<string>, list<\'£\'|\'€\'>}', $matches);

if (Preg::isMatchAllStrictGroups('/Price: (?<test>£|€)\d+/', $s, $matches)) {
assertType('array{0: list<string>, test: list<\'£\'|\'€\'>, 1: list<\'£\'|\'€\'>}', $matches);
} else {
assertType('array{}', $matches);
}
assertType('array{}|array{0: list<string>, test: list<\'£\'|\'€\'>, 1: list<\'£\'|\'€\'>}', $matches);

if (Preg::isMatchAllStrictGroups('/Price: (?<test>£|€)?\d+/', $s, $matches)) {
assertType('array{0: list<string>, test: list<\'£\'|\'€\'>, 1: list<\'£\'|\'€\'>}', $matches);
}
}

// disabled until https://github.com/phpstan/phpstan-src/pull/3185 can be resolved
//
//function identicalMatch(string $s): void
//{
// if (Preg::match('/Price: /i', $s, $matches) === 1) {
// assertType('array{string}', $matches);
// } else {
// assertType('array{}', $matches);
// }
// assertType('array{}|array{string}', $matches);
//}
//
//function equalMatch(string $s): void
//{
// if (Preg::match('/Price: /i', $s, $matches) == 1) {
// assertType('array{string}', $matches);
// } else {
// assertType('array{}', $matches);
// }
// assertType('array{}|array{string}', $matches);
//}
2 changes: 1 addition & 1 deletion tests/PHPStanTests/nsrt/preg-match.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php // lint >= 7.4

namespace PregMatchShapes;

Expand Down
Loading