diff --git a/src/Type/Php/RegexArrayShapeMatcher.php b/src/Type/Php/RegexArrayShapeMatcher.php index 794579695f..49db1198db 100644 --- a/src/Type/Php/RegexArrayShapeMatcher.php +++ b/src/Type/Php/RegexArrayShapeMatcher.php @@ -32,6 +32,11 @@ final class RegexArrayShapeMatcher { + /** + * Pass this into $flagsType as well if the library supports emulating PREG_UNMATCHED_AS_NULL on PHP 7.2 and 7.3 + */ + public const PREG_UNMATCHED_AS_NULL_ON_72_73 = 2048; + private static ?Parser $parser = null; public function __construct( @@ -53,14 +58,17 @@ public function matchType(Type $patternType, ?Type $flagsType, TrinaryLogic $was $flags = null; if ($flagsType !== null) { - if ( - !$flagsType instanceof ConstantIntegerType - || !in_array($flagsType->getValue(), [PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL, PREG_OFFSET_CAPTURE | PREG_UNMATCHED_AS_NULL], true) - ) { + if (!$flagsType instanceof ConstantIntegerType) { return null; } - $flags = $flagsType->getValue(); + /** @var int-mask $flags */ + $flags = $flagsType->getValue() & (PREG_OFFSET_CAPTURE | PREG_UNMATCHED_AS_NULL | self::PREG_UNMATCHED_AS_NULL_ON_72_73); + + // some other unsupported/unexpected flag was passed in + if ($flags !== $flagsType->getValue()) { + return null; + } } $matchedTypes = []; @@ -81,7 +89,7 @@ public function matchType(Type $patternType, ?Type $flagsType, TrinaryLogic $was } /** - * @param int-mask|null $flags + * @param int-mask|null $flags */ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched): ?Type { @@ -292,7 +300,7 @@ private function buildArrayType( private function containsUnmatchedAsNull(int $flags): bool { - return ($flags & PREG_UNMATCHED_AS_NULL) !== 0 && $this->phpVersion->supportsPregUnmatchedAsNull(); + return ($flags & PREG_UNMATCHED_AS_NULL) !== 0 && (($flags & self::PREG_UNMATCHED_AS_NULL_ON_72_73) !== 0 || $this->phpVersion->supportsPregUnmatchedAsNull()); } private function getKeyType(int|string $key): Type