22
33namespace PHPStan \Type \Php ;
44
5- use PhpParser \Node \Arg ;
6- use PhpParser \Node \Expr ;
7- use PhpParser \Node \Expr \BinaryOp \BitwiseOr ;
85use PhpParser \Node \Expr \FuncCall ;
9- use PhpParser \Node \Name ;
106use PHPStan \Analyser \Scope ;
117use PHPStan \Reflection \FunctionReflection ;
128use PHPStan \Reflection \ParametersAcceptorSelector ;
13- use PHPStan \Reflection \ReflectionProvider ;
14- use PHPStan \ShouldNotHappenException ;
159use PHPStan \Type \ArrayType ;
10+ use PHPStan \Type \BitwiseFlagHelper ;
1611use PHPStan \Type \Constant \ConstantArrayType ;
1712use PHPStan \Type \Constant \ConstantBooleanType ;
1813use PHPStan \Type \Constant \ConstantIntegerType ;
2217use PHPStan \Type \StringType ;
2318use PHPStan \Type \Type ;
2419use PHPStan \Type \TypeCombinator ;
25- use function sprintf ;
2620use function strtolower ;
2721
2822class PregSplitDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
2923{
3024
31- public function __construct (private ReflectionProvider $ reflectionProvider )
25+ public function __construct (
26+ private BitwiseFlagHelper $ bitwiseFlagAnalyser ,
27+ )
3228 {
3329 }
3430
35-
3631 public function isFunctionSupported (FunctionReflection $ functionReflection ): bool
3732 {
3833 return strtolower ($ functionReflection ->getName ()) === 'preg_split ' ;
3934 }
4035
41-
4236 public function getTypeFromFunctionCall (FunctionReflection $ functionReflection , FuncCall $ functionCall , Scope $ scope ): Type
4337 {
4438 $ flagsArg = $ functionCall ->getArgs ()[3 ] ?? null ;
4539
46- if ($ this ->hasFlag ( $ this -> getConstant ( ' PREG_SPLIT_OFFSET_CAPTURE ' ), $ flagsArg , $ scope )) {
40+ if ($ flagsArg !== null && $ this ->bitwiseFlagAnalyser -> bitwiseOrContainsConstant ( $ flagsArg-> value , $ scope, ' PREG_SPLIT_OFFSET_CAPTURE ' )-> yes ( )) {
4741 $ type = new ArrayType (
4842 new IntegerType (),
4943 new ConstantArrayType ([new ConstantIntegerType (0 ), new ConstantIntegerType (1 )], [new StringType (), IntegerRangeType::fromInterval (0 , null )]),
@@ -54,39 +48,4 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5448 return ParametersAcceptorSelector::selectSingle ($ functionReflection ->getVariants ())->getReturnType ();
5549 }
5650
57-
58- private function hasFlag (int $ flag , ?Arg $ arg , Scope $ scope ): bool
59- {
60- if ($ arg === null ) {
61- return false ;
62- }
63-
64- return $ this ->isConstantFlag ($ flag , $ arg ->value , $ scope );
65- }
66-
67- private function isConstantFlag (int $ flag , Expr $ expression , Scope $ scope ): bool
68- {
69- if ($ expression instanceof BitwiseOr) {
70- $ left = $ expression ->left ;
71- $ right = $ expression ->right ;
72-
73- return $ this ->isConstantFlag ($ flag , $ left , $ scope ) || $ this ->isConstantFlag ($ flag , $ right , $ scope );
74- }
75-
76- $ type = $ scope ->getType ($ expression );
77- return $ type instanceof ConstantIntegerType && ($ type ->getValue () & $ flag ) === $ flag ;
78- }
79-
80-
81- private function getConstant (string $ constantName ): int
82- {
83- $ constant = $ this ->reflectionProvider ->getConstant (new Name ($ constantName ), null );
84- $ valueType = $ constant ->getValueType ();
85- if (!$ valueType instanceof ConstantIntegerType) {
86- throw new ShouldNotHappenException (sprintf ('Constant %s does not have integer type. ' , $ constantName ));
87- }
88-
89- return $ valueType ->getValue ();
90- }
91-
9251}
0 commit comments