Skip to content

Commit 70bfb67

Browse files
authored
Merge pull request #12 from vkill/ignore-json-throw-on-error
Ignore json_decode and json_encode when use JSON_THROW_ON_ERROR option
2 parents 8bb68fb + e7fa736 commit 70bfb67

File tree

4 files changed

+79
-0
lines changed

4 files changed

+79
-0
lines changed

src/Rules/UseSafeFunctionsRule.php

+47
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\ShouldNotHappenException;
1212
use TheCodingMachine\Safe\PHPStan\Utils\FunctionListLoader;
13+
use PhpParser\Node\Arg;
14+
use PhpParser\Node\Expr;
15+
use PhpParser\Node\Scalar;
1316

1417
/**
1518
* This rule checks that no superglobals are used in code.
@@ -35,9 +38,53 @@ public function processNode(Node $node, Scope $scope): array
3538
$unsafeFunctions = FunctionListLoader::getFunctionList();
3639

3740
if (isset($unsafeFunctions[$functionName])) {
41+
if (version_compare(PHP_VERSION, '7.3.0', '>=')) {
42+
if ($functionName === "json_decode") {
43+
if (count($node->args) == 4) {
44+
if ($this->argValueIncludeJSONTHROWONERROR($node->args[3])) {
45+
return [];
46+
}
47+
}
48+
}
49+
if ($functionName === "json_encode") {
50+
if (count($node->args) >= 2) {
51+
if ($this->argValueIncludeJSONTHROWONERROR($node->args[1])) {
52+
return [];
53+
}
54+
}
55+
}
56+
}
57+
3858
return ["Function $functionName is unsafe to use. It can return FALSE instead of throwing an exception. Please add 'use function Safe\\$functionName;' at the beginning of the file to use the variant provided by the 'thecodingmachine/safe' library."];
3959
}
4060

4161
return [];
4262
}
63+
64+
private function argValueIncludeJSONTHROWONERROR(Arg $arg): bool
65+
{
66+
$parseValue = function ($expr, array $options) use (&$parseValue): array {
67+
if ($expr instanceof Expr\BinaryOp\BitwiseOr) {
68+
return array_merge($parseValue($expr->left, $options), $parseValue($expr->right, $options));
69+
} elseif ($expr instanceof Expr\ConstFetch) {
70+
return array_merge($options, $expr->name->parts);
71+
} elseif ($expr instanceof Scalar\LNumber) {
72+
return array_merge($options, [$expr->value]);
73+
} else {
74+
return $options;
75+
}
76+
};
77+
$options = $parseValue($arg->value, []);
78+
79+
if (in_array("JSON_THROW_ON_ERROR", $options)) {
80+
return true;
81+
}
82+
83+
return in_array(true, array_map(function ($element) {
84+
// JSON_THROW_ON_ERROR == 4194304
85+
return ($element & 4194304) == 4194304;
86+
}, array_filter($options, function ($element) {
87+
return is_int($element);
88+
})));
89+
}
4390
}

tests/Rules/UseSafeFunctionsRuleTest.php

+18
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,22 @@ public function testExprCall()
3131
{
3232
$this->analyse([__DIR__ . '/data/undirect_call.php'], []);
3333
}
34+
35+
public function testJSONDecodeNoCatchSafe()
36+
{
37+
if (version_compare(PHP_VERSION, '7.3.0', '>=')) {
38+
$this->analyse([__DIR__ . '/data/safe_json_decode_for_7.3.0.php'], []);
39+
} else {
40+
$this->assertTrue(true);
41+
}
42+
}
43+
44+
public function testJSONEncodeNoCatchSafe()
45+
{
46+
if (version_compare(PHP_VERSION, '7.3.0', '>=')) {
47+
$this->analyse([__DIR__ . '/data/safe_json_encode_for_7.3.0.php'], []);
48+
} else {
49+
$this->assertTrue(true);
50+
}
51+
}
3452
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
json_decode("{}", true, 512, JSON_THROW_ON_ERROR);
4+
json_decode("{}", true, 512, JSON_INVALID_UTF8_IGNORE | JSON_THROW_ON_ERROR);
5+
json_decode("{}", true, 512, JSON_INVALID_UTF8_IGNORE | JSON_OBJECT_AS_ARRAY | JSON_THROW_ON_ERROR);
6+
7+
json_decode("{}", true, 512, 4194304);
8+
json_decode("{}", true, 512, 1048576 | 4194304);
9+
json_decode("{}", true, 512, 1048576 | 1 | 4194304);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
json_encode([], JSON_THROW_ON_ERROR, 512);
4+
json_encode([], JSON_FORCE_OBJECT | JSON_THROW_ON_ERROR, 512);
5+
json_encode([], JSON_FORCE_OBJECT | JSON_INVALID_UTF8_IGNORE | JSON_THROW_ON_ERROR, 512);

0 commit comments

Comments
 (0)