Passed
Pull Request — master (#41)
by Sergei
02:08
created

Json::processObject()   B

Complexity

Conditions 10
Paths 7

Size

Total Lines 30
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 10.0296

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 14
c 1
b 0
f 0
nc 7
nop 1
dl 0
loc 30
ccs 14
cts 15
cp 0.9333
crap 10.0296
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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