2
2
3
3
namespace PHPStan \Type \Php ;
4
4
5
+ <<<<<<< HEAD
5
6
<<<<<<< HEAD
6
7
=======
7
8
use PhpParser \Node \Arg ;
9
+ =======
10
+ use PhpParser \ConstExprEvaluator ;
11
+ >>>>>>> check for JSON_OBJECT_AS_ARRAY , in case of null and array
8
12
use PhpParser \Node \Expr;
9
13
use PhpParser \Node \Expr \BinaryOp \BitwiseOr ;
10
14
use PhpParser \Node \Expr \ConstFetch ;
35
39
use PHPStan \Type \Type ;
36
40
use PHPStan \Type \TypeCombinator ;
37
41
use stdClass ;
42
+ use function constant ;
38
43
use function json_decode ;
44
+ use const JSON_OBJECT_AS_ARRAY ;
39
45
40
46
class JsonThrowOnErrorDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
41
47
{
@@ -94,17 +100,17 @@ public function getTypeFromFunctionCall(
94
100
private function narrowTypeForJsonDecode (FuncCall $ funcCall , Scope $ scope ): Type
95
101
{
96
102
$ args = $ funcCall ->getArgs ();
97
- $ isForceArray = $ this ->isForceArray ($ funcCall );
103
+ $ isArrayWithoutStdClass = $ this ->isForceArrayWithoutStdClass ($ funcCall );
98
104
99
105
$ firstArgValue = $ args [0 ]->value ;
100
106
$ firstValueType = $ scope ->getType ($ firstArgValue );
101
107
102
108
if ($ firstValueType instanceof ConstantStringType) {
103
- return $ this ->resolveConstantStringType ($ firstValueType , $ isForceArray );
109
+ return $ this ->resolveConstantStringType ($ firstValueType , $ isArrayWithoutStdClass );
104
110
}
105
111
106
112
// fallback type
107
- if ($ isForceArray ) {
113
+ if ($ isArrayWithoutStdClass ) {
108
114
return new MixedType (true , new ObjectType (stdClass::class));
109
115
}
110
116
@@ -114,20 +120,45 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope): Type
114
120
/**
115
121
* Is "json_decode(..., true)"?
116
122
*/
117
- private function isForceArray (FuncCall $ funcCall ): bool
123
+ private function isForceArrayWithoutStdClass (FuncCall $ funcCall ): bool
118
124
{
119
125
$ args = $ funcCall ->getArgs ();
120
126
121
- if (! isset ($ args [1 ])) {
122
- return false ;
123
- }
124
-
125
- $ secondArgValue = $ args [1 ]->value ;
126
- if (! $ secondArgValue instanceof ConstFetch) {
127
- return false ;
127
+ $ constExprEvaluator = new ConstExprEvaluator (static function (Expr $ expr ) {
128
+ if ($ expr instanceof ConstFetch) {
129
+ return constant ($ expr ->name ->toString ());
130
+ }
131
+
132
+ return null ;
133
+ });
134
+
135
+ if (isset ($ args [1 ])) {
136
+ $ secondArgValue = $ args [1 ]->value ;
137
+
138
+ $ constValue = $ constExprEvaluator ->evaluateSilently ($ secondArgValue );
139
+ if ($ constValue === true ) {
140
+ return true ;
141
+ }
142
+
143
+ if ($ constValue === false ) {
144
+ return false ;
145
+ }
146
+
147
+ // depends on used constants
148
+ if ($ constValue === null ) {
149
+ if (! isset ($ args [3 ])) {
150
+ return false ;
151
+ }
152
+
153
+ // @see https://www.php.net/manual/en/json.constants.php#constant.json-object-as-array
154
+ $ thirdArgValue = $ constExprEvaluator ->evaluateSilently ($ args [3 ]->value );
155
+ if ($ thirdArgValue & JSON_OBJECT_AS_ARRAY ) {
156
+ return true ;
157
+ }
158
+ }
128
159
}
129
160
130
- return $ secondArgValue -> name -> toLowerString () === ' true ' ;
161
+ return false ;
131
162
}
132
163
133
164
private function resolveConstantStringType (ConstantStringType $ constantStringType , bool $ isForceArray ): Type
0 commit comments