Skip to content

Commit b9665f5

Browse files
committed
handle multiple values in second argument
1 parent 157be8b commit b9665f5

File tree

2 files changed

+51
-15
lines changed

2 files changed

+51
-15
lines changed

src/Type/Php/JsonThrowOnErrorDynamicReturnTypeExtension.php

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PHPStan\Reflection\FunctionReflection;
1010
use PHPStan\Reflection\ParametersAcceptorSelector;
1111
use PHPStan\Reflection\ReflectionProvider;
12+
use PHPStan\TrinaryLogic;
1213
use PHPStan\Type\BitwiseFlagHelper;
1314
use PHPStan\Type\Constant\ConstantBooleanType;
1415
use PHPStan\Type\Constant\ConstantStringType;
@@ -17,7 +18,6 @@
1718
use PHPStan\Type\ObjectWithoutClassType;
1819
use PHPStan\Type\Type;
1920
use PHPStan\Type\TypeCombinator;
20-
use function count;
2121
use function is_bool;
2222
use 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
}

tests/PHPStan/Analyser/nsrt/json-decode/narrow_type.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,25 @@ function ($mixed) {
3333
assertType('mixed', $value);
3434
};
3535

36-
function(string $json): void {
36+
function ($mixed, $asArray) {
37+
$value = json_decode($mixed, $asArray);
38+
assertType('mixed', $value);
39+
};
40+
41+
function(string $json, ?bool $asArray): void {
3742
/** @var '{}'|'null' $json */
3843
$value = json_decode($json);
3944
assertType('stdClass|null', $value);
4045

4146
$value = json_decode($json, true);
4247
assertType('array{}|null', $value);
48+
49+
$value = json_decode($json, flags: JSON_OBJECT_AS_ARRAY);
50+
assertType('array{}|null', $value);
51+
52+
$value = json_decode($json, $asArray);
53+
assertType('array{}|stdClass|null', $value);
54+
55+
$value = json_decode($json, 'foo');
56+
assertType('stdClass|null', $value);
4357
};

0 commit comments

Comments
 (0)