9
9
use PHPStan \Reflection \FunctionReflection ;
10
10
use PHPStan \Reflection \ParametersAcceptorSelector ;
11
11
use PHPStan \Reflection \ReflectionProvider ;
12
+ use PHPStan \TrinaryLogic ;
12
13
use PHPStan \Type \BitwiseFlagHelper ;
13
14
use PHPStan \Type \Constant \ConstantBooleanType ;
14
15
use PHPStan \Type \Constant \ConstantStringType ;
15
- use PHPStan \Type \ConstantScalarType ;
16
16
use PHPStan \Type \ConstantTypeHelper ;
17
17
use PHPStan \Type \DynamicFunctionReturnTypeExtension ;
18
18
use PHPStan \Type \ObjectWithoutClassType ;
@@ -87,11 +87,17 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope, Type
87
87
}
88
88
89
89
$ firstValueType = $ scope ->getType ($ args [0 ]->value );
90
- if ($ firstValueType instanceof ConstantStringType) {
91
- return $ this ->resolveConstantStringType ($ firstValueType , $ isForceArray );
90
+ if ($ firstValueType ->getConstantStrings () !== []) {
91
+ $ types = [];
92
+
93
+ foreach ($ firstValueType ->getConstantStrings () as $ constantString ) {
94
+ $ types [] = $ this ->resolveConstantStringType ($ constantString , $ isForceArray );
95
+ }
96
+
97
+ return TypeCombinator::union (...$ types );
92
98
}
93
99
94
- if ($ isForceArray ) {
100
+ if ($ isForceArray-> yes () ) {
95
101
return TypeCombinator::remove ($ fallbackType , new ObjectWithoutClassType ());
96
102
}
97
103
@@ -101,33 +107,55 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope, Type
101
107
/**
102
108
* Is "json_decode(..., true)"?
103
109
*/
104
- private function isForceArray (FuncCall $ funcCall , Scope $ scope ): bool
110
+ private function isForceArray (FuncCall $ funcCall , Scope $ scope ): TrinaryLogic
105
111
{
106
112
$ args = $ funcCall ->getArgs ();
113
+ $ flagValue = $ this ->getFlagValue ($ funcCall , $ scope );
107
114
if (!isset ($ args [1 ])) {
108
- return false ;
115
+ return TrinaryLogic:: createNo () ;
109
116
}
110
117
111
118
$ secondArgType = $ scope ->getType ($ args [1 ]->value );
112
- $ secondArgValue = $ secondArgType instanceof ConstantScalarType ? $ secondArgType ->getValue () : 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
+ }
113
130
114
- if (is_bool ( $ secondArgValue ) ) {
115
- return $ secondArgValue ;
131
+ if ($ secondArgValues === [] ) {
132
+ return TrinaryLogic:: createNo () ;
116
133
}
117
134
118
- if ($ secondArgValue !== null || !isset ($ args [3 ])) {
119
- 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 );
120
145
}
121
146
122
- // depends on used constants, @see https://www.php.net/manual/en/json.constants.php#constant.json-object-as-array
123
- return $ this ->bitwiseFlagAnalyser ->bitwiseOrContainsConstant ($ args [3 ]->value , $ scope , 'JSON_OBJECT_AS_ARRAY ' )->yes ();
147
+ return TypeCombinator::union (...$ types );
124
148
}
125
149
126
- private function resolveConstantStringType ( ConstantStringType $ constantStringType , bool $ isForceArray ): Type
150
+ private function getFlagValue ( FuncCall $ funcCall , Scope $ scope ): TrinaryLogic
127
151
{
128
- $ decodedValue = json_decode ($ constantStringType ->getValue (), $ isForceArray );
152
+ $ args = $ funcCall ->getArgs ();
153
+ if (!isset ($ args [3 ])) {
154
+ return TrinaryLogic::createNo ();
155
+ }
129
156
130
- 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 ' );
131
159
}
132
160
133
161
}
0 commit comments