Skip to content

Commit

Permalink
Merge branch '2.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
Seldaek committed Aug 27, 2024
2 parents 7cafee3 + 26859a8 commit 63aaeac
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 12 deletions.
33 changes: 33 additions & 0 deletions src/PHPStan/PregMatchFlags.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
namespace Composer\Pcre\PHPStan;

use PHPStan\Analyser\Scope;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\Type;
use PhpParser\Node\Arg;
use PHPStan\Type\Php\RegexArrayShapeMatcher;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;

final class PregMatchFlags
{
Expand All @@ -34,4 +39,32 @@ static public function getType(?Arg $flagsArg, Scope $scope): ?Type
}
return TypeCombinator::union(...$internalFlagsTypes);
}

static public function removeNullFromMatches(Type $matchesType): Type
{
return TypeTraverser::map($matchesType, static function (Type $type, callable $traverse): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}

if ($type instanceof ConstantArrayType) {
return new ConstantArrayType(
$type->getKeyTypes(),
array_map(static function (Type $valueType) use ($traverse): Type {
return $traverse($valueType);
}, $type->getValueTypes()),
$type->getNextAutoIndexes(),
[],
$type->isList()
);
}

if ($type instanceof ArrayType) {
return new ArrayType($type->getKeyType(), $traverse($type->getItemType()));
}

return TypeCombinator::removeNull($type);
});
}

}
14 changes: 2 additions & 12 deletions src/PHPStan/PregMatchTypeSpecifyingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,9 @@ public function specifyTypes(MethodReflection $methodReflection, StaticCall $nod
}

if (
in_array($methodReflection->getName(), ['matchStrictGroups', 'isMatchStrictGroups'], true)
&& count($matchedType->getConstantArrays()) === 1
in_array($methodReflection->getName(), ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true)
) {
$matchedType = $matchedType->getConstantArrays()[0];
$matchedType = new ConstantArrayType(
$matchedType->getKeyTypes(),
array_map(static function (Type $valueType): Type {
return TypeCombinator::removeNull($valueType);
}, $matchedType->getValueTypes()),
$matchedType->getNextAutoIndexes(),
[],
$matchedType->isList()
);
$matchedType = PregMatchFlags::removeNullFromMatches($matchedType);
}

$overwrite = false;
Expand Down
4 changes: 4 additions & 0 deletions tests/PHPStanTests/UnsafeStrictGroupsCallRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public function testRule(): void
'The isMatchStrictGroups call is potentially unsafe as $matches\' type could not be inferred.',
86,
],
[
'The isMatchAllStrictGroups call is unsafe as match groups "test", "1" are optional and may be null.',
114
]
]);
}

Expand Down
4 changes: 4 additions & 0 deletions tests/PHPStanTests/nsrt/preg-match.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ function doMatchAllStrictGroups(string $s): void
assertType('array{}', $matches);
}
assertType('array{}|array{0: list<string>, test: list<non-empty-string>, 1: list<non-empty-string>}', $matches);

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

// disabled until https://github.com/phpstan/phpstan-src/pull/3185 can be resolved
Expand Down

0 comments on commit 63aaeac

Please sign in to comment.