2
2
3
3
namespace PHPStan \Type \Php ;
4
4
5
+ <<<<<<< HEAD
5
6
<<<<<<< HEAD
6
7
<<<<<<< HEAD
7
8
=======
8
9
use PhpParser \Node \Arg ;
9
10
=======
11
+ =======
12
+ use PhpParser \ConstExprEvaluationException ;
13
+ >>>>>>> decopule type resolution to static
10
14
use PhpParser \ConstExprEvaluator ;
11
15
>>>>>>> check for JSON_OBJECT_AS_ARRAY , in case of null and array
12
16
use PhpParser \Node \Expr;
40
44
use PHPStan \Type \TypeCombinator ;
41
45
use stdClass ;
42
46
use function constant ;
47
+ use function is_bool ;
43
48
use function json_decode ;
44
49
use const JSON_OBJECT_AS_ARRAY ;
45
50
46
51
class JsonThrowOnErrorDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
47
52
{
48
53
54
+ private const UNABLE_TO_RESOLVE = '__UNABLE_TO_RESOLVE__ ' ;
55
+
49
56
/** @var array<string, int> */
50
57
private array $ argumentPositions = [
51
58
'json_encode ' => 1 ,
52
59
'json_decode ' => 3 ,
53
60
];
54
61
62
+ <<<<<<< HEAD
55
63
public function __construct (
56
64
private ReflectionProvider $ reflectionProvider ,
57
65
private BitwiseFlagHelper $ bitwiseFlagAnalyser ,
58
66
)
67
+ =======
68
+ private ConstExprEvaluator $ constExprEvaluator ;
69
+
70
+ public function __construct(private ReflectionProvider $ reflectionProvider )
71
+ >>>>>>> decopule type resolution to static
59
72
{
73
+ $ this ->constExprEvaluator = new ConstExprEvaluator (static function (Expr $ expr ) {
74
+ if ($ expr instanceof ConstFetch) {
75
+ return constant ($ expr ->name ->toString ());
76
+ }
77
+
78
+ return null ;
79
+ });
60
80
}
61
81
62
82
public function isFunctionSupported (
@@ -100,7 +120,7 @@ public function getTypeFromFunctionCall(
100
120
private function narrowTypeForJsonDecode (FuncCall $ funcCall , Scope $ scope ): Type
101
121
{
102
122
$ args = $ funcCall ->getArgs ();
103
- $ isArrayWithoutStdClass = $ this ->isForceArrayWithoutStdClass ($ funcCall );
123
+ $ isArrayWithoutStdClass = $ this ->isForceArrayWithoutStdClass ($ funcCall, $ scope );
104
124
105
125
$ firstArgValue = $ args [0 ]->value ;
106
126
$ firstValueType = $ scope ->getType ($ firstArgValue );
@@ -110,7 +130,7 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope): Type
110
130
}
111
131
112
132
// fallback type
113
- if ($ isArrayWithoutStdClass ) {
133
+ if ($ isArrayWithoutStdClass === true ) {
114
134
return new MixedType (true , new ObjectType (stdClass::class));
115
135
}
116
136
@@ -120,39 +140,30 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope): Type
120
140
/**
121
141
* Is "json_decode(..., true)"?
122
142
*/
123
- private function isForceArrayWithoutStdClass (FuncCall $ funcCall ): bool
143
+ private function isForceArrayWithoutStdClass (FuncCall $ funcCall, Scope $ scope ): bool
124
144
{
125
145
$ args = $ funcCall ->getArgs ();
126
146
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
147
if (isset ($ args [1 ])) {
136
- $ secondArgValue = $ args [1 ]->value ;
137
-
138
- $ constValue = $ constExprEvaluator ->evaluateSilently ($ secondArgValue );
139
- if ($ constValue === true ) {
140
- return true ;
148
+ $ secondArgValue = $ this ->resolveMaskValue ($ args [1 ]->value , $ scope );
149
+ if ($ secondArgValue === self ::UNABLE_TO_RESOLVE ) {
150
+ return false ;
141
151
}
142
152
143
- if ($ constValue === false ) {
144
- return false ;
153
+ if (is_bool ( $ secondArgValue ) ) {
154
+ return $ secondArgValue ;
145
155
}
146
156
147
157
// depends on used constants
148
- if ($ constValue === null ) {
158
+ if ($ secondArgValue === null ) {
149
159
if (! isset ($ args [3 ])) {
150
160
return false ;
151
161
}
152
162
153
163
// @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 ) {
164
+ $ thirdArgValue = $ args [3 ]->value ;
165
+ $ resolvedThirdArgValue = $ this ->resolveMaskValue ($ thirdArgValue , $ scope );
166
+ if (($ resolvedThirdArgValue & JSON_OBJECT_AS_ARRAY ) !== 0 ) {
156
167
return true ;
157
168
}
158
169
}
@@ -168,4 +179,23 @@ private function resolveConstantStringType(ConstantStringType $constantStringTyp
168
179
return ConstantTypeHelper::getTypeFromValue ($ decodedValue );
169
180
}
170
181
182
+ /**
183
+ * @return mixed
184
+ */
185
+ private function resolveMaskValue (Expr $ expr , Scope $ scope )
186
+ {
187
+ $ thirdArgValueType = $ scope ->getType ($ expr );
188
+ if ($ thirdArgValueType instanceof ConstantIntegerType) {
189
+ return $ thirdArgValueType ->getValue ();
190
+ }
191
+
192
+ // fallback to value resolver
193
+ try {
194
+ return $ this ->constExprEvaluator ->evaluateSilently ($ expr );
195
+ } catch (ConstExprEvaluationException ) {
196
+ // unable to resolve
197
+ return self ::UNABLE_TO_RESOLVE ;
198
+ }
199
+ }
200
+
171
201
}
0 commit comments