22
33namespace PHPStan \Type \Php ;
44
5+ <<<<<<< HEAD
56<<<<<<< HEAD
67<<<<<<< HEAD
78=======
89use PhpParser \Node \Arg ;
910=======
11+ =======
12+ use PhpParser \ConstExprEvaluationException ;
13+ >>>>>>> decopule type resolution to static
1014use PhpParser \ConstExprEvaluator ;
1115>>>>>>> check for JSON_OBJECT_AS_ARRAY , in case of null and array
1216use PhpParser \Node \Expr;
4044use PHPStan \Type \TypeCombinator ;
4145use stdClass ;
4246use function constant ;
47+ use function is_bool ;
4348use function json_decode ;
4449use const JSON_OBJECT_AS_ARRAY ;
4550
4651class JsonThrowOnErrorDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
4752{
4853
54+ private const UNABLE_TO_RESOLVE = '__UNABLE_TO_RESOLVE__ ' ;
55+
4956 /** @var array<string, int> */
5057 private array $ argumentPositions = [
5158 'json_encode ' => 1 ,
5259 'json_decode ' => 3 ,
5360 ];
5461
62+ <<<<<<< HEAD
5563 public function __construct (
5664 private ReflectionProvider $ reflectionProvider ,
5765 private BitwiseFlagHelper $ bitwiseFlagAnalyser ,
5866 )
67+ =======
68+ private ConstExprEvaluator $ constExprEvaluator ;
69+
70+ public function __construct(private ReflectionProvider $ reflectionProvider )
71+ >>>>>>> decopule type resolution to static
5972 {
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+ });
6080 }
6181
6282 public function isFunctionSupported (
@@ -100,7 +120,7 @@ public function getTypeFromFunctionCall(
100120 private function narrowTypeForJsonDecode (FuncCall $ funcCall , Scope $ scope ): Type
101121 {
102122 $ args = $ funcCall ->getArgs ();
103- $ isArrayWithoutStdClass = $ this ->isForceArrayWithoutStdClass ($ funcCall );
123+ $ isArrayWithoutStdClass = $ this ->isForceArrayWithoutStdClass ($ funcCall, $ scope );
104124
105125 $ firstArgValue = $ args [0 ]->value ;
106126 $ firstValueType = $ scope ->getType ($ firstArgValue );
@@ -110,7 +130,7 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope): Type
110130 }
111131
112132 // fallback type
113- if ($ isArrayWithoutStdClass ) {
133+ if ($ isArrayWithoutStdClass === true ) {
114134 return new MixedType (true , new ObjectType (stdClass::class));
115135 }
116136
@@ -120,39 +140,30 @@ private function narrowTypeForJsonDecode(FuncCall $funcCall, Scope $scope): Type
120140 /**
121141 * Is "json_decode(..., true)"?
122142 */
123- private function isForceArrayWithoutStdClass (FuncCall $ funcCall ): bool
143+ private function isForceArrayWithoutStdClass (FuncCall $ funcCall, Scope $ scope ): bool
124144 {
125145 $ args = $ funcCall ->getArgs ();
126146
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-
135147 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 ;
141151 }
142152
143- if ($ constValue === false ) {
144- return false ;
153+ if (is_bool ( $ secondArgValue ) ) {
154+ return $ secondArgValue ;
145155 }
146156
147157 // depends on used constants
148- if ($ constValue === null ) {
158+ if ($ secondArgValue === null ) {
149159 if (! isset ($ args [3 ])) {
150160 return false ;
151161 }
152162
153163 // @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 ) {
156167 return true ;
157168 }
158169 }
@@ -168,4 +179,23 @@ private function resolveConstantStringType(ConstantStringType $constantStringTyp
168179 return ConstantTypeHelper::getTypeFromValue ($ decodedValue );
169180 }
170181
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+
171201}
0 commit comments