33namespace PHPStan \Type \Nette ;
44
55use Nette \Utils \Strings ;
6+ use PhpParser \Node \Arg ;
67use PhpParser \Node \Expr \StaticCall ;
78use PHPStan \Analyser \Scope ;
89use PHPStan \Reflection \MethodReflection ;
910use PHPStan \TrinaryLogic ;
11+ use PHPStan \Type \Constant \ConstantBooleanType ;
12+ use PHPStan \Type \Constant \ConstantIntegerType ;
1013use PHPStan \Type \DynamicStaticMethodReturnTypeExtension ;
1114use PHPStan \Type \Php \RegexArrayShapeMatcher ;
1215use 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 ;
1321
1422class StringsMatchAllDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension
1523{
@@ -36,17 +44,47 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
3644 {
3745 $ args = $ methodCall ->getArgs ();
3846 $ patternArg = $ args [1 ] ?? null ;
47+
3948 if ($ patternArg === null ) {
4049 return null ;
4150 }
4251
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 );
4776 }
4877
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+ );
5088 }
5189
5290}
0 commit comments