diff --git a/src/Type/Php/PregMatchParameterOutTypeExtension.php b/src/Type/Php/PregMatchParameterOutTypeExtension.php index 5c210fd..bb157d3 100644 --- a/src/Type/Php/PregMatchParameterOutTypeExtension.php +++ b/src/Type/Php/PregMatchParameterOutTypeExtension.php @@ -13,8 +13,10 @@ use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\ParameterReflection; use PHPStan\TrinaryLogic; +use PHPStan\Type\Constant\ConstantArrayTypeBuilder; use PHPStan\Type\FunctionParameterOutTypeExtension; use PHPStan\Type\Type; +use PHPStan\Type\TypeCombinator; use function in_array; final class PregMatchParameterOutTypeExtension implements FunctionParameterOutTypeExtension @@ -50,7 +52,14 @@ public function getParameterOutTypeFromFunctionCall(FunctionReflection $function } if ($functionReflection->getName() === 'Safe\preg_match') { - return $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createMaybe(), $scope); + $matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope); + if ($matchedType === null) { + return null; + } + return TypeCombinator::union( + ConstantArrayTypeBuilder::createEmpty()->getArray(), + $matchedType, + ); } return $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createMaybe(), $scope); } diff --git a/src/Type/Php/PregMatchTypeSpecifyingExtension.php b/src/Type/Php/PregMatchTypeSpecifyingExtension.php index 487044c..f560a08 100644 --- a/src/Type/Php/PregMatchTypeSpecifyingExtension.php +++ b/src/Type/Php/PregMatchTypeSpecifyingExtension.php @@ -55,10 +55,18 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n $flagsType = $scope->getType($flagsArg->value); } + if ($context->true() && $context->falsey()) { + $wasMatched = TrinaryLogic::createMaybe(); + } elseif ($context->true()) { + $wasMatched = TrinaryLogic::createYes(); + } else { + $wasMatched = TrinaryLogic::createNo(); + } + if ($functionReflection->getName() === 'Safe\preg_match') { - $matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope); + $matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, $wasMatched, $scope); } else { - $matchedType = $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope); + $matchedType = $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, $wasMatched, $scope); } if ($matchedType === null) { return new SpecifiedTypes(); diff --git a/tests/Type/Php/TypeAssertionsTest.php b/tests/Type/Php/TypeAssertionsTest.php index 75c1c7b..e87b6fc 100644 --- a/tests/Type/Php/TypeAssertionsTest.php +++ b/tests/Type/Php/TypeAssertionsTest.php @@ -15,6 +15,7 @@ public static function dataFileAsserts(): iterable yield from self::gatherAssertTypes(__DIR__ . '/data/preg_match_checked.php'); yield from self::gatherAssertTypes(__DIR__ . '/data/preg_replace_return.php'); yield from self::gatherAssertTypes(__DIR__ . '/data/json_decode_return.php'); + yield from self::gatherAssertTypes(__DIR__ . '/data/preg_match_identity_check.php'); } /** diff --git a/tests/Type/Php/data/preg_match_identity_check.php b/tests/Type/Php/data/preg_match_identity_check.php new file mode 100644 index 0000000..f844494 --- /dev/null +++ b/tests/Type/Php/data/preg_match_identity_check.php @@ -0,0 +1,31 @@ +