Passed
Push — master ( 4b62dd...44767d )
by Sergei
02:42 queued 40s
created

Json::processArray()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 6
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 13
ccs 7
cts 7
cp 1
crap 4
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Json;
6
7
use DateTimeInterface;
8
use JsonException;
9
use JsonSerializable;
10
use SimpleXMLElement;
11
use stdClass;
12
use Traversable;
13
14
use function get_object_vars;
15
use function json_decode;
16
use function json_encode;
17
use function is_array;
18
use function is_object;
19
use function iterator_to_array;
20
21
/**
22
 * Json is a helper class providing JSON data encoding and decoding.
23
 * It enhances the PHP built-in functions `json_encode()` and `json_decode()`
24
 * by throwing exceptions when decoding fails.
25
 */
26
final class Json
27
{
28
    /**
29
     * Encodes the given value into a JSON string.
30
     *
31
     * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.
32
     * You must ensure strings passed to this method have proper encoding before passing them.
33
     *
34
     * @param mixed $value The data to be encoded.
35
     * @param int $options The encoding options. For more details please refer to
36
     * {@see http://www.php.net/manual/en/function.json-encode.php}.
37
     * Default is `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR`.
38
     * @param int $depth The maximum depth.
39
     *
40
     * @psalm-param int<1, 2147483647> $depth
41
     *
42
     * @throws JsonException if there is any encoding error.
43
     *
44
     * @return string The encoding result.
45
     */
46 25
    public static function encode(
47
        $value,
48
        int $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR,
49
        int $depth = 512
50
    ): string {
51 25
        if (is_array($value)) {
52 5
            $value = self::processArray($value);
53 21
        } elseif (is_object($value)) {
54
            /** @psalm-var mixed $value */
55 17
            $value = self::processObject($value);
56
        }
57
58 25
        return json_encode($value, JSON_THROW_ON_ERROR | $options, $depth);
59
    }
60
61
    /**
62
     * Encodes the given value into a JSON string HTML-escaping entities so it is safe to be embedded in HTML code.
63
     *
64
     * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.
65
     * You must ensure strings passed to this method have proper encoding before passing them.
66
     *
67
     * @param mixed $value The data to be encoded.
68
     *
69
     * @throws JsonException If there is any encoding error.
70
     *
71
     * @return string The encoding result.
72
     */
73 6
    public static function htmlEncode($value): string
74
    {
75 6
        return self::encode(
76 6
            $value,
77 6
            JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_THROW_ON_ERROR
78 6
        );
79
    }
80
81
    /**
82
     * Decodes the given JSON string into a PHP data structure.
83
     *
84
     * @param string $json The JSON string to be decoded.
85
     * @param bool $asArray Whether to return objects in terms of associative arrays.
86
     * @param int $depth The recursion depth.
87
     * @param int $options The decode options.
88
     *
89
     * @psalm-param int<1, 2147483647> $depth
90
     *
91
     * @throws JsonException If there is any decoding error.
92
     *
93
     * @return mixed The PHP data.
94
     */
95 6
    public static function decode(
96
        string $json,
97
        bool $asArray = true,
98
        int $depth = 512,
99
        int $options = JSON_THROW_ON_ERROR
100
    ) {
101 6
        if ($json === '') {
102 1
            return null;
103
        }
104 5
        return json_decode($json, $asArray, $depth, JSON_THROW_ON_ERROR | $options);
105
    }
106
107
    /**
108
     * Pre-processes the array before sending it to `json_encode()`.
109
     *
110
     * @param array $data The array to be processed.
111
     *
112
     * @return array The processed array.
113
     */
114 15
    private static function processArray(array $data): array
115
    {
116
        /** @psalm-var mixed $value */
117 15
        foreach ($data as $key => $value) {
118 10
            if (is_array($value)) {
119 2
                $data[$key] = self::processArray($value);
120 10
            } elseif (is_object($value)) {
121
                /** @psalm-var mixed */
122 2
                $data[$key] = self::processObject($value);
123
            }
124
        }
125
126 15
        return $data;
127
    }
128
129
    /**
130
     * Pre-processes the object before sending it to `json_encode()`.
131
     *
132
     * @param object $data The object to be processed.
133
     *
134
     * @return mixed The processed data.
135
     */
136 18
    private static function processObject(object $data)
137
    {
138 18
        if ($data instanceof JsonSerializable) {
139
            /** @psalm-var mixed $data */
140 6
            $data = $data->jsonSerialize();
141
142 6
            if (is_array($data)) {
143 4
                return self::processArray($data);
144
            }
145
146 2
            if (is_object($data)) {
147 1
                return self::processObject($data);
148
            }
149
150 1
            return $data;
151
        }
152
153 14
        if ($data instanceof DateTimeInterface) {
154 2
            return $data;
155
        }
156
157 12
        if ($data instanceof SimpleXMLElement) {
158 4
            return (array)$data ?: new stdClass();
159
        }
160
161 8
        if ($data instanceof Traversable) {
162 1
            return self::processArray(iterator_to_array($data)) ?: new stdClass();
163
        }
164
165 7
        return self::processArray(get_object_vars($data)) ?: new stdClass();
166
    }
167
}
168