99use PHPStan \Reflection \FunctionReflection ;
1010use PHPStan \Reflection \ParametersAcceptorSelector ;
1111use PHPStan \Reflection \ReflectionProvider ;
12+ use PHPStan \TrinaryLogic ;
1213use PHPStan \Type \BitwiseFlagHelper ;
1314use PHPStan \Type \Constant \ConstantBooleanType ;
1415use PHPStan \Type \Constant \ConstantStringType ;
1718use PHPStan \Type \ObjectWithoutClassType ;
1819use PHPStan \Type \Type ;
1920use PHPStan \Type \TypeCombinator ;
20- use function count ;
2121use function is_bool ;
2222use function json_decode ;
2323
@@ -97,7 +97,7 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope, Type
9797 return TypeCombinator::union (...$ types );
9898 }
9999
100- if ($ isForceArray ) {
100+ if ($ isForceArray-> yes () ) {
101101 return TypeCombinator::remove ($ fallbackType , new ObjectWithoutClassType ());
102102 }
103103
@@ -107,33 +107,55 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope, Type
107107 /**
108108 * Is "json_decode(..., true)"?
109109 */
110- private function isForceArray (FuncCall $ funcCall , Scope $ scope ): bool
110+ private function isForceArray (FuncCall $ funcCall , Scope $ scope ): TrinaryLogic
111111 {
112112 $ args = $ funcCall ->getArgs ();
113+ $ flagValue = $ this ->getFlagValue ($ funcCall , $ scope );
113114 if (!isset ($ args [1 ])) {
114- return false ;
115+ return TrinaryLogic:: createNo () ;
115116 }
116117
117118 $ secondArgType = $ scope ->getType ($ args [1 ]->value );
118- $ secondArgValue = count ($ secondArgType ->getConstantScalarValues ()) === 1 ? $ secondArgType ->getConstantScalarValues ()[0 ] : null ;
119+ $ secondArgValues = [];
120+ foreach ($ secondArgType ->getConstantScalarValues () as $ value ) {
121+ if ($ value === null ) {
122+ $ secondArgValues [] = $ flagValue ;
123+ continue ;
124+ }
125+ if (!is_bool ($ value )) {
126+ return TrinaryLogic::createNo ();
127+ }
128+ $ secondArgValues [] = TrinaryLogic::createFromBoolean ($ value );
129+ }
119130
120- if (is_bool ( $ secondArgValue ) ) {
121- return $ secondArgValue ;
131+ if ($ secondArgValues === [] ) {
132+ return TrinaryLogic:: createNo () ;
122133 }
123134
124- if ($ secondArgValue !== null || !isset ($ args [3 ])) {
125- return false ;
135+ return TrinaryLogic::extremeIdentity (...$ secondArgValues );
136+ }
137+
138+ private function resolveConstantStringType (ConstantStringType $ constantStringType , TrinaryLogic $ isForceArray ): Type
139+ {
140+ $ types = [];
141+ /** @var bool $asArray */
142+ foreach ($ isForceArray ->toBooleanType ()->getConstantScalarValues () as $ asArray ) {
143+ $ decodedValue = json_decode ($ constantStringType ->getValue (), $ asArray );
144+ $ types [] = ConstantTypeHelper::getTypeFromValue ($ decodedValue );
126145 }
127146
128- // depends on used constants, @see https://www.php.net/manual/en/json.constants.php#constant.json-object-as-array
129- return $ this ->bitwiseFlagAnalyser ->bitwiseOrContainsConstant ($ args [3 ]->value , $ scope , 'JSON_OBJECT_AS_ARRAY ' )->yes ();
147+ return TypeCombinator::union (...$ types );
130148 }
131149
132- private function resolveConstantStringType ( ConstantStringType $ constantStringType , bool $ isForceArray ): Type
150+ private function getFlagValue ( FuncCall $ funcCall , Scope $ scope ): TrinaryLogic
133151 {
134- $ decodedValue = json_decode ($ constantStringType ->getValue (), $ isForceArray );
152+ $ args = $ funcCall ->getArgs ();
153+ if (!isset ($ args [3 ])) {
154+ return TrinaryLogic::createNo ();
155+ }
135156
136- return ConstantTypeHelper::getTypeFromValue ($ decodedValue );
157+ // depends on used constants, @see https://www.php.net/manual/en/json.constants.php#constant.json-object-as-array
158+ return $ this ->bitwiseFlagAnalyser ->bitwiseOrContainsConstant ($ args [3 ]->value , $ scope , 'JSON_OBJECT_AS_ARRAY ' );
137159 }
138160
139161}
0 commit comments