Completed
Push — 2.1 ( d5f162...fcc12f )
by
unknown
49:41 queued 41:18
created

BaseJson::htmlEncode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
155 2
                return static::processData($data->jsonSerialize(), $expressions, $expPrefix);
156 7
            } elseif ($data instanceof Arrayable) {
157 2
                $data = $data->toArray();
158 6
            } elseif ($data instanceof \SimpleXMLElement) {
159 1
                $data = (array) $data;
160
            } else {
161 6
                $result = [];
162 6
                foreach ($data as $name => $value) {
163 6
                    $result[$name] = $value;
164
                }
165 6
                $data = $result;
166
            }
167
168 7
            if ($data === []) {
169 2
                return new \stdClass();
170
            }
171
        }
172
173 38
        if (is_array($data)) {
174 31
            foreach ($data as $key => $value) {
175 24
                if (is_array($value) || is_object($value)) {
176 24
                    $data[$key] = static::processData($value, $expressions, $expPrefix);
177
                }
178
            }
179
        }
180
181 38
        return $data;
182
    }
183
184
    /**
185
     * Generates a summary of the validation errors.
186
     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
187
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
188
     *
189
     * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise
190
     *   only the first error message for each attribute will be shown. Defaults to `false`.
191
     *
192
     * @return string the generated error summary
193
     * @since 2.0.14
194
     */
195 1
    public static function errorSummary($models, $options = [])
196
    {
197 1
        $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
198 1
        $lines = self::collectErrors($models, $showAllErrors);
199
200 1
        return json_encode($lines);
201
    }
202
203
    /**
204
     * Return array of the validation errors
205
     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
206
     * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise
207
     * only the first error message for each attribute will be shown.
208
     * @return array of the validation errors
209
     * @since 2.0.14
210
     */
211 1
    private static function collectErrors($models, $showAllErrors)
212
    {
213 1
        $lines = [];
214 1
        if (!is_array($models)) {
215 1
            $models = [$models];
216
        }
217
218 1
        foreach ($models as $model) {
219 1
            $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));
220
        }
221
222 1
        return $lines;
223
    }
224
}
225