Annotation of embedaddon/php/ext/json/json.c, revision 1.1.1.1
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2012 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: json.c 321634 2012-01-01 13:15:04Z felipe $ */
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 */
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: */
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: */
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:
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: if (i == HASH_KEY_IS_STRING) {
154: return 1;
155: } else {
156: if (index != idx) {
157: return 1;
158: }
159: }
160: idx++;
161: }
162: }
163:
164: return PHP_JSON_OUTPUT_ARRAY;
165: }
166: /* }}} */
167:
168: static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */
169: {
170: int i, r;
171: HashTable *myht;
172:
173: if (Z_TYPE_PP(val) == IS_ARRAY) {
174: myht = HASH_OF(*val);
175: r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC);
176: } else {
177: myht = Z_OBJPROP_PP(val);
178: r = PHP_JSON_OUTPUT_OBJECT;
179: }
180:
181: if (myht && myht->nApplyCount > 1) {
182: php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
183: smart_str_appendl(buf, "null", 4);
184: return;
185: }
186:
187: if (r == PHP_JSON_OUTPUT_ARRAY) {
188: smart_str_appendc(buf, '[');
189: } else {
190: smart_str_appendc(buf, '{');
191: }
192:
193: i = myht ? zend_hash_num_elements(myht) : 0;
194:
195: if (i > 0)
196: {
197: char *key;
198: zval **data;
199: ulong index;
200: uint key_len;
201: HashPosition pos;
202: HashTable *tmp_ht;
203: int need_comma = 0;
204:
205: zend_hash_internal_pointer_reset_ex(myht, &pos);
206: for (;; zend_hash_move_forward_ex(myht, &pos)) {
207: i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
208: if (i == HASH_KEY_NON_EXISTANT)
209: break;
210:
211: if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
212: tmp_ht = HASH_OF(*data);
213: if (tmp_ht) {
214: tmp_ht->nApplyCount++;
215: }
216:
217: if (r == PHP_JSON_OUTPUT_ARRAY) {
218: if (need_comma) {
219: smart_str_appendc(buf, ',');
220: } else {
221: need_comma = 1;
222: }
223:
224: php_json_encode(buf, *data, options TSRMLS_CC);
225: } else if (r == PHP_JSON_OUTPUT_OBJECT) {
226: if (i == HASH_KEY_IS_STRING) {
227: if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
228: /* Skip protected and private members. */
229: if (tmp_ht) {
230: tmp_ht->nApplyCount--;
231: }
232: continue;
233: }
234:
235: if (need_comma) {
236: smart_str_appendc(buf, ',');
237: } else {
238: need_comma = 1;
239: }
240:
241: json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
242: smart_str_appendc(buf, ':');
243:
244: php_json_encode(buf, *data, options TSRMLS_CC);
245: } else {
246: if (need_comma) {
247: smart_str_appendc(buf, ',');
248: } else {
249: need_comma = 1;
250: }
251:
252: smart_str_appendc(buf, '"');
253: smart_str_append_long(buf, (long) index);
254: smart_str_appendc(buf, '"');
255: smart_str_appendc(buf, ':');
256:
257: php_json_encode(buf, *data, options TSRMLS_CC);
258: }
259: }
260:
261: if (tmp_ht) {
262: tmp_ht->nApplyCount--;
263: }
264: }
265: }
266: }
267:
268: if (r == PHP_JSON_OUTPUT_ARRAY) {
269: smart_str_appendc(buf, ']');
270: } else {
271: smart_str_appendc(buf, '}');
272: }
273: }
274: /* }}} */
275:
276: #define REVERSE16(us) (((us & 0xf) << 12) | (((us >> 4) & 0xf) << 8) | (((us >> 8) & 0xf) << 4) | ((us >> 12) & 0xf))
277:
278: static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
279: {
280: int pos = 0;
281: unsigned short us;
282: unsigned short *utf16;
283:
284: if (len == 0) {
285: smart_str_appendl(buf, "\"\"", 2);
286: return;
287: }
288:
289: if (options & PHP_JSON_NUMERIC_CHECK) {
290: double d;
291: int type;
292: long p;
293:
294: if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
295: if (type == IS_LONG) {
296: smart_str_append_long(buf, p);
297: } else if (type == IS_DOUBLE) {
298: if (!zend_isinf(d) && !zend_isnan(d)) {
299: char *tmp;
300: int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
301: smart_str_appendl(buf, tmp, l);
302: efree(tmp);
303: } else {
304: php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d);
305: smart_str_appendc(buf, '0');
306: }
307: }
308: return;
309: }
310:
311: }
312:
313: utf16 = (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
314:
315: len = utf8_to_utf16(utf16, s, len);
316: if (len <= 0) {
317: if (utf16) {
318: efree(utf16);
319: }
320: if (len < 0) {
321: JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
322: if (!PG(display_errors)) {
323: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
324: }
325: smart_str_appendl(buf, "null", 4);
326: } else {
327: smart_str_appendl(buf, "\"\"", 2);
328: }
329: return;
330: }
331:
332: smart_str_appendc(buf, '"');
333:
334: while (pos < len)
335: {
336: us = utf16[pos++];
337:
338: switch (us)
339: {
340: case '"':
341: if (options & PHP_JSON_HEX_QUOT) {
342: smart_str_appendl(buf, "\\u0022", 6);
343: } else {
344: smart_str_appendl(buf, "\\\"", 2);
345: }
346: break;
347:
348: case '\\':
349: smart_str_appendl(buf, "\\\\", 2);
350: break;
351:
352: case '/':
353: smart_str_appendl(buf, "\\/", 2);
354: break;
355:
356: case '\b':
357: smart_str_appendl(buf, "\\b", 2);
358: break;
359:
360: case '\f':
361: smart_str_appendl(buf, "\\f", 2);
362: break;
363:
364: case '\n':
365: smart_str_appendl(buf, "\\n", 2);
366: break;
367:
368: case '\r':
369: smart_str_appendl(buf, "\\r", 2);
370: break;
371:
372: case '\t':
373: smart_str_appendl(buf, "\\t", 2);
374: break;
375:
376: case '<':
377: if (options & PHP_JSON_HEX_TAG) {
378: smart_str_appendl(buf, "\\u003C", 6);
379: } else {
380: smart_str_appendc(buf, '<');
381: }
382: break;
383:
384: case '>':
385: if (options & PHP_JSON_HEX_TAG) {
386: smart_str_appendl(buf, "\\u003E", 6);
387: } else {
388: smart_str_appendc(buf, '>');
389: }
390: break;
391:
392: case '&':
393: if (options & PHP_JSON_HEX_AMP) {
394: smart_str_appendl(buf, "\\u0026", 6);
395: } else {
396: smart_str_appendc(buf, '&');
397: }
398: break;
399:
400: case '\'':
401: if (options & PHP_JSON_HEX_APOS) {
402: smart_str_appendl(buf, "\\u0027", 6);
403: } else {
404: smart_str_appendc(buf, '\'');
405: }
406: break;
407:
408: default:
409: if (us >= ' ' && (us & 127) == us) {
410: smart_str_appendc(buf, (unsigned char) us);
411: } else {
412: smart_str_appendl(buf, "\\u", 2);
413: us = REVERSE16(us);
414:
415: smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
416: us >>= 4;
417: smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
418: us >>= 4;
419: smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
420: us >>= 4;
421: smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
422: }
423: break;
424: }
425: }
426:
427: smart_str_appendc(buf, '"');
428: efree(utf16);
429: }
430: /* }}} */
431:
432: PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
433: {
434: switch (Z_TYPE_P(val))
435: {
436: case IS_NULL:
437: smart_str_appendl(buf, "null", 4);
438: break;
439:
440: case IS_BOOL:
441: if (Z_BVAL_P(val)) {
442: smart_str_appendl(buf, "true", 4);
443: } else {
444: smart_str_appendl(buf, "false", 5);
445: }
446: break;
447:
448: case IS_LONG:
449: smart_str_append_long(buf, Z_LVAL_P(val));
450: break;
451:
452: case IS_DOUBLE:
453: {
454: char *d = NULL;
455: int len;
456: double dbl = Z_DVAL_P(val);
457:
458: if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
459: len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
460: smart_str_appendl(buf, d, len);
461: efree(d);
462: } else {
463: php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl);
464: smart_str_appendc(buf, '0');
465: }
466: }
467: break;
468:
469: case IS_STRING:
470: json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
471: break;
472:
473: case IS_ARRAY:
474: case IS_OBJECT:
475: json_encode_array(buf, &val, options TSRMLS_CC);
476: break;
477:
478: default:
479: php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null");
480: smart_str_appendl(buf, "null", 4);
481: break;
482: }
483:
484: return;
485: }
486: /* }}} */
487:
488: PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) /* {{{ */
489: {
490: int utf16_len;
491: zval *z;
492: unsigned short *utf16;
493: JSON_parser jp;
494:
495: utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
496:
497: utf16_len = utf8_to_utf16(utf16, str, str_len);
498: if (utf16_len <= 0) {
499: if (utf16) {
500: efree(utf16);
501: }
502: JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
503: RETURN_NULL();
504: }
505:
506: if (depth <= 0) {
507: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero");
508: efree(utf16);
509: RETURN_NULL();
510: }
511:
512: ALLOC_INIT_ZVAL(z);
513: jp = new_JSON_parser(depth);
514: if (parse_JSON(jp, z, utf16, utf16_len, assoc TSRMLS_CC)) {
515: *return_value = *z;
516: }
517: else
518: {
519: double d;
520: int type;
521: long p;
522:
523: RETVAL_NULL();
524: if (str_len == 4) {
525: if (!strcasecmp(str, "null")) {
526: /* We need to explicitly clear the error because its an actual NULL and not an error */
527: jp->error_code = PHP_JSON_ERROR_NONE;
528: RETVAL_NULL();
529: } else if (!strcasecmp(str, "true")) {
530: RETVAL_BOOL(1);
531: }
532: } else if (str_len == 5 && !strcasecmp(str, "false")) {
533: RETVAL_BOOL(0);
534: }
535:
536: if ((type = is_numeric_string(str, str_len, &p, &d, 0)) != 0) {
537: if (type == IS_LONG) {
538: RETVAL_LONG(p);
539: } else if (type == IS_DOUBLE) {
540: RETVAL_DOUBLE(d);
541: }
542: }
543:
544: if (Z_TYPE_P(return_value) != IS_NULL) {
545: jp->error_code = PHP_JSON_ERROR_NONE;
546: }
547:
548: zval_dtor(z);
549: }
550: FREE_ZVAL(z);
551: efree(utf16);
552: JSON_G(error_code) = jp->error_code;
553: free_JSON_parser(jp);
554: }
555: /* }}} */
556:
557: /* {{{ proto string json_encode(mixed data [, int options])
558: Returns the JSON representation of a value */
559: static PHP_FUNCTION(json_encode)
560: {
561: zval *parameter;
562: smart_str buf = {0};
563: long options = 0;
564:
565: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", ¶meter, &options) == FAILURE) {
566: return;
567: }
568:
569: JSON_G(error_code) = PHP_JSON_ERROR_NONE;
570:
571: php_json_encode(&buf, parameter, options TSRMLS_CC);
572:
573: ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
574:
575: smart_str_free(&buf);
576: }
577: /* }}} */
578:
579: /* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]])
580: Decodes the JSON representation into a PHP value */
581: static PHP_FUNCTION(json_decode)
582: {
583: char *str;
584: int str_len;
585: zend_bool assoc = 0; /* return JS objects as PHP objects by default */
586: long depth = JSON_PARSER_DEFAULT_DEPTH;
587:
588: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &str, &str_len, &assoc, &depth) == FAILURE) {
589: return;
590: }
591:
592: JSON_G(error_code) = 0;
593:
594: if (!str_len) {
595: RETURN_NULL();
596: }
597:
598: php_json_decode(return_value, str, str_len, assoc, depth TSRMLS_CC);
599: }
600: /* }}} */
601:
602: /* {{{ proto int json_last_error()
603: Returns the error code of the last json_decode(). */
604: static PHP_FUNCTION(json_last_error)
605: {
606: if (zend_parse_parameters_none() == FAILURE) {
607: return;
608: }
609:
610: RETURN_LONG(JSON_G(error_code));
611: }
612: /* }}} */
613:
614: /*
615: * Local variables:
616: * tab-width: 4
617: * c-basic-offset: 4
618: * End:
619: * vim600: noet sw=4 ts=4 fdm=marker
620: * vim<600: noet sw=4 ts=4
621: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>