Skip to content

Commit 2f10677

Browse files
authored
RegexArrayShapeMatcher: Support resolving of constants in patterns
1 parent 4d055b0 commit 2f10677

File tree

2 files changed

+54
-28
lines changed

2 files changed

+54
-28
lines changed

src/Type/Php/RegexArrayShapeMatcher.php

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PhpParser\Node\Name;
1414
use PHPStan\Analyser\Scope;
1515
use PHPStan\Php\PhpVersion;
16+
use PHPStan\Reflection\InitializerExprTypeResolver;
1617
use PHPStan\ShouldNotHappenException;
1718
use PHPStan\TrinaryLogic;
1819
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
@@ -55,6 +56,7 @@ final class RegexArrayShapeMatcher
5556

5657
public function __construct(
5758
private PhpVersion $phpVersion,
59+
private InitializerExprTypeResolver $initializerExprTypeResolver,
5860
)
5961
{
6062
}
@@ -725,38 +727,42 @@ private function getPatternType(Expr $patternExpr, Scope $scope): Type
725727
*/
726728
private function resolvePatternConcat(Expr\BinaryOp\Concat $concat, Scope $scope): Type
727729
{
728-
if (
729-
$concat->left instanceof Expr\FuncCall
730-
&& $concat->left->name instanceof Name
731-
&& $concat->left->name->toLowerString() === 'preg_quote'
732-
) {
733-
$left = new ConstantStringType('');
734-
} elseif ($concat->left instanceof Expr\BinaryOp\Concat) {
735-
$left = $this->resolvePatternConcat($concat->left, $scope);
736-
} else {
737-
$left = $scope->getType($concat->left);
738-
}
730+
$resolver = new class($scope) {
739731

740-
if (
741-
$concat->right instanceof Expr\FuncCall
742-
&& $concat->right->name instanceof Name
743-
&& $concat->right->name->toLowerString() === 'preg_quote'
744-
) {
745-
$right = new ConstantStringType('');
746-
} elseif ($concat->right instanceof Expr\BinaryOp\Concat) {
747-
$right = $this->resolvePatternConcat($concat->right, $scope);
748-
} else {
749-
$right = $scope->getType($concat->right);
750-
}
732+
public function __construct(private Scope $scope)
733+
{
734+
}
735+
736+
public function resolve(Expr $expr): Type
737+
{
738+
if (
739+
$expr instanceof Expr\FuncCall
740+
&& $expr->name instanceof Name
741+
&& $expr->name->toLowerString() === 'preg_quote'
742+
) {
743+
return new ConstantStringType('');
744+
}
745+
746+
if ($expr instanceof Expr\BinaryOp\Concat) {
747+
$left = $this->resolve($expr->left);
748+
$right = $this->resolve($expr->right);
749+
750+
$strings = [];
751+
foreach ($left->toString()->getConstantStrings() as $leftString) {
752+
foreach ($right->toString()->getConstantStrings() as $rightString) {
753+
$strings[] = new ConstantStringType($leftString->getValue() . $rightString->getValue());
754+
}
755+
}
751756

752-
$strings = [];
753-
foreach ($left->getConstantStrings() as $leftString) {
754-
foreach ($right->getConstantStrings() as $rightString) {
755-
$strings[] = new ConstantStringType($leftString->getValue() . $rightString->getValue());
757+
return TypeCombinator::union(...$strings);
758+
}
759+
760+
return $this->scope->getType($expr);
756761
}
757-
}
758762

759-
return TypeCombinator::union(...$strings);
763+
};
764+
765+
return $this->initializerExprTypeResolver->getConcatType($concat->left, $concat->right, static fn (Expr $expr): Type => $resolver->resolve($expr));
760766
}
761767

762768
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bug11384;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Bar
8+
{
9+
public const VAL = 3;
10+
}
11+
12+
class HelloWorld
13+
{
14+
public function sayHello(string $s): void
15+
{
16+
if (preg_match('{(' . Bar::VAL . ')}', $s, $m)) {
17+
assertType('array{string, numeric-string}', $m);
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)