Completed
Push — master ( eed00d...bda091 )
by Dmitry
10:44
created

BaseJson   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 92.45%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 3
dl 0
loc 161
ccs 49
cts 53
cp 0.9245
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A encode() 0 13 2
A htmlEncode() 0 4 1
A decode() 0 12 4
B handleJsonError() 0 19 5
C processData() 0 37 12
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\helpers;
9
10
use yii\base\InvalidParamException;
11
use yii\base\Arrayable;
12
use yii\web\JsExpression;
13
14
/**
15
 * BaseJson provides concrete implementation for [[Json]].
16
 *
17
 * Do not use BaseJson. Use [[Json]] instead.
18
 *
19
 * @author Qiang Xue <[email protected]>
20
 * @since 2.0
21
 */
22
class BaseJson
23
{
24
    /**
25
     * List of JSON Error messages assigned to constant names for better handling of version differences
26
     * @var array
27
     * @since 2.0.7
28
     */
29
    public static $jsonErrorMessages = [
30
        'JSON_ERROR_DEPTH' => 'The maximum stack depth has been exceeded.',
31
        'JSON_ERROR_STATE_MISMATCH' => 'Invalid or malformed JSON.',
32
        'JSON_ERROR_CTRL_CHAR' => 'Control character error, possibly incorrectly encoded.',
33
        'JSON_ERROR_SYNTAX' => 'Syntax error.',
34
        'JSON_ERROR_UTF8' => 'Malformed UTF-8 characters, possibly incorrectly encoded.', // PHP 5.3.3
35
        'JSON_ERROR_RECURSION' => 'One or more recursive references in the value to be encoded.', // PHP 5.5.0
36
        'JSON_ERROR_INF_OR_NAN' => 'One or more NAN or INF values in the value to be encoded', // PHP 5.5.0
37
        'JSON_ERROR_UNSUPPORTED_TYPE' => 'A value of a type that cannot be encoded was given', // PHP 5.5.0
38
    ];
39
40
41
    /**
42
     * Encodes the given value into a JSON string.
43
     *
44
     * The method enhances `json_encode()` by supporting JavaScript expressions.
45
     * In particular, the method will not encode a JavaScript expression that is
46
     * represented in terms of a [[JsExpression]] object.
47
     *
48
     * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.
49
     * You must ensure strings passed to this method have proper encoding before passing them.
50
     *
51
     * @param mixed $value the data to be encoded.
52
     * @param int $options the encoding options. For more details please refer to
53
     * <http://www.php.net/manual/en/function.json-encode.php>. Default is `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`.
54
     * @return string the encoding result.
55
     * @throws InvalidParamException if there is any encoding error.
56
     */
57 27
    public static function encode($value, $options = 320)
58
    {
59 27
        $expressions = [];
60 27
        $value = static::processData($value, $expressions, uniqid('', true));
61 27
        set_error_handler(function () {
62
            static::handleJsonError(JSON_ERROR_SYNTAX);
63 27
        }, E_WARNING);
64 27
        $json = json_encode($value, $options);
65 27
        restore_error_handler();
66 27
        static::handleJsonError(json_last_error());
67
68 26
        return $expressions === [] ? $json : strtr($json, $expressions);
69
    }
70
71
    /**
72
     * Encodes the given value into a JSON string HTML-escaping entities so it is safe to be embedded in HTML code.
73
     *
74
     * The method enhances `json_encode()` by supporting JavaScript expressions.
75
     * In particular, the method will not encode a JavaScript expression that is
76
     * represented in terms of a [[JsExpression]] object.
77
     *
78
     * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.
79
     * You must ensure strings passed to this method have proper encoding before passing them.
80
     *
81
     * @param mixed $value the data to be encoded
82
     * @return string the encoding result
83
     * @since 2.0.4
84
     * @throws InvalidParamException if there is any encoding error
85
     */
86 4
    public static function htmlEncode($value)
87
    {
88 4
        return static::encode($value, JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS);
89
    }
90
91
    /**
92
     * Decodes the given JSON string into a PHP data structure.
93
     * @param string $json the JSON string to be decoded
94
     * @param bool $asArray whether to return objects in terms of associative arrays.
95
     * @return mixed the PHP data
96
     * @throws InvalidParamException if there is any decoding error
97
     */
98 10
    public static function decode($json, $asArray = true)
99
    {
100 10
        if (is_array($json)) {
101
            throw new InvalidParamException('Invalid JSON data.');
102 10
        } elseif ($json === null || $json === '') {
103
            return null;
104
        }
105 10
        $decode = json_decode((string) $json, $asArray);
106 10
        static::handleJsonError(json_last_error());
107
108 9
        return $decode;
109
    }
110
111
    /**
112
     * Handles [[encode()]] and [[decode()]] errors by throwing exceptions with the respective error message.
113
     *
114
     * @param int $lastError error code from [json_last_error()](http://php.net/manual/en/function.json-last-error.php).
115
     * @throws \yii\base\InvalidParamException if there is any encoding/decoding error.
116
     * @since 2.0.6
117
     */
118 36
    protected static function handleJsonError($lastError)
119
    {
120 36
        if ($lastError === JSON_ERROR_NONE) {
121 35
            return;
122
        }
123
124 2
        $availableErrors = [];
125 2
        foreach (static::$jsonErrorMessages as $const => $message) {
126 2
            if (defined($const)) {
127 2
                $availableErrors[constant($const)] = $message;
128
            }
129
        }
130
131 2
        if (isset($availableErrors[$lastError])) {
132 2
            throw new InvalidParamException($availableErrors[$lastError], $lastError);
133
        }
134
135
        throw new InvalidParamException('Unknown JSON encoding/decoding error.');
136
    }
137
138
    /**
139
     * Pre-processes the data before sending it to `json_encode()`.
140
     * @param mixed $data the data to be processed
141
     * @param array $expressions collection of JavaScript expressions
142
     * @param string $expPrefix a prefix internally used to handle JS expressions
143
     * @return mixed the processed data
144
     */
145 27
    protected static function processData($data, &$expressions, $expPrefix)
146
    {
147 27
        if (is_object($data)) {
148 8
            if ($data instanceof JsExpression) {
149 3
                $token = "!{[$expPrefix=" . count($expressions) . ']}!';
150 3
                $expressions['"' . $token . '"'] = $data->expression;
151
152 3
                return $token;
153 7
            } elseif ($data instanceof \JsonSerializable) {
154 2
                return static::processData($data->jsonSerialize(), $expressions, $expPrefix);
155 7
            } elseif ($data instanceof Arrayable) {
156 1
                $data = $data->toArray();
157 6
            } elseif ($data instanceof \SimpleXMLElement) {
158 1
                $data = (array) $data;
159
            } else {
160 6
                $result = [];
161 6
                foreach ($data as $name => $value) {
162 6
                    $result[$name] = $value;
163
                }
164 6
                $data = $result;
165
            }
166
167 7
            if ($data === []) {
168 2
                return new \stdClass();
169
            }
170
        }
171
172 27
        if (is_array($data)) {
173 23
            foreach ($data as $key => $value) {
174 21
                if (is_array($value) || is_object($value)) {
175 10
                    $data[$key] = static::processData($value, $expressions, $expPrefix);
176
                }
177
            }
178
        }
179
180 27
        return $data;
181
    }
182
}
183