Skip to content

Commit 26ec2df

Browse files
committed
Narrow Strings::match/Strings::matchAll subject string type when match is truthy
1 parent 130b009 commit 26ec2df

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Nette;
4+
5+
use Nette\Utils\Strings;
6+
use PhpParser\Node\Arg;
7+
use PhpParser\Node\Expr\StaticCall;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Analyser\SpecifiedTypes;
10+
use PHPStan\Analyser\TypeSpecifier;
11+
use PHPStan\Analyser\TypeSpecifierContext;
12+
use PHPStan\Reflection\MethodReflection;
13+
use PHPStan\TrinaryLogic;
14+
use PHPStan\Type\Constant\ConstantBooleanType;
15+
use PHPStan\Type\Constant\ConstantIntegerType;
16+
use PHPStan\Type\DynamicStaticMethodReturnTypeExtension;
17+
use PHPStan\Type\NullType;
18+
use PHPStan\Type\Php\RegexArrayShapeMatcher;
19+
use PHPStan\Type\StaticMethodTypeSpecifyingExtension;
20+
use PHPStan\Type\Type;
21+
use PHPStan\Type\TypeCombinator;
22+
use function array_key_exists;
23+
use const PREG_OFFSET_CAPTURE;
24+
use const PREG_UNMATCHED_AS_NULL;
25+
26+
class StringsMatchTypeSpecifiyingExtension implements StaticMethodTypeSpecifyingExtension
27+
{
28+
private RegexArrayShapeMatcher $regexArrayShapeMatcher;
29+
30+
private TypeSpecifier $typeSpecifier;
31+
32+
public function __construct(RegexArrayShapeMatcher $regexArrayShapeMatcher)
33+
{
34+
$this->regexArrayShapeMatcher = $regexArrayShapeMatcher;
35+
}
36+
37+
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
38+
{
39+
$this->typeSpecifier = $typeSpecifier;
40+
}
41+
42+
public function getClass(): string
43+
{
44+
return Strings::class;
45+
}
46+
47+
public function isStaticMethodSupported(MethodReflection $staticMethodReflection, StaticCall $node, TypeSpecifierContext $context): bool
48+
{
49+
return $context->true() && $staticMethodReflection->getName() === 'match';
50+
}
51+
52+
public function specifyTypes(MethodReflection $staticMethodReflection, StaticCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
53+
{
54+
$args = $node->getArgs();
55+
$subjectArg = $args[0] ?? null;
56+
$patternArg = $args[1] ?? null;
57+
58+
$subjectTypes = new SpecifiedTypes();
59+
if ($patternArg === null) {
60+
return $subjectTypes;
61+
}
62+
63+
if (
64+
$subjectArg !== null
65+
&& $context->true()
66+
&& $scope->getType($subjectArg->value)->isString()->yes()
67+
) {
68+
$subjectType = $this->regexArrayShapeMatcher->matchSubjectExpr($patternArg->value, $scope);
69+
if ($subjectType !== null) {
70+
$subjectTypes = $this->typeSpecifier->create(
71+
$subjectArg->value,
72+
$subjectType,
73+
$context,
74+
$scope,
75+
)->setRootExpr($node);
76+
}
77+
}
78+
79+
return $subjectTypes;
80+
}
81+
}

0 commit comments

Comments
 (0)