Passed
Pull Request — master (#25)
by Alexander
07:37
created

Json   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 121
Duplicated Lines 0 %

Test Coverage

Coverage 91.67%

Importance

Changes 6
Bugs 1 Features 0
Metric Value
wmc 17
eloc 34
c 6
b 1
f 0
dl 0
loc 121
ccs 33
cts 36
cp 0.9167
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A encode() 0 9 2
A decode() 0 10 2
A htmlEncode() 0 5 1
C processData() 0 39 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Json;
6
7
/**
8
 * Json is a helper class providing JSON data encoding and decoding.
9
 * It enhances the PHP built-in functions `json_encode()` and `json_decode()`
10
 * by throwing exceptions when decoding fails.
11
 */
12
final class Json
13
{
14
    /**
15
     * Encodes the given value into a JSON string.
16
     *
17
     * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.
18
     * You must ensure strings passed to this method have proper encoding before passing them.
19
     *
20
     * @param mixed $value The data to be encoded.
21
     * @param int $options The encoding options. For more details please refer to
22
     * {@see http://www.php.net/manual/en/function.json-encode.php}.
23
     * Default is `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR`.
24
     * @param int $depth The maximum depth.
25
     *
26
     * @throws \JsonException if there is any encoding error.
27
     *
28
     * @return string The encoding result.
29
     */
30 21
    public static function encode(
31
        $value,
32
        int $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR,
33
        int $depth = 512
34
    ): string {
35 21
        $expressions = [];
36 21
        $value = self::processData($value, $expressions, uniqid('', true));
37 21
        $json = json_encode($value, JSON_THROW_ON_ERROR | $options, $depth);
38 20
        return $expressions === [] ? $json : strtr($json, $expressions);
39
    }
40
41
    /**
42
     * Encodes the given value into a JSON string HTML-escaping entities so it is safe to be embedded in HTML code.
43
     *
44
     * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.
45
     * You must ensure strings passed to this method have proper encoding before passing them.
46
     *
47
     * @param mixed $value The data to be encoded.
48
     *
49
     * @throws \JsonException If there is any encoding error.
50
     *
51
     * @return string The encoding result.
52
     */
53 6
    public static function htmlEncode($value): string
54
    {
55 6
        return self::encode(
56 6
            $value,
57 6
            JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_THROW_ON_ERROR
58
        );
59
    }
60
61
    /**
62
     * Decodes the given JSON string into a PHP data structure.
63
     *
64
     * @param string $json The JSON string to be decoded.
65
     * @param bool $asArray Whether to return objects in terms of associative arrays.
66
     * @param int $depth The recursion depth.
67
     * @param int $options The decode options.
68
     *
69
     * @throws \JsonException If there is any decoding error.
70
     *
71
     * @return mixed The PHP data.
72
     */
73 6
    public static function decode(
74
        string $json,
75
        bool $asArray = true,
76
        int $depth = 512,
77
        int $options = JSON_THROW_ON_ERROR
78
    ) {
79 6
        if ($json === '') {
80 1
            return null;
81
        }
82 5
        return json_decode($json, $asArray, $depth, JSON_THROW_ON_ERROR | $options);
83
    }
84
85
    /**
86
     * Pre-processes the data before sending it to `json_encode()`.
87
     *
88
     * @param mixed $data The data to be processed.
89
     * @param array $expressions collection of JavaScript expressions
90
     * @param string $expPrefix a prefix internally used to handle JS expressions
91
     *
92
     * @return mixed The processed data.
93
     */
94 21
    private static function processData($data, &$expressions, $expPrefix)
95
    {
96 21
        if (\is_object($data)) {
97 14
            if ($data instanceof JsExpression) {
98
                $token = "!{[$expPrefix=" . count($expressions) . ']}!';
99
                $expressions['"' . $token . '"'] = $data->expression;
100
101
                return $token;
102
            }
103
104 14
            if ($data instanceof \JsonSerializable) {
105 5
                return self::processData($data->jsonSerialize(), $expressions, $expPrefix);
106
            }
107
108 11
            if ($data instanceof \DateTimeInterface) {
109 1
                return self::processData((array)$data, $expressions, $expPrefix);
110
            }
111
112 10
            if ($data instanceof \SimpleXMLElement) {
113 3
                $data = (array)$data;
114
            } else {
115 7
                $result = [];
116 7
                foreach ($data as $name => $value) {
117 3
                    $result[$name] = $value;
118
                }
119 7
                $data = $result;
120
            }
121 10
            if ($data === []) {
122 4
                return new \stdClass();
123
            }
124
        }
125 18
        if (\is_array($data)) {
126 14
            foreach ($data as $key => $value) {
127 12
                if (\is_array($value) || \is_object($value)) {
128 3
                    $data[$key] = self::processData($value, $expressions, $expPrefix);
129
                }
130
            }
131
        }
132 18
        return $data;
133
    }
134
}
135