/src/php-src/ext/json/json.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright (c) The PHP Group | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to version 3.01 of the PHP license, | |
6 | | | that is bundled with this package in the file LICENSE, and is | |
7 | | | available through the world-wide-web at the following url: | |
8 | | | http://www.php.net/license/3_01.txt | |
9 | | | If you did not receive a copy of the PHP license and are unable to | |
10 | | | obtain it through the world-wide-web, please send a note to | |
11 | | | license@php.net so we can mail you a copy immediately. | |
12 | | +----------------------------------------------------------------------+ |
13 | | | Author: Omar Kilani <omar@php.net> | |
14 | | | Jakub Zelenka <bukka@php.net> | |
15 | | +----------------------------------------------------------------------+ |
16 | | */ |
17 | | |
18 | | #ifdef HAVE_CONFIG_H |
19 | | #include "config.h" |
20 | | #endif |
21 | | |
22 | | #include "php.h" |
23 | | #include "php_ini.h" |
24 | | #include "ext/standard/info.h" |
25 | | #include "ext/standard/html.h" |
26 | | #include "zend_smart_str.h" |
27 | | #include "php_json.h" |
28 | | #include "php_json_encoder.h" |
29 | | #include "php_json_parser.h" |
30 | | #include "json_arginfo.h" |
31 | | #include <zend_exceptions.h> |
32 | | |
33 | | static PHP_MINFO_FUNCTION(json); |
34 | | |
35 | | PHP_JSON_API zend_class_entry *php_json_serializable_ce; |
36 | | PHP_JSON_API zend_class_entry *php_json_exception_ce; |
37 | | |
38 | | PHP_JSON_API ZEND_DECLARE_MODULE_GLOBALS(json) |
39 | | |
40 | | /* Register constant for options and errors */ |
41 | | #define PHP_JSON_REGISTER_CONSTANT(_name, _value) \ |
42 | 137k | REGISTER_LONG_CONSTANT(_name, _value, CONST_CS | CONST_PERSISTENT); |
43 | | |
44 | | /* {{{ MINIT */ |
45 | | static PHP_MINIT_FUNCTION(json) |
46 | 4.89k | { |
47 | 4.89k | zend_class_entry ce; |
48 | | |
49 | 4.89k | INIT_CLASS_ENTRY(ce, "JsonSerializable", class_JsonSerializable_methods); |
50 | 4.89k | php_json_serializable_ce = zend_register_internal_interface(&ce); |
51 | | |
52 | 4.89k | INIT_CLASS_ENTRY(ce, "JsonException", NULL); |
53 | 4.89k | php_json_exception_ce = zend_register_internal_class_ex(&ce, zend_ce_exception); |
54 | | |
55 | | /* options for json_encode */ |
56 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_HEX_TAG", PHP_JSON_HEX_TAG); |
57 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_HEX_AMP", PHP_JSON_HEX_AMP); |
58 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS); |
59 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT); |
60 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_FORCE_OBJECT", PHP_JSON_FORCE_OBJECT); |
61 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK); |
62 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_UNESCAPED_SLASHES", PHP_JSON_UNESCAPED_SLASHES); |
63 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_PRETTY_PRINT", PHP_JSON_PRETTY_PRINT); |
64 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE); |
65 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_PARTIAL_OUTPUT_ON_ERROR", PHP_JSON_PARTIAL_OUTPUT_ON_ERROR); |
66 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_PRESERVE_ZERO_FRACTION", PHP_JSON_PRESERVE_ZERO_FRACTION); |
67 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_UNESCAPED_LINE_TERMINATORS", PHP_JSON_UNESCAPED_LINE_TERMINATORS); |
68 | | |
69 | | /* options for json_decode */ |
70 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_OBJECT_AS_ARRAY", PHP_JSON_OBJECT_AS_ARRAY); |
71 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_BIGINT_AS_STRING", PHP_JSON_BIGINT_AS_STRING); |
72 | | |
73 | | /* common options for json_decode and json_encode */ |
74 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_INVALID_UTF8_IGNORE", PHP_JSON_INVALID_UTF8_IGNORE); |
75 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_INVALID_UTF8_SUBSTITUTE", PHP_JSON_INVALID_UTF8_SUBSTITUTE); |
76 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_THROW_ON_ERROR", PHP_JSON_THROW_ON_ERROR); |
77 | | |
78 | | /* json error constants */ |
79 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE); |
80 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH); |
81 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH); |
82 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR); |
83 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX); |
84 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8); |
85 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_RECURSION", PHP_JSON_ERROR_RECURSION); |
86 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_INF_OR_NAN", PHP_JSON_ERROR_INF_OR_NAN); |
87 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_UNSUPPORTED_TYPE", PHP_JSON_ERROR_UNSUPPORTED_TYPE); |
88 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_INVALID_PROPERTY_NAME", PHP_JSON_ERROR_INVALID_PROPERTY_NAME); |
89 | 4.89k | PHP_JSON_REGISTER_CONSTANT("JSON_ERROR_UTF16", PHP_JSON_ERROR_UTF16); |
90 | | |
91 | 4.89k | return SUCCESS; |
92 | 4.89k | } |
93 | | /* }}} */ |
94 | | |
95 | | /* {{{ PHP_GINIT_FUNCTION */ |
96 | | static PHP_GINIT_FUNCTION(json) |
97 | 4.89k | { |
98 | | #if defined(COMPILE_DL_JSON) && defined(ZTS) |
99 | | ZEND_TSRMLS_CACHE_UPDATE(); |
100 | | #endif |
101 | 4.89k | json_globals->encoder_depth = 0; |
102 | 4.89k | json_globals->error_code = 0; |
103 | 4.89k | json_globals->encode_max_depth = PHP_JSON_PARSER_DEFAULT_DEPTH; |
104 | 4.89k | } |
105 | | /* }}} */ |
106 | | |
107 | | |
108 | | /* {{{ json_module_entry */ |
109 | | zend_module_entry json_module_entry = { |
110 | | STANDARD_MODULE_HEADER, |
111 | | "json", |
112 | | ext_functions, |
113 | | PHP_MINIT(json), |
114 | | NULL, |
115 | | NULL, |
116 | | NULL, |
117 | | PHP_MINFO(json), |
118 | | PHP_JSON_VERSION, |
119 | | PHP_MODULE_GLOBALS(json), |
120 | | PHP_GINIT(json), |
121 | | NULL, |
122 | | NULL, |
123 | | STANDARD_MODULE_PROPERTIES_EX |
124 | | }; |
125 | | /* }}} */ |
126 | | |
127 | | #ifdef COMPILE_DL_JSON |
128 | | #ifdef ZTS |
129 | | ZEND_TSRMLS_CACHE_DEFINE() |
130 | | #endif |
131 | | ZEND_GET_MODULE(json) |
132 | | #endif |
133 | | |
134 | | /* {{{ PHP_MINFO_FUNCTION */ |
135 | | static PHP_MINFO_FUNCTION(json) |
136 | 27 | { |
137 | 27 | php_info_print_table_start(); |
138 | 27 | php_info_print_table_row(2, "json support", "enabled"); |
139 | 27 | php_info_print_table_end(); |
140 | 27 | } |
141 | | /* }}} */ |
142 | | |
143 | | PHP_JSON_API int php_json_encode_ex(smart_str *buf, zval *val, int options, zend_long depth) /* {{{ */ |
144 | 0 | { |
145 | 0 | php_json_encoder encoder; |
146 | 0 | int return_code; |
147 | |
|
148 | 0 | php_json_encode_init(&encoder); |
149 | 0 | encoder.max_depth = depth; |
150 | |
|
151 | 0 | return_code = php_json_encode_zval(buf, val, options, &encoder); |
152 | 0 | JSON_G(error_code) = encoder.error_code; |
153 | |
|
154 | 0 | return return_code; |
155 | 0 | } |
156 | | /* }}} */ |
157 | | |
158 | | PHP_JSON_API int php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */ |
159 | 0 | { |
160 | 0 | return php_json_encode_ex(buf, val, options, JSON_G(encode_max_depth)); |
161 | 0 | } |
162 | | /* }}} */ |
163 | | |
164 | | static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ */ |
165 | 0 | { |
166 | 0 | switch(error_code) { |
167 | 0 | case PHP_JSON_ERROR_NONE: |
168 | 0 | return "No error"; |
169 | 0 | case PHP_JSON_ERROR_DEPTH: |
170 | 0 | return "Maximum stack depth exceeded"; |
171 | 0 | case PHP_JSON_ERROR_STATE_MISMATCH: |
172 | 0 | return "State mismatch (invalid or malformed JSON)"; |
173 | 0 | case PHP_JSON_ERROR_CTRL_CHAR: |
174 | 0 | return "Control character error, possibly incorrectly encoded"; |
175 | 0 | case PHP_JSON_ERROR_SYNTAX: |
176 | 0 | return "Syntax error"; |
177 | 0 | case PHP_JSON_ERROR_UTF8: |
178 | 0 | return "Malformed UTF-8 characters, possibly incorrectly encoded"; |
179 | 0 | case PHP_JSON_ERROR_RECURSION: |
180 | 0 | return "Recursion detected"; |
181 | 0 | case PHP_JSON_ERROR_INF_OR_NAN: |
182 | 0 | return "Inf and NaN cannot be JSON encoded"; |
183 | 0 | case PHP_JSON_ERROR_UNSUPPORTED_TYPE: |
184 | 0 | return "Type is not supported"; |
185 | 0 | case PHP_JSON_ERROR_INVALID_PROPERTY_NAME: |
186 | 0 | return "The decoded property name is invalid"; |
187 | 0 | case PHP_JSON_ERROR_UTF16: |
188 | 0 | return "Single unpaired UTF-16 surrogate in unicode escape"; |
189 | 0 | default: |
190 | 0 | return "Unknown error"; |
191 | 0 | } |
192 | 0 | } |
193 | | /* }}} */ |
194 | | |
195 | | PHP_JSON_API int php_json_decode_ex(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */ |
196 | 0 | { |
197 | 0 | php_json_parser parser; |
198 | |
|
199 | 0 | php_json_parser_init(&parser, return_value, str, str_len, (int)options, (int)depth); |
200 | |
|
201 | 0 | if (php_json_yyparse(&parser)) { |
202 | 0 | php_json_error_code error_code = php_json_parser_error_code(&parser); |
203 | 0 | if (!(options & PHP_JSON_THROW_ON_ERROR)) { |
204 | 0 | JSON_G(error_code) = error_code; |
205 | 0 | } else { |
206 | 0 | zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(error_code), error_code); |
207 | 0 | } |
208 | 0 | RETVAL_NULL(); |
209 | 0 | return FAILURE; |
210 | 0 | } |
211 | | |
212 | 0 | return SUCCESS; |
213 | 0 | } |
214 | | /* }}} */ |
215 | | |
216 | | /* {{{ Returns the JSON representation of a value */ |
217 | | PHP_FUNCTION(json_encode) |
218 | 202k | { |
219 | 202k | zval *parameter; |
220 | 202k | php_json_encoder encoder; |
221 | 202k | smart_str buf = {0}; |
222 | 202k | zend_long options = 0; |
223 | 202k | zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH; |
224 | | |
225 | 606k | ZEND_PARSE_PARAMETERS_START(1, 3) |
226 | 202k | Z_PARAM_ZVAL(parameter) |
227 | 202k | Z_PARAM_OPTIONAL |
228 | 34.5k | Z_PARAM_LONG(options) |
229 | 34.5k | Z_PARAM_LONG(depth) |
230 | 202k | ZEND_PARSE_PARAMETERS_END(); |
231 | | |
232 | 202k | php_json_encode_init(&encoder); |
233 | 202k | encoder.max_depth = (int)depth; |
234 | 202k | php_json_encode_zval(&buf, parameter, (int)options, &encoder); |
235 | | |
236 | 202k | if (!(options & PHP_JSON_THROW_ON_ERROR) || (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { |
237 | 202k | JSON_G(error_code) = encoder.error_code; |
238 | 202k | if (encoder.error_code != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { |
239 | 2.91k | smart_str_free(&buf); |
240 | 2.91k | RETURN_FALSE; |
241 | 2.91k | } |
242 | 37 | } else { |
243 | 37 | if (encoder.error_code != PHP_JSON_ERROR_NONE) { |
244 | 0 | smart_str_free(&buf); |
245 | 0 | zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(encoder.error_code), encoder.error_code); |
246 | 0 | RETURN_THROWS(); |
247 | 0 | } |
248 | 37 | } |
249 | | |
250 | 199k | smart_str_0(&buf); /* copy? */ |
251 | 199k | if (buf.s) { |
252 | 199k | RETURN_NEW_STR(buf.s); |
253 | 199k | } |
254 | 0 | RETURN_EMPTY_STRING(); |
255 | 0 | } |
256 | | /* }}} */ |
257 | | |
258 | | /* {{{ Decodes the JSON representation into a PHP value */ |
259 | | PHP_FUNCTION(json_decode) |
260 | 0 | { |
261 | 0 | char *str; |
262 | 0 | size_t str_len; |
263 | 0 | zend_bool assoc = 0; /* return JS objects as PHP objects by default */ |
264 | 0 | zend_bool assoc_null = 1; |
265 | 0 | zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH; |
266 | 0 | zend_long options = 0; |
267 | |
|
268 | 0 | ZEND_PARSE_PARAMETERS_START(1, 4) |
269 | 0 | Z_PARAM_STRING(str, str_len) |
270 | 0 | Z_PARAM_OPTIONAL |
271 | 0 | Z_PARAM_BOOL_EX(assoc, assoc_null, 1, 0) |
272 | 0 | Z_PARAM_LONG(depth) |
273 | 0 | Z_PARAM_LONG(options) |
274 | 0 | ZEND_PARSE_PARAMETERS_END(); |
275 | |
|
276 | 0 | if (!(options & PHP_JSON_THROW_ON_ERROR)) { |
277 | 0 | JSON_G(error_code) = PHP_JSON_ERROR_NONE; |
278 | 0 | } |
279 | |
|
280 | 0 | if (!str_len) { |
281 | 0 | if (!(options & PHP_JSON_THROW_ON_ERROR)) { |
282 | 0 | JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX; |
283 | 0 | } else { |
284 | 0 | zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX); |
285 | 0 | } |
286 | 0 | RETURN_NULL(); |
287 | 0 | } |
288 | |
|
289 | 0 | if (depth <= 0) { |
290 | 0 | zend_argument_value_error(3, "must be greater than 0"); |
291 | 0 | RETURN_THROWS(); |
292 | 0 | } |
293 | |
|
294 | 0 | if (depth > INT_MAX) { |
295 | 0 | zend_argument_value_error(3, "must be less than %d", INT_MAX); |
296 | 0 | RETURN_THROWS(); |
297 | 0 | } |
298 | | |
299 | | /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */ |
300 | 0 | if (!assoc_null) { |
301 | 0 | if (assoc) { |
302 | 0 | options |= PHP_JSON_OBJECT_AS_ARRAY; |
303 | 0 | } else { |
304 | 0 | options &= ~PHP_JSON_OBJECT_AS_ARRAY; |
305 | 0 | } |
306 | 0 | } |
307 | |
|
308 | 0 | php_json_decode_ex(return_value, str, str_len, options, depth); |
309 | 0 | } |
310 | | /* }}} */ |
311 | | |
312 | | /* {{{ Returns the error code of the last json_encode() or json_decode() call. */ |
313 | | PHP_FUNCTION(json_last_error) |
314 | 0 | { |
315 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
316 | |
|
317 | 0 | RETURN_LONG(JSON_G(error_code)); |
318 | 0 | } |
319 | | /* }}} */ |
320 | | |
321 | | /* {{{ Returns the error string of the last json_encode() or json_decode() call. */ |
322 | | PHP_FUNCTION(json_last_error_msg) |
323 | 0 | { |
324 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
325 | |
|
326 | 0 | RETURN_STRING(php_json_get_error_msg(JSON_G(error_code))); |
327 | 0 | } |
328 | | /* }}} */ |