3
3
namespace PHPStan \Type \Nette ;
4
4
5
5
use Nette \Utils \Strings ;
6
+ use PhpParser \Node \Arg ;
6
7
use PhpParser \Node \Expr \StaticCall ;
7
8
use PHPStan \Analyser \Scope ;
8
9
use PHPStan \Reflection \MethodReflection ;
9
10
use PHPStan \TrinaryLogic ;
11
+ use PHPStan \Type \Constant \ConstantBooleanType ;
12
+ use PHPStan \Type \Constant \ConstantIntegerType ;
10
13
use PHPStan \Type \DynamicStaticMethodReturnTypeExtension ;
11
14
use PHPStan \Type \Php \RegexArrayShapeMatcher ;
12
15
use PHPStan \Type \Type ;
16
+ use function array_key_exists ;
17
+ use const PREG_OFFSET_CAPTURE ;
18
+ use const PREG_PATTERN_ORDER ;
19
+ use const PREG_SET_ORDER ;
20
+ use const PREG_UNMATCHED_AS_NULL ;
13
21
14
22
class StringsMatchAllDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
15
23
{
@@ -36,17 +44,47 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
36
44
{
37
45
$ args = $ methodCall ->getArgs ();
38
46
$ patternArg = $ args [1 ] ?? null ;
47
+
39
48
if ($ patternArg === null ) {
40
49
return null ;
41
50
}
42
51
43
- $ flagsArg = $ args [2 ] ?? null ;
44
- $ flagsType = null ;
45
- if ($ flagsArg !== null ) {
46
- $ flagsType = $ scope ->getType ($ flagsArg ->value );
52
+ return $ this ->regexArrayShapeMatcher ->matchAllExpr (
53
+ $ patternArg ->value ,
54
+ $ this ->resolveFlagsType ($ args , $ scope ),
55
+ TrinaryLogic::createYes (),
56
+ $ scope
57
+ );
58
+ }
59
+
60
+ /**
61
+ * @param array<Arg> $args
62
+ */
63
+ private function resolveFlagsType (array $ args , Scope $ scope ): ConstantIntegerType
64
+ {
65
+ if (!array_key_exists (2 , $ args )) {
66
+ return new ConstantIntegerType (PREG_SET_ORDER );
67
+ }
68
+
69
+ $ captureOffsetType = $ scope ->getType ($ args [2 ]->value );
70
+
71
+ if ($ captureOffsetType instanceof ConstantIntegerType) {
72
+ $ captureOffset = $ captureOffsetType ->getValue ();
73
+ $ flags = ($ captureOffset & PREG_PATTERN_ORDER ) === PREG_PATTERN_ORDER ? $ captureOffset : ($ captureOffset | PREG_SET_ORDER );
74
+
75
+ return new ConstantIntegerType ($ flags );
47
76
}
48
77
49
- return $ this ->regexArrayShapeMatcher ->matchAllExpr ($ patternArg ->value , $ flagsType , TrinaryLogic::createYes (), $ scope );
78
+ $ unmatchedAsNullType = array_key_exists (4 , $ args ) ? $ scope ->getType ($ args [4 ]->value ) : new ConstantBooleanType (false );
79
+ $ patternOrderType = array_key_exists (5 , $ args ) ? $ scope ->getType ($ args [5 ]->value ) : new ConstantBooleanType (false );
80
+
81
+ $ captureOffset = $ captureOffsetType ->isTrue ()->yes ();
82
+ $ unmatchedAsNull = $ unmatchedAsNullType ->isTrue ()->yes ();
83
+ $ patternOrder = $ patternOrderType ->isTrue ()->yes ();
84
+
85
+ return new ConstantIntegerType (
86
+ ($ captureOffset ? PREG_OFFSET_CAPTURE : 0 ) | ($ unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0 ) | ($ patternOrder ? PREG_PATTERN_ORDER : 0 )
87
+ );
50
88
}
51
89
52
90
}
0 commit comments