1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Omar Kilani <omar@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "ext/standard/php_smart_str.h"
29 #include "utf8_to_utf16.h"
30 #include "JSON_parser.h"
31 #include "php_json.h"
32
33 static PHP_MINFO_FUNCTION(json);
34 static PHP_FUNCTION(json_encode);
35 static PHP_FUNCTION(json_decode);
36 static PHP_FUNCTION(json_last_error);
37
38 static const char digits[] = "0123456789abcdef";
39
40 ZEND_DECLARE_MODULE_GLOBALS(json)
41
42 /* {{{ arginfo */
43 ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1)
44 ZEND_ARG_INFO(0, value)
45 ZEND_ARG_INFO(0, options)
46 ZEND_END_ARG_INFO()
47
48 ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1)
49 ZEND_ARG_INFO(0, json)
50 ZEND_ARG_INFO(0, assoc)
51 ZEND_ARG_INFO(0, depth)
52 ZEND_END_ARG_INFO()
53
54 ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0)
55 ZEND_END_ARG_INFO()
56 /* }}} */
57
58 /* {{{ json_functions[] */
59 static const function_entry json_functions[] = {
60 PHP_FE(json_encode, arginfo_json_encode)
61 PHP_FE(json_decode, arginfo_json_decode)
62 PHP_FE(json_last_error, arginfo_json_last_error)
63 PHP_FE_END
64 };
65 /* }}} */
66
67 /* {{{ MINIT */
PHP_MINIT_FUNCTION(json)68 static PHP_MINIT_FUNCTION(json)
69 {
70 REGISTER_LONG_CONSTANT("JSON_HEX_TAG", PHP_JSON_HEX_TAG, CONST_CS | CONST_PERSISTENT);
71 REGISTER_LONG_CONSTANT("JSON_HEX_AMP", PHP_JSON_HEX_AMP, CONST_CS | CONST_PERSISTENT);
72 REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT);
73 REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT);
74 REGISTER_LONG_CONSTANT("JSON_FORCE_OBJECT", PHP_JSON_FORCE_OBJECT, CONST_CS | CONST_PERSISTENT);
75 REGISTER_LONG_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK, CONST_CS | CONST_PERSISTENT);
76
77 REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT);
78 REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT);
79 REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT);
80 REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT);
81 REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT);
82 REGISTER_LONG_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8, CONST_CS | CONST_PERSISTENT);
83
84 return SUCCESS;
85 }
86 /* }}} */
87
88 /* {{{ PHP_GINIT_FUNCTION
89 */
PHP_GINIT_FUNCTION(json)90 static PHP_GINIT_FUNCTION(json)
91 {
92 json_globals->error_code = 0;
93 }
94 /* }}} */
95
96
97 /* {{{ json_module_entry
98 */
99 zend_module_entry json_module_entry = {
100 STANDARD_MODULE_HEADER,
101 "json",
102 json_functions,
103 PHP_MINIT(json),
104 NULL,
105 NULL,
106 NULL,
107 PHP_MINFO(json),
108 PHP_JSON_VERSION,
109 PHP_MODULE_GLOBALS(json),
110 PHP_GINIT(json),
111 NULL,
112 NULL,
113 STANDARD_MODULE_PROPERTIES_EX
114 };
115 /* }}} */
116
117 #ifdef COMPILE_DL_JSON
118 ZEND_GET_MODULE(json)
119 #endif
120
121 /* {{{ PHP_MINFO_FUNCTION
122 */
PHP_MINFO_FUNCTION(json)123 static PHP_MINFO_FUNCTION(json)
124 {
125 php_info_print_table_start();
126 php_info_print_table_row(2, "json support", "enabled");
127 php_info_print_table_row(2, "json version", PHP_JSON_VERSION);
128 php_info_print_table_end();
129 }
130 /* }}} */
131
132 static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC);
133
json_determine_array_type(zval ** val TSRMLS_DC)134 static int json_determine_array_type(zval **val TSRMLS_DC) /* {{{ */
135 {
136 int i;
137 HashTable *myht = HASH_OF(*val);
138
139 i = myht ? zend_hash_num_elements(myht) : 0;
140 if (i > 0) {
141 char *key;
142 ulong index, idx;
143 uint key_len;
144 HashPosition pos;
145
146 zend_hash_internal_pointer_reset_ex(myht, &pos);
147 idx = 0;
148 for (;; zend_hash_move_forward_ex(myht, &pos)) {
149 i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
150 if (i == HASH_KEY_NON_EXISTANT) {
151 break;
152 }
153
154 if (i == HASH_KEY_IS_STRING) {
155 return 1;
156 } else {
157 if (index != idx) {
158 return 1;
159 }
160 }
161 idx++;
162 }
163 }
164
165 return PHP_JSON_OUTPUT_ARRAY;
166 }
167 /* }}} */
168
json_encode_array(smart_str * buf,zval ** val,int options TSRMLS_DC)169 static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */
170 {
171 int i, r;
172 HashTable *myht;
173
174 if (Z_TYPE_PP(val) == IS_ARRAY) {
175 myht = HASH_OF(*val);
176 r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC);
177 } else {
178 myht = Z_OBJPROP_PP(val);
179 r = PHP_JSON_OUTPUT_OBJECT;
180 }
181
182 if (myht && myht->nApplyCount > 1) {
183 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
184 smart_str_appendl(buf, "null", 4);
185 return;
186 }
187
188 if (r == PHP_JSON_OUTPUT_ARRAY) {
189 smart_str_appendc(buf, '[');
190 } else {
191 smart_str_appendc(buf, '{');
192 }
193
194 i = myht ? zend_hash_num_elements(myht) : 0;
195
196 if (i > 0)
197 {
198 char *key;
199 zval **data;
200 ulong index;
201 uint key_len;
202 HashPosition pos;
203 HashTable *tmp_ht;
204 int need_comma = 0;
205
206 zend_hash_internal_pointer_reset_ex(myht, &pos);
207 for (;; zend_hash_move_forward_ex(myht, &pos)) {
208 i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
209 if (i == HASH_KEY_NON_EXISTANT)
210 break;
211
212 if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
213 tmp_ht = HASH_OF(*data);
214 if (tmp_ht) {
215 tmp_ht->nApplyCount++;
216 }
217
218 if (r == PHP_JSON_OUTPUT_ARRAY) {
219 if (need_comma) {
220 smart_str_appendc(buf, ',');
221 } else {
222 need_comma = 1;
223 }
224
225 php_json_encode(buf, *data, options TSRMLS_CC);
226 } else if (r == PHP_JSON_OUTPUT_OBJECT) {
227 if (i == HASH_KEY_IS_STRING) {
228 if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
229 /* Skip protected and private members. */
230 if (tmp_ht) {
231 tmp_ht->nApplyCount--;
232 }
233 continue;
234 }
235
236 if (need_comma) {
237 smart_str_appendc(buf, ',');
238 } else {
239 need_comma = 1;
240 }
241
242 json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
243 smart_str_appendc(buf, ':');
244
245 php_json_encode(buf, *data, options TSRMLS_CC);
246 } else {
247 if (need_comma) {
248 smart_str_appendc(buf, ',');
249 } else {
250 need_comma = 1;
251 }
252
253 smart_str_appendc(buf, '"');
254 smart_str_append_long(buf, (long) index);
255 smart_str_appendc(buf, '"');
256 smart_str_appendc(buf, ':');
257
258 php_json_encode(buf, *data, options TSRMLS_CC);
259 }
260 }
261
262 if (tmp_ht) {
263 tmp_ht->nApplyCount--;
264 }
265 }
266 }
267 }
268
269 if (r == PHP_JSON_OUTPUT_ARRAY) {
270 smart_str_appendc(buf, ']');
271 } else {
272 smart_str_appendc(buf, '}');
273 }
274 }
275 /* }}} */
276
277 #define REVERSE16(us) (((us & 0xf) << 12) | (((us >> 4) & 0xf) << 8) | (((us >> 8) & 0xf) << 4) | ((us >> 12) & 0xf))
278
json_escape_string(smart_str * buf,char * s,int len,int options TSRMLS_DC)279 static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
280 {
281 int pos = 0;
282 unsigned short us;
283 unsigned short *utf16;
284
285 if (len == 0) {
286 smart_str_appendl(buf, "\"\"", 2);
287 return;
288 }
289
290 if (options & PHP_JSON_NUMERIC_CHECK) {
291 double d;
292 int type;
293 long p;
294
295 if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
296 if (type == IS_LONG) {
297 smart_str_append_long(buf, p);
298 } else if (type == IS_DOUBLE) {
299 if (!zend_isinf(d) && !zend_isnan(d)) {
300 char *tmp;
301 int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
302 smart_str_appendl(buf, tmp, l);
303 efree(tmp);
304 } else {
305 php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d);
306 smart_str_appendc(buf, '0');
307 }
308 }
309 return;
310 }
311
312 }
313
314 utf16 = (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
315
316 len = utf8_to_utf16(utf16, s, len);
317 if (len <= 0) {
318 if (utf16) {
319 efree(utf16);
320 }
321 if (len < 0) {
322 JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
323 if (!PG(display_errors)) {
324 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
325 }
326 smart_str_appendl(buf, "null", 4);
327 } else {
328 smart_str_appendl(buf, "\"\"", 2);
329 }
330 return;
331 }
332
333 smart_str_appendc(buf, '"');
334
335 while (pos < len)
336 {
337 us = utf16[pos++];
338
339 switch (us)
340 {
341 case '"':
342 if (options & PHP_JSON_HEX_QUOT) {
343 smart_str_appendl(buf, "\\u0022", 6);
344 } else {
345 smart_str_appendl(buf, "\\\"", 2);
346 }
347 break;
348
349 case '\\':
350 smart_str_appendl(buf, "\\\\", 2);
351 break;
352
353 case '/':
354 smart_str_appendl(buf, "\\/", 2);
355 break;
356
357 case '\b':
358 smart_str_appendl(buf, "\\b", 2);
359 break;
360
361 case '\f':
362 smart_str_appendl(buf, "\\f", 2);
363 break;
364
365 case '\n':
366 smart_str_appendl(buf, "\\n", 2);
367 break;
368
369 case '\r':
370 smart_str_appendl(buf, "\\r", 2);
371 break;
372
373 case '\t':
374 smart_str_appendl(buf, "\\t", 2);
375 break;
376
377 case '<':
378 if (options & PHP_JSON_HEX_TAG) {
379 smart_str_appendl(buf, "\\u003C", 6);
380 } else {
381 smart_str_appendc(buf, '<');
382 }
383 break;
384
385 case '>':
386 if (options & PHP_JSON_HEX_TAG) {
387 smart_str_appendl(buf, "\\u003E", 6);
388 } else {
389 smart_str_appendc(buf, '>');
390 }
391 break;
392
393 case '&':
394 if (options & PHP_JSON_HEX_AMP) {
395 smart_str_appendl(buf, "\\u0026", 6);
396 } else {
397 smart_str_appendc(buf, '&');
398 }
399 break;
400
401 case '\'':
402 if (options & PHP_JSON_HEX_APOS) {
403 smart_str_appendl(buf, "\\u0027", 6);
404 } else {
405 smart_str_appendc(buf, '\'');
406 }
407 break;
408
409 default:
410 if (us >= ' ' && (us & 127) == us) {
411 smart_str_appendc(buf, (unsigned char) us);
412 } else {
413 smart_str_appendl(buf, "\\u", 2);
414 us = REVERSE16(us);
415
416 smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
417 us >>= 4;
418 smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
419 us >>= 4;
420 smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
421 us >>= 4;
422 smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
423 }
424 break;
425 }
426 }
427
428 smart_str_appendc(buf, '"');
429 efree(utf16);
430 }
431 /* }}} */
432
php_json_encode(smart_str * buf,zval * val,int options TSRMLS_DC)433 PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
434 {
435 switch (Z_TYPE_P(val))
436 {
437 case IS_NULL:
438 smart_str_appendl(buf, "null", 4);
439 break;
440
441 case IS_BOOL:
442 if (Z_BVAL_P(val)) {
443 smart_str_appendl(buf, "true", 4);
444 } else {
445 smart_str_appendl(buf, "false", 5);
446 }
447 break;
448
449 case IS_LONG:
450 smart_str_append_long(buf, Z_LVAL_P(val));
451 break;
452
453 case IS_DOUBLE:
454 {
455 char *d = NULL;
456 int len;
457 double dbl = Z_DVAL_P(val);
458
459 if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
460 len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
461 smart_str_appendl(buf, d, len);
462 efree(d);
463 } else {
464 php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl);
465 smart_str_appendc(buf, '0');
466 }
467 }
468 break;
469
470 case IS_STRING:
471 json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
472 break;
473
474 case IS_ARRAY:
475 case IS_OBJECT:
476 json_encode_array(buf, &val, options TSRMLS_CC);
477 break;
478
479 default:
480 php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null");
481 smart_str_appendl(buf, "null", 4);
482 break;
483 }
484
485 return;
486 }
487 /* }}} */
488
php_json_decode(zval * return_value,char * str,int str_len,zend_bool assoc,long depth TSRMLS_DC)489 PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) /* {{{ */
490 {
491 int utf16_len;
492 zval *z;
493 unsigned short *utf16;
494 JSON_parser jp;
495
496 utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
497
498 utf16_len = utf8_to_utf16(utf16, str, str_len);
499 if (utf16_len <= 0) {
500 if (utf16) {
501 efree(utf16);
502 }
503 JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
504 RETURN_NULL();
505 }
506
507 if (depth <= 0) {
508 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero");
509 efree(utf16);
510 RETURN_NULL();
511 }
512
513 ALLOC_INIT_ZVAL(z);
514 jp = new_JSON_parser(depth);
515 if (parse_JSON(jp, z, utf16, utf16_len, assoc TSRMLS_CC)) {
516 *return_value = *z;
517 }
518 else
519 {
520 double d;
521 int type;
522 long p;
523
524 RETVAL_NULL();
525 if (str_len == 4) {
526 if (!strcasecmp(str, "null")) {
527 /* We need to explicitly clear the error because its an actual NULL and not an error */
528 jp->error_code = PHP_JSON_ERROR_NONE;
529 RETVAL_NULL();
530 } else if (!strcasecmp(str, "true")) {
531 RETVAL_BOOL(1);
532 }
533 } else if (str_len == 5 && !strcasecmp(str, "false")) {
534 RETVAL_BOOL(0);
535 }
536
537 if ((type = is_numeric_string(str, str_len, &p, &d, 0)) != 0) {
538 if (type == IS_LONG) {
539 RETVAL_LONG(p);
540 } else if (type == IS_DOUBLE) {
541 RETVAL_DOUBLE(d);
542 }
543 }
544
545 if (Z_TYPE_P(return_value) != IS_NULL) {
546 jp->error_code = PHP_JSON_ERROR_NONE;
547 }
548
549 zval_dtor(z);
550 }
551 FREE_ZVAL(z);
552 efree(utf16);
553 JSON_G(error_code) = jp->error_code;
554 free_JSON_parser(jp);
555 }
556 /* }}} */
557
558 /* {{{ proto string json_encode(mixed data [, int options])
559 Returns the JSON representation of a value */
PHP_FUNCTION(json_encode)560 static PHP_FUNCTION(json_encode)
561 {
562 zval *parameter;
563 smart_str buf = {0};
564 long options = 0;
565
566 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", ¶meter, &options) == FAILURE) {
567 return;
568 }
569
570 JSON_G(error_code) = PHP_JSON_ERROR_NONE;
571
572 php_json_encode(&buf, parameter, options TSRMLS_CC);
573
574 ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
575
576 smart_str_free(&buf);
577 }
578 /* }}} */
579
580 /* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]])
581 Decodes the JSON representation into a PHP value */
PHP_FUNCTION(json_decode)582 static PHP_FUNCTION(json_decode)
583 {
584 char *str;
585 int str_len;
586 zend_bool assoc = 0; /* return JS objects as PHP objects by default */
587 long depth = JSON_PARSER_DEFAULT_DEPTH;
588
589 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &str, &str_len, &assoc, &depth) == FAILURE) {
590 return;
591 }
592
593 JSON_G(error_code) = 0;
594
595 if (!str_len) {
596 RETURN_NULL();
597 }
598
599 php_json_decode(return_value, str, str_len, assoc, depth TSRMLS_CC);
600 }
601 /* }}} */
602
603 /* {{{ proto int json_last_error()
604 Returns the error code of the last json_decode(). */
PHP_FUNCTION(json_last_error)605 static PHP_FUNCTION(json_last_error)
606 {
607 if (zend_parse_parameters_none() == FAILURE) {
608 return;
609 }
610
611 RETURN_LONG(JSON_G(error_code));
612 }
613 /* }}} */
614
615 /*
616 * Local variables:
617 * tab-width: 4
618 * c-basic-offset: 4
619 * End:
620 * vim600: noet sw=4 ts=4 fdm=marker
621 * vim<600: noet sw=4 ts=4
622 */
623