xref: /PHP-5.3/ext/json/json.c (revision a2045ff3)
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", &parameter, &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