diff --git a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/fixture.php.inc b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/fixture.php.inc index e47df536fe3..4447810e978 100644 --- a/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/fixture.php.inc +++ b/rules-tests/Php73/Rector/FuncCall/JsonThrowOnErrorRector/Fixture/fixture.php.inc @@ -10,6 +10,8 @@ function jsonThrowOnError() json_decode($json, true, 215); json_decode($json, true, 122, JSON_THROW_ON_ERROR); + + json_decode($json, true, 122, JSON_UNESCAPED_UNICODE); } ?> @@ -26,6 +28,8 @@ function jsonThrowOnError() json_decode($json, true, 215, JSON_THROW_ON_ERROR); json_decode($json, true, 122, JSON_THROW_ON_ERROR); + + json_decode($json, true, 122, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); } ?> diff --git a/rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php b/rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php index d79e97d8d57..82a5af714ce 100644 --- a/rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php +++ b/rules/Php73/Rector/FuncCall/JsonThrowOnErrorRector.php @@ -26,6 +26,7 @@ final class JsonThrowOnErrorRector extends AbstractRector implements MinPhpVersionInterface { private bool $hasChanged = false; + private const FLAGS = ['JSON_THROW_ON_ERROR']; public function __construct( private readonly ValueResolver $valueResolver, @@ -135,20 +136,22 @@ private function shouldSkipFuncCall(FuncCall $funcCall): bool private function processJsonEncode(FuncCall $funcCall): ?FuncCall { + $flags = []; if (isset($funcCall->args[1])) { - return null; + $flags = $this->getFlags($funcCall->args[1]); + } + if (!is_null($newArg = $this->getArgWithFlags($flags))) { + $this->hasChanged = true; + $funcCall->args[1] = $newArg; } - - $this->hasChanged = true; - - $funcCall->args[1] = new Arg($this->createConstFetch('JSON_THROW_ON_ERROR')); return $funcCall; } private function processJsonDecode(FuncCall $funcCall): ?FuncCall { + $flags = []; if (isset($funcCall->args[3])) { - return null; + $flags = $this->getFlags($funcCall->args[3]); } // set default to inter-args @@ -160,9 +163,10 @@ private function processJsonDecode(FuncCall $funcCall): ?FuncCall $funcCall->args[2] = new Arg(new Int_(512)); } - $this->hasChanged = true; - $funcCall->args[3] = new Arg($this->createConstFetch('JSON_THROW_ON_ERROR')); - + if (!is_null($newArg = $this->getArgWithFlags($flags))) { + $this->hasChanged = true; + $funcCall->args[3] = $newArg; + } return $funcCall; } @@ -186,4 +190,63 @@ private function isFirstValueStringOrArray(FuncCall $funcCall): bool return is_array($value); } + + private function getFlags(Arg|Node\Expr\BinaryOp\BitwiseOr|ConstFetch $arg, array $result = []): array + { + if ($arg instanceof ConstFetch) { + $constFetch = $arg; + } else { + if ($arg instanceof Arg) { + $array = $arg->value->jsonSerialize(); + } else { + $array = $arg->jsonSerialize(); + } + if ($arg->value instanceof ConstFetch) { // single flag + $constFetch = $arg->value; + } else { // multiple flag + $result = $this->getFlags($array['left'], $result); + $constFetch = $array['right']; + } + } + if (!is_null($constFetch)) { + $result[] = $constFetch->jsonSerialize()['name']->getFirst(); + } + return $result; + } + + private function getArgWithFlags(array $flags): Arg|null + { + $oldNbFlags = count($flags); + $flags = array_values(array_unique([...$flags, ...self::FLAGS])); + $newNbFlags = count($flags); + if ($oldNbFlags === $newNbFlags) { + return null; + } + if ($newNbFlags === 1) { + return new Arg($this->createConstFetch($flags[0])); + } + /** @var ConstFetch[] $constFetchs */ + $constFetchs = []; + foreach ($flags as $flag) { + $constFetchs[] = $this->createConstFetch($flag); + } + $result = null; + foreach ($constFetchs as $i => $constFetch) { + if ($i === 1) { + continue; + } + if (is_null($result)) { + $result = new Node\Expr\BinaryOp\BitwiseOr( + $constFetch, + $constFetchs[$i + 1], + ); + } else { + $result = new Node\Expr\BinaryOp\BitwiseOr( + $result, + $constFetch + ); + } + } + return new Arg($result); + } }