xref: /PHP-5.4/ext/json/json.c (revision 3f42f2f5)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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/html.h"
29 #include "ext/standard/php_smart_str.h"
30 #include "JSON_parser.h"
31 #include "php_json.h"
32 #include <zend_exceptions.h>
33 
34 static PHP_MINFO_FUNCTION(json);
35 static PHP_FUNCTION(json_encode);
36 static PHP_FUNCTION(json_decode);
37 static PHP_FUNCTION(json_last_error);
38 
39 static const char digits[] = "0123456789abcdef";
40 
41 PHP_JSON_API zend_class_entry *php_json_serializable_ce;
42 
43 ZEND_DECLARE_MODULE_GLOBALS(json)
44 
45 /* {{{ arginfo */
46 ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1)
47 	ZEND_ARG_INFO(0, value)
48 	ZEND_ARG_INFO(0, options)
49 ZEND_END_ARG_INFO()
50 
51 ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1)
52 	ZEND_ARG_INFO(0, json)
53 	ZEND_ARG_INFO(0, assoc)
54 	ZEND_ARG_INFO(0, depth)
55 	ZEND_ARG_INFO(0, options)
56 ZEND_END_ARG_INFO()
57 
58 ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0)
59 ZEND_END_ARG_INFO()
60 /* }}} */
61 
62 /* {{{ json_functions[] */
63 static const zend_function_entry json_functions[] = {
64 	PHP_FE(json_encode, arginfo_json_encode)
65 	PHP_FE(json_decode, arginfo_json_decode)
66 	PHP_FE(json_last_error, arginfo_json_last_error)
67 	PHP_FE_END
68 };
69 /* }}} */
70 
71 /* {{{ JsonSerializable methods */
72 ZEND_BEGIN_ARG_INFO(json_serialize_arginfo, 0)
73 	/* No arguments */
74 ZEND_END_ARG_INFO();
75 
76 static const zend_function_entry json_serializable_interface[] = {
77 	PHP_ABSTRACT_ME(JsonSerializable, jsonSerialize, json_serialize_arginfo)
78 	PHP_FE_END
79 };
80 /* }}} */
81 
82 /* {{{ MINIT */
PHP_MINIT_FUNCTION(json)83 static PHP_MINIT_FUNCTION(json)
84 {
85 	zend_class_entry ce;
86 
87 	INIT_CLASS_ENTRY(ce, "JsonSerializable", json_serializable_interface);
88 	php_json_serializable_ce = zend_register_internal_interface(&ce TSRMLS_CC);
89 
90 	REGISTER_LONG_CONSTANT("JSON_HEX_TAG",  PHP_JSON_HEX_TAG,  CONST_CS | CONST_PERSISTENT);
91 	REGISTER_LONG_CONSTANT("JSON_HEX_AMP",  PHP_JSON_HEX_AMP,  CONST_CS | CONST_PERSISTENT);
92 	REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT);
93 	REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT);
94 	REGISTER_LONG_CONSTANT("JSON_FORCE_OBJECT", PHP_JSON_FORCE_OBJECT, CONST_CS | CONST_PERSISTENT);
95 	REGISTER_LONG_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK, CONST_CS | CONST_PERSISTENT);
96 	REGISTER_LONG_CONSTANT("JSON_UNESCAPED_SLASHES", PHP_JSON_UNESCAPED_SLASHES, CONST_CS | CONST_PERSISTENT);
97 	REGISTER_LONG_CONSTANT("JSON_PRETTY_PRINT", PHP_JSON_PRETTY_PRINT, CONST_CS | CONST_PERSISTENT);
98 	REGISTER_LONG_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE, CONST_CS | CONST_PERSISTENT);
99 
100 	REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT);
101 	REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT);
102 	REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT);
103 	REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT);
104 	REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT);
105 	REGISTER_LONG_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8, CONST_CS | CONST_PERSISTENT);
106 
107 	REGISTER_LONG_CONSTANT("JSON_OBJECT_AS_ARRAY",		PHP_JSON_OBJECT_AS_ARRAY,		CONST_CS | CONST_PERSISTENT);
108 	REGISTER_LONG_CONSTANT("JSON_BIGINT_AS_STRING",		PHP_JSON_BIGINT_AS_STRING,		CONST_CS | CONST_PERSISTENT);
109 
110 	return SUCCESS;
111 }
112 /* }}} */
113 
114 /* {{{ PHP_GINIT_FUNCTION
115 */
PHP_GINIT_FUNCTION(json)116 static PHP_GINIT_FUNCTION(json)
117 {
118 	json_globals->encoder_depth = 0;
119 	json_globals->error_code = 0;
120 }
121 /* }}} */
122 
123 
124 /* {{{ json_module_entry
125  */
126 zend_module_entry json_module_entry = {
127 	STANDARD_MODULE_HEADER,
128 	"json",
129 	json_functions,
130 	PHP_MINIT(json),
131 	NULL,
132 	NULL,
133 	NULL,
134 	PHP_MINFO(json),
135 	PHP_JSON_VERSION,
136 	PHP_MODULE_GLOBALS(json),
137 	PHP_GINIT(json),
138 	NULL,
139 	NULL,
140 	STANDARD_MODULE_PROPERTIES_EX
141 };
142 /* }}} */
143 
144 #ifdef COMPILE_DL_JSON
145 ZEND_GET_MODULE(json)
146 #endif
147 
148 /* {{{ PHP_MINFO_FUNCTION
149  */
PHP_MINFO_FUNCTION(json)150 static PHP_MINFO_FUNCTION(json)
151 {
152 	php_info_print_table_start();
153 	php_info_print_table_row(2, "json support", "enabled");
154 	php_info_print_table_row(2, "json version", PHP_JSON_VERSION);
155 	php_info_print_table_end();
156 }
157 /* }}} */
158 
159 static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC);
160 
json_determine_array_type(zval ** val TSRMLS_DC)161 static int json_determine_array_type(zval **val TSRMLS_DC) /* {{{ */
162 {
163 	int i;
164 	HashTable *myht = HASH_OF(*val);
165 
166 	i = myht ? zend_hash_num_elements(myht) : 0;
167 	if (i > 0) {
168 		char *key;
169 		ulong index, idx;
170 		uint key_len;
171 		HashPosition pos;
172 
173 		zend_hash_internal_pointer_reset_ex(myht, &pos);
174 		idx = 0;
175 		for (;; zend_hash_move_forward_ex(myht, &pos)) {
176 			i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
177 			if (i == HASH_KEY_NON_EXISTANT) {
178 				break;
179 			}
180 
181 			if (i == HASH_KEY_IS_STRING) {
182 				return 1;
183 			} else {
184 				if (index != idx) {
185 					return 1;
186 				}
187 			}
188 			idx++;
189 		}
190 	}
191 
192 	return PHP_JSON_OUTPUT_ARRAY;
193 }
194 /* }}} */
195 
196 /* {{{ Pretty printing support functions */
197 
json_pretty_print_char(smart_str * buf,int options,char c TSRMLS_DC)198 static inline void json_pretty_print_char(smart_str *buf, int options, char c TSRMLS_DC) /* {{{ */
199 {
200 	if (options & PHP_JSON_PRETTY_PRINT) {
201 		smart_str_appendc(buf, c);
202 	}
203 }
204 /* }}} */
205 
json_pretty_print_indent(smart_str * buf,int options TSRMLS_DC)206 static inline void json_pretty_print_indent(smart_str *buf, int options TSRMLS_DC) /* {{{ */
207 {
208 	int i;
209 
210 	if (options & PHP_JSON_PRETTY_PRINT) {
211 		for (i = 0; i < JSON_G(encoder_depth); ++i) {
212 			smart_str_appendl(buf, "    ", 4);
213 		}
214 	}
215 }
216 /* }}} */
217 
218 /* }}} */
219 
json_encode_array(smart_str * buf,zval ** val,int options TSRMLS_DC)220 static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */
221 {
222 	int i, r, need_comma = 0;
223 	HashTable *myht;
224 
225 	if (Z_TYPE_PP(val) == IS_ARRAY) {
226 		myht = HASH_OF(*val);
227 		r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC);
228 	} else {
229 		myht = Z_OBJPROP_PP(val);
230 		r = PHP_JSON_OUTPUT_OBJECT;
231 	}
232 
233 	if (myht && myht->nApplyCount > 1) {
234 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
235 		smart_str_appendl(buf, "null", 4);
236 		return;
237 	}
238 
239 	if (r == PHP_JSON_OUTPUT_ARRAY) {
240 		smart_str_appendc(buf, '[');
241 	} else {
242 		smart_str_appendc(buf, '{');
243 	}
244 
245 	++JSON_G(encoder_depth);
246 
247 	i = myht ? zend_hash_num_elements(myht) : 0;
248 
249 	if (i > 0)
250 	{
251 		char *key;
252 		zval **data;
253 		ulong index;
254 		uint key_len;
255 		HashPosition pos;
256 		HashTable *tmp_ht;
257 
258 		zend_hash_internal_pointer_reset_ex(myht, &pos);
259 		for (;; zend_hash_move_forward_ex(myht, &pos)) {
260 			i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
261 			if (i == HASH_KEY_NON_EXISTANT)
262 				break;
263 
264 			if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
265 				tmp_ht = HASH_OF(*data);
266 				if (tmp_ht) {
267 					tmp_ht->nApplyCount++;
268 				}
269 
270 				if (r == PHP_JSON_OUTPUT_ARRAY) {
271 					if (need_comma) {
272 						smart_str_appendc(buf, ',');
273 					} else {
274 						need_comma = 1;
275 					}
276 
277 					json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
278 					json_pretty_print_indent(buf, options TSRMLS_CC);
279 					php_json_encode(buf, *data, options TSRMLS_CC);
280 				} else if (r == PHP_JSON_OUTPUT_OBJECT) {
281 					if (i == HASH_KEY_IS_STRING) {
282 						if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
283 							/* Skip protected and private members. */
284 							if (tmp_ht) {
285 								tmp_ht->nApplyCount--;
286 							}
287 							continue;
288 						}
289 
290 						if (need_comma) {
291 							smart_str_appendc(buf, ',');
292 						} else {
293 							need_comma = 1;
294 						}
295 
296 						json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
297 						json_pretty_print_indent(buf, options TSRMLS_CC);
298 
299 						json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
300 						smart_str_appendc(buf, ':');
301 
302 						json_pretty_print_char(buf, options, ' ' TSRMLS_CC);
303 
304 						php_json_encode(buf, *data, options TSRMLS_CC);
305 					} else {
306 						if (need_comma) {
307 							smart_str_appendc(buf, ',');
308 						} else {
309 							need_comma = 1;
310 						}
311 
312 						json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
313 						json_pretty_print_indent(buf, options TSRMLS_CC);
314 
315 						smart_str_appendc(buf, '"');
316 						smart_str_append_long(buf, (long) index);
317 						smart_str_appendc(buf, '"');
318 						smart_str_appendc(buf, ':');
319 
320 						json_pretty_print_char(buf, options, ' ' TSRMLS_CC);
321 
322 						php_json_encode(buf, *data, options TSRMLS_CC);
323 					}
324 				}
325 
326 				if (tmp_ht) {
327 					tmp_ht->nApplyCount--;
328 				}
329 			}
330 		}
331 	}
332 
333 	--JSON_G(encoder_depth);
334 
335 	/* Only keep closing bracket on same line for empty arrays/objects */
336 	if (need_comma) {
337 		json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
338 		json_pretty_print_indent(buf, options TSRMLS_CC);
339 	}
340 
341 	if (r == PHP_JSON_OUTPUT_ARRAY) {
342 		smart_str_appendc(buf, ']');
343 	} else {
344 		smart_str_appendc(buf, '}');
345 	}
346 }
347 /* }}} */
348 
json_utf8_to_utf16(unsigned short * utf16,char utf8[],int len)349 static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{{ */
350 {
351 	size_t pos = 0, us;
352 	int j, status;
353 
354 	if (utf16) {
355 		/* really convert the utf8 string */
356 		for (j=0 ; pos < len ; j++) {
357 			us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
358 			if (status != SUCCESS) {
359 				return -1;
360 			}
361 			/* From http://en.wikipedia.org/wiki/UTF16 */
362 			if (us >= 0x10000) {
363 				us -= 0x10000;
364 				utf16[j++] = (unsigned short)((us >> 10) | 0xd800);
365 				utf16[j] = (unsigned short)((us & 0x3ff) | 0xdc00);
366 			} else {
367 				utf16[j] = (unsigned short)us;
368 			}
369 		}
370 	} else {
371 		/* Only check if utf8 string is valid, and compute utf16 length */
372 		for (j=0 ; pos < len ; j++) {
373 			us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
374 			if (status != SUCCESS) {
375 				return -1;
376 			}
377 			if (us >= 0x10000) {
378 				j++;
379 			}
380 		}
381 	}
382 	return j;
383 }
384 /* }}} */
385 
386 
json_escape_string(smart_str * buf,char * s,int len,int options TSRMLS_DC)387 static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
388 {
389 	int pos = 0, ulen = 0;
390 	unsigned short us;
391 	unsigned short *utf16;
392 	size_t newlen;
393 
394 	if (len == 0) {
395 		smart_str_appendl(buf, "\"\"", 2);
396 		return;
397 	}
398 
399 	if (options & PHP_JSON_NUMERIC_CHECK) {
400 		double d;
401 		int type;
402 		long p;
403 
404 		if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
405 			if (type == IS_LONG) {
406 				smart_str_append_long(buf, p);
407 			} else if (type == IS_DOUBLE) {
408 				if (!zend_isinf(d) && !zend_isnan(d)) {
409 					char *tmp;
410 					int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
411 					smart_str_appendl(buf, tmp, l);
412 					efree(tmp);
413 				} else {
414 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d);
415 					smart_str_appendc(buf, '0');
416 				}
417 			}
418 			return;
419 		}
420 
421 	}
422 
423 	utf16 = (options & PHP_JSON_UNESCAPED_UNICODE) ? NULL : (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
424 	ulen = json_utf8_to_utf16(utf16, s, len);
425 	if (ulen <= 0) {
426 		if (utf16) {
427 			efree(utf16);
428 		}
429 		if (ulen < 0) {
430 			JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
431 			if (!PG(display_errors)) {
432 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
433 			}
434 			smart_str_appendl(buf, "null", 4);
435 		} else {
436 			smart_str_appendl(buf, "\"\"", 2);
437 		}
438 		return;
439 	}
440 	if (!(options & PHP_JSON_UNESCAPED_UNICODE)) {
441 		len = ulen;
442 	}
443 
444 	/* pre-allocate for string length plus 2 quotes */
445 	smart_str_alloc(buf, len+2, 0);
446 	smart_str_appendc(buf, '"');
447 
448 	while (pos < len)
449 	{
450 		us = (options & PHP_JSON_UNESCAPED_UNICODE) ? s[pos++] : utf16[pos++];
451 
452 		switch (us)
453 		{
454 			case '"':
455 				if (options & PHP_JSON_HEX_QUOT) {
456 					smart_str_appendl(buf, "\\u0022", 6);
457 				} else {
458 					smart_str_appendl(buf, "\\\"", 2);
459 				}
460 				break;
461 
462 			case '\\':
463 				smart_str_appendl(buf, "\\\\", 2);
464 				break;
465 
466 			case '/':
467 				if (options & PHP_JSON_UNESCAPED_SLASHES) {
468 					smart_str_appendc(buf, '/');
469 				} else {
470 					smart_str_appendl(buf, "\\/", 2);
471 				}
472 				break;
473 
474 			case '\b':
475 				smart_str_appendl(buf, "\\b", 2);
476 				break;
477 
478 			case '\f':
479 				smart_str_appendl(buf, "\\f", 2);
480 				break;
481 
482 			case '\n':
483 				smart_str_appendl(buf, "\\n", 2);
484 				break;
485 
486 			case '\r':
487 				smart_str_appendl(buf, "\\r", 2);
488 				break;
489 
490 			case '\t':
491 				smart_str_appendl(buf, "\\t", 2);
492 				break;
493 
494 			case '<':
495 				if (options & PHP_JSON_HEX_TAG) {
496 					smart_str_appendl(buf, "\\u003C", 6);
497 				} else {
498 					smart_str_appendc(buf, '<');
499 				}
500 				break;
501 
502 			case '>':
503 				if (options & PHP_JSON_HEX_TAG) {
504 					smart_str_appendl(buf, "\\u003E", 6);
505 				} else {
506 					smart_str_appendc(buf, '>');
507 				}
508 				break;
509 
510 			case '&':
511 				if (options & PHP_JSON_HEX_AMP) {
512 					smart_str_appendl(buf, "\\u0026", 6);
513 				} else {
514 					smart_str_appendc(buf, '&');
515 				}
516 				break;
517 
518 			case '\'':
519 				if (options & PHP_JSON_HEX_APOS) {
520 					smart_str_appendl(buf, "\\u0027", 6);
521 				} else {
522 					smart_str_appendc(buf, '\'');
523 				}
524 				break;
525 
526 			default:
527 				if (us >= ' ' && ((options & PHP_JSON_UNESCAPED_UNICODE) || (us & 127) == us)) {
528 					smart_str_appendc(buf, (unsigned char) us);
529 				} else {
530 					smart_str_appendl(buf, "\\u", 2);
531 					smart_str_appendc(buf, digits[(us & 0xf000) >> 12]);
532 					smart_str_appendc(buf, digits[(us & 0xf00)  >> 8]);
533 					smart_str_appendc(buf, digits[(us & 0xf0)   >> 4]);
534 					smart_str_appendc(buf, digits[(us & 0xf)]);
535 				}
536 				break;
537 		}
538 	}
539 
540 	smart_str_appendc(buf, '"');
541 	if (utf16) {
542 		efree(utf16);
543 	}
544 }
545 /* }}} */
546 
547 
json_encode_serializable_object(smart_str * buf,zval * val,int options TSRMLS_DC)548 static void json_encode_serializable_object(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
549 {
550 	zend_class_entry *ce = Z_OBJCE_P(val);
551 	zval *retval = NULL, fname;
552 	HashTable* myht;
553 
554 	if (Z_TYPE_P(val) == IS_ARRAY) {
555 		myht = HASH_OF(val);
556 	} else {
557 		myht = Z_OBJPROP_P(val);
558 	}
559 
560 	if (myht && myht->nApplyCount > 1) {
561 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
562 		smart_str_appendl(buf, "null", 4);
563 		return;
564 	}
565 
566 	ZVAL_STRING(&fname, "jsonSerialize", 0);
567 
568 	if (FAILURE == call_user_function_ex(EG(function_table), &val, &fname, &retval, 0, NULL, 1, NULL TSRMLS_CC) || !retval) {
569 		zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Failed calling %s::jsonSerialize()", ce->name);
570 		smart_str_appendl(buf, "null", sizeof("null") - 1);
571 		return;
572     }
573 
574 	if (EG(exception)) {
575 		/* Error already raised */
576 		zval_ptr_dtor(&retval);
577 		smart_str_appendl(buf, "null", sizeof("null") - 1);
578 		return;
579 	}
580 
581 	if ((Z_TYPE_P(retval) == IS_OBJECT) &&
582 		(Z_OBJ_HANDLE_P(retval) == Z_OBJ_HANDLE_P(val))) {
583 		/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
584 		json_encode_array(buf, &retval, options TSRMLS_CC);
585 	} else {
586 		/* All other types, encode as normal */
587 		php_json_encode(buf, retval, options TSRMLS_CC);
588 	}
589 
590 	zval_ptr_dtor(&retval);
591 }
592 /* }}} */
593 
php_json_encode(smart_str * buf,zval * val,int options TSRMLS_DC)594 PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
595 {
596 	switch (Z_TYPE_P(val))
597 	{
598 		case IS_NULL:
599 			smart_str_appendl(buf, "null", 4);
600 			break;
601 
602 		case IS_BOOL:
603 			if (Z_BVAL_P(val)) {
604 				smart_str_appendl(buf, "true", 4);
605 			} else {
606 				smart_str_appendl(buf, "false", 5);
607 			}
608 			break;
609 
610 		case IS_LONG:
611 			smart_str_append_long(buf, Z_LVAL_P(val));
612 			break;
613 
614 		case IS_DOUBLE:
615 			{
616 				char *d = NULL;
617 				int len;
618 				double dbl = Z_DVAL_P(val);
619 
620 				if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
621 					len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
622 					smart_str_appendl(buf, d, len);
623 					efree(d);
624 				} else {
625 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl);
626 					smart_str_appendc(buf, '0');
627 				}
628 			}
629 			break;
630 
631 		case IS_STRING:
632 			json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
633 			break;
634 
635 		case IS_OBJECT:
636 			if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce TSRMLS_CC)) {
637 				json_encode_serializable_object(buf, val, options TSRMLS_CC);
638 				break;
639 			}
640 			/* fallthrough -- Non-serializable object */
641 		case IS_ARRAY:
642 			json_encode_array(buf, &val, options TSRMLS_CC);
643 			break;
644 
645 		default:
646 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null");
647 			smart_str_appendl(buf, "null", 4);
648 			break;
649 	}
650 
651 	return;
652 }
653 /* }}} */
654 
php_json_decode_ex(zval * return_value,char * str,int str_len,int options,long depth TSRMLS_DC)655 PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */
656 {
657 	int utf16_len;
658 	zval *z;
659 	unsigned short *utf16;
660 	JSON_parser jp;
661 
662 	utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
663 
664 	utf16_len = json_utf8_to_utf16(utf16, str, str_len);
665 	if (utf16_len <= 0) {
666 		if (utf16) {
667 			efree(utf16);
668 		}
669 		JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
670 		RETURN_NULL();
671 	}
672 
673 	if (depth <= 0) {
674 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero");
675 		efree(utf16);
676 		RETURN_NULL();
677 	}
678 
679 	ALLOC_INIT_ZVAL(z);
680 	jp = new_JSON_parser(depth);
681 	if (parse_JSON_ex(jp, z, utf16, utf16_len, options TSRMLS_CC)) {
682 		*return_value = *z;
683 	}
684 	else
685 	{
686 		double d;
687 		int type, overflow_info;
688 		long p;
689 		char *trim = str;
690 		int trim_len = str_len;
691 
692 		/* Increment trimmed string pointer to strip leading whitespace */
693 		/* JSON RFC says to consider as whitespace: space, tab, LF or CR */
694 		while (trim_len && (*trim == ' ' || *trim == '\t' || *trim == '\n' || *trim == '\r')) {
695 			trim++;
696 			trim_len--;
697 		}
698 
699 		/* Decrement trimmed string length to strip trailing whitespace */
700 		while (trim_len && (trim[trim_len - 1] == ' ' || trim[trim_len - 1] == '\t' || trim[trim_len - 1] == '\n' || trim[trim_len - 1] == '\r')) {
701 			trim_len--;
702 		}
703 
704 		RETVAL_NULL();
705 		if (trim_len == 4) {
706 			if (!strncasecmp(trim, "null", trim_len)) {
707 				/* We need to explicitly clear the error because its an actual NULL and not an error */
708 				jp->error_code = PHP_JSON_ERROR_NONE;
709 				RETVAL_NULL();
710 			} else if (!strncasecmp(trim, "true", trim_len)) {
711 				RETVAL_BOOL(1);
712 			}
713 		} else if (trim_len == 5 && !strncasecmp(trim, "false", trim_len)) {
714 			RETVAL_BOOL(0);
715 		}
716 
717 		if ((type = is_numeric_string_ex(trim, trim_len, &p, &d, 0, &overflow_info)) != 0) {
718 			if (type == IS_LONG) {
719 				RETVAL_LONG(p);
720 			} else if (type == IS_DOUBLE) {
721 				if (options & PHP_JSON_BIGINT_AS_STRING && overflow_info) {
722 					/* Within an object or array, a numeric literal is assumed
723 					 * to be an integer if and only if it's entirely made up of
724 					 * digits (exponent notation will result in the number
725 					 * being treated as a double). We'll match that behaviour
726 					 * here. */
727 					int i;
728 					zend_bool is_float = 0;
729 
730 					for (i = (trim[0] == '-' ? 1 : 0); i < trim_len; i++) {
731 						/* Not using isdigit() because it's locale specific,
732 						 * but we expect JSON input to always be UTF-8. */
733 						if (trim[i] < '0' || trim[i] > '9') {
734 							is_float = 1;
735 							break;
736 						}
737 					}
738 
739 					if (is_float) {
740 						RETVAL_DOUBLE(d);
741 					} else {
742 						RETVAL_STRINGL(trim, trim_len, 1);
743 					}
744 				} else {
745 					RETVAL_DOUBLE(d);
746 				}
747 			}
748 		}
749 
750 		if (Z_TYPE_P(return_value) != IS_NULL) {
751 			jp->error_code = PHP_JSON_ERROR_NONE;
752 		}
753 
754 		zval_dtor(z);
755 	}
756 	FREE_ZVAL(z);
757 	efree(utf16);
758 	JSON_G(error_code) = jp->error_code;
759 	free_JSON_parser(jp);
760 }
761 /* }}} */
762 
763 
764 /* {{{ proto string json_encode(mixed data [, int options])
765    Returns the JSON representation of a value */
PHP_FUNCTION(json_encode)766 static PHP_FUNCTION(json_encode)
767 {
768 	zval *parameter;
769 	smart_str buf = {0};
770 	long options = 0;
771 
772 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &parameter, &options) == FAILURE) {
773 		return;
774 	}
775 
776 	JSON_G(error_code) = PHP_JSON_ERROR_NONE;
777 
778 	php_json_encode(&buf, parameter, options TSRMLS_CC);
779 
780 	ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
781 
782 	smart_str_free(&buf);
783 }
784 /* }}} */
785 
786 /* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]])
787    Decodes the JSON representation into a PHP value */
PHP_FUNCTION(json_decode)788 static PHP_FUNCTION(json_decode)
789 {
790 	char *str;
791 	int str_len;
792 	zend_bool assoc = 0; /* return JS objects as PHP objects by default */
793 	long depth = JSON_PARSER_DEFAULT_DEPTH;
794 	long options = 0;
795 
796 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) {
797 		return;
798 	}
799 
800 	JSON_G(error_code) = 0;
801 
802 	if (!str_len) {
803 		RETURN_NULL();
804 	}
805 
806 	/* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */
807 	if (assoc) {
808 		options |=  PHP_JSON_OBJECT_AS_ARRAY;
809 	} else {
810 		options &= ~PHP_JSON_OBJECT_AS_ARRAY;
811 	}
812 
813 	php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC);
814 }
815 /* }}} */
816 
817 /* {{{ proto int json_last_error()
818    Returns the error code of the last json_decode(). */
PHP_FUNCTION(json_last_error)819 static PHP_FUNCTION(json_last_error)
820 {
821 	if (zend_parse_parameters_none() == FAILURE) {
822 		return;
823 	}
824 
825 	RETURN_LONG(JSON_G(error_code));
826 }
827 /* }}} */
828 
829 /*
830  * Local variables:
831  * tab-width: 4
832  * c-basic-offset: 4
833  * End:
834  * vim600: noet sw=4 ts=4 fdm=marker
835  * vim<600: noet sw=4 ts=4
836  */
837