xref: /PHP-7.0/ext/json/json_encoder.c (revision 478f119a)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2017 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   |         Jakub Zelenka <bukka@php.net>                                |
17   +----------------------------------------------------------------------+
18 */
19 
20 /* $Id$ */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "php.h"
27 #include "php_ini.h"
28 #include "ext/standard/info.h"
29 #include "ext/standard/html.h"
30 #include "zend_smart_str.h"
31 #include "php_json.h"
32 #include <zend_exceptions.h>
33 
34 /* double limits */
35 #include <float.h>
36 #if defined(DBL_MANT_DIG) && defined(DBL_MIN_EXP)
37 #define PHP_JSON_DOUBLE_MAX_LENGTH (3 + DBL_MANT_DIG - DBL_MIN_EXP)
38 #else
39 #define PHP_JSON_DOUBLE_MAX_LENGTH 1080
40 #endif
41 
42 static const char digits[] = "0123456789abcdef";
43 
44 static void php_json_escape_string(smart_str *buf, char *s, size_t len, int options);
45 
php_json_determine_array_type(zval * val)46 static int php_json_determine_array_type(zval *val) /* {{{ */
47 {
48 	int i;
49 	HashTable *myht = Z_ARRVAL_P(val);
50 
51 	i = myht ? zend_hash_num_elements(myht) : 0;
52 	if (i > 0) {
53 		zend_string *key;
54 		zend_ulong index, idx;
55 
56 		idx = 0;
57 		ZEND_HASH_FOREACH_KEY(myht, index, key) {
58 			if (key) {
59 				return PHP_JSON_OUTPUT_OBJECT;
60 			} else {
61 				if (index != idx) {
62 					return PHP_JSON_OUTPUT_OBJECT;
63 				}
64 			}
65 			idx++;
66 		} ZEND_HASH_FOREACH_END();
67 	}
68 
69 	return PHP_JSON_OUTPUT_ARRAY;
70 }
71 /* }}} */
72 
73 /* {{{ Pretty printing support functions */
74 
php_json_pretty_print_char(smart_str * buf,int options,char c)75 static inline void php_json_pretty_print_char(smart_str *buf, int options, char c) /* {{{ */
76 {
77 	if (options & PHP_JSON_PRETTY_PRINT) {
78 		smart_str_appendc(buf, c);
79 	}
80 }
81 /* }}} */
82 
php_json_pretty_print_indent(smart_str * buf,int options)83 static inline void php_json_pretty_print_indent(smart_str *buf, int options) /* {{{ */
84 {
85 	int i;
86 
87 	if (options & PHP_JSON_PRETTY_PRINT) {
88 		for (i = 0; i < JSON_G(encoder_depth); ++i) {
89 			smart_str_appendl(buf, "    ", 4);
90 		}
91 	}
92 }
93 /* }}} */
94 
95 /* }}} */
96 
php_json_is_valid_double(double d)97 static inline int php_json_is_valid_double(double d) /* {{{ */
98 {
99 	return !zend_isinf(d) && !zend_isnan(d);
100 }
101 /* }}} */
102 
php_json_encode_double(smart_str * buf,double d,int options)103 static inline void php_json_encode_double(smart_str *buf, double d, int options) /* {{{ */
104 {
105 	size_t len;
106 	char num[PHP_JSON_DOUBLE_MAX_LENGTH];
107 	php_gcvt(d, (int)EG(precision), '.', 'e', &num[0]);
108 	len = strlen(num);
109 	if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < PHP_JSON_DOUBLE_MAX_LENGTH - 2) {
110 		num[len++] = '.';
111 		num[len++] = '0';
112 		num[len] = '\0';
113 	}
114 	smart_str_appendl(buf, num, len);
115 }
116 /* }}} */
117 
php_json_encode_array(smart_str * buf,zval * val,int options)118 static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{ */
119 {
120 	int i, r, need_comma = 0;
121 	HashTable *myht;
122 
123 	if (Z_TYPE_P(val) == IS_ARRAY) {
124 		myht = Z_ARRVAL_P(val);
125 		r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : php_json_determine_array_type(val);
126 	} else {
127 		myht = Z_OBJPROP_P(val);
128 		r = PHP_JSON_OUTPUT_OBJECT;
129 	}
130 
131 	if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
132 		JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
133 		smart_str_appendl(buf, "null", 4);
134 		return;
135 	}
136 
137 	if (r == PHP_JSON_OUTPUT_ARRAY) {
138 		smart_str_appendc(buf, '[');
139 	} else {
140 		smart_str_appendc(buf, '{');
141 	}
142 
143 	++JSON_G(encoder_depth);
144 
145 	i = myht ? zend_hash_num_elements(myht) : 0;
146 
147 	if (i > 0) {
148 		zend_string *key;
149 		zval *data;
150 		zend_ulong index;
151 		HashTable *tmp_ht;
152 
153 		ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
154 			ZVAL_DEREF(data);
155 			tmp_ht = HASH_OF(data);
156 			if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
157 				ZEND_HASH_INC_APPLY_COUNT(tmp_ht);
158 			}
159 
160 			if (r == PHP_JSON_OUTPUT_ARRAY) {
161 				if (need_comma) {
162 					smart_str_appendc(buf, ',');
163 				} else {
164 					need_comma = 1;
165 				}
166 
167 				php_json_pretty_print_char(buf, options, '\n');
168 				php_json_pretty_print_indent(buf, options);
169 				php_json_encode(buf, data, options);
170 			} else if (r == PHP_JSON_OUTPUT_OBJECT) {
171 				if (key) {
172 					if (ZSTR_VAL(key)[0] == '\0' && Z_TYPE_P(val) == IS_OBJECT) {
173 						/* Skip protected and private members. */
174 						if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
175 							ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
176 						}
177 						continue;
178 					}
179 
180 					if (need_comma) {
181 						smart_str_appendc(buf, ',');
182 					} else {
183 						need_comma = 1;
184 					}
185 
186 					php_json_pretty_print_char(buf, options, '\n');
187 					php_json_pretty_print_indent(buf, options);
188 
189 					php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key), options & ~PHP_JSON_NUMERIC_CHECK);
190 					smart_str_appendc(buf, ':');
191 
192 					php_json_pretty_print_char(buf, options, ' ');
193 
194 					php_json_encode(buf, data, options);
195 				} else {
196 					if (need_comma) {
197 						smart_str_appendc(buf, ',');
198 					} else {
199 						need_comma = 1;
200 					}
201 
202 					php_json_pretty_print_char(buf, options, '\n');
203 					php_json_pretty_print_indent(buf, options);
204 
205 					smart_str_appendc(buf, '"');
206 					smart_str_append_long(buf, (zend_long) index);
207 					smart_str_appendc(buf, '"');
208 					smart_str_appendc(buf, ':');
209 
210 					php_json_pretty_print_char(buf, options, ' ');
211 
212 					php_json_encode(buf, data, options);
213 				}
214 			}
215 
216 			if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
217 				ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
218 			}
219 		} ZEND_HASH_FOREACH_END();
220 	}
221 
222 	if (JSON_G(encoder_depth) > JSON_G(encode_max_depth)) {
223 		JSON_G(error_code) = PHP_JSON_ERROR_DEPTH;
224 	}
225 	--JSON_G(encoder_depth);
226 
227 	/* Only keep closing bracket on same line for empty arrays/objects */
228 	if (need_comma) {
229 		php_json_pretty_print_char(buf, options, '\n');
230 		php_json_pretty_print_indent(buf, options);
231 	}
232 
233 	if (r == PHP_JSON_OUTPUT_ARRAY) {
234 		smart_str_appendc(buf, ']');
235 	} else {
236 		smart_str_appendc(buf, '}');
237 	}
238 }
239 /* }}} */
240 
php_json_utf8_to_utf16(unsigned short * utf16,char utf8[],size_t len)241 static int php_json_utf8_to_utf16(unsigned short *utf16, char utf8[], size_t len) /* {{{ */
242 {
243 	size_t pos = 0, us;
244 	int j, status;
245 
246 	if (utf16) {
247 		/* really convert the utf8 string */
248 		for (j=0 ; pos < len ; j++) {
249 			us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
250 			if (status != SUCCESS) {
251 				return -1;
252 			}
253 			/* From http://en.wikipedia.org/wiki/UTF16 */
254 			if (us >= 0x10000) {
255 				us -= 0x10000;
256 				utf16[j++] = (unsigned short)((us >> 10) | 0xd800);
257 				utf16[j] = (unsigned short)((us & 0x3ff) | 0xdc00);
258 			} else {
259 				utf16[j] = (unsigned short)us;
260 			}
261 		}
262 	} else {
263 		/* Only check if utf8 string is valid, and compute utf16 length */
264 		for (j=0 ; pos < len ; j++) {
265 			us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
266 			if (status != SUCCESS) {
267 				return -1;
268 			}
269 			if (us >= 0x10000) {
270 				j++;
271 			}
272 		}
273 	}
274 	return j;
275 }
276 /* }}} */
277 
php_json_escape_string(smart_str * buf,char * s,size_t len,int options)278 static void php_json_escape_string(smart_str *buf, char *s, size_t len, int options) /* {{{ */
279 {
280 	int status;
281 	unsigned int us;
282 	size_t pos, checkpoint;
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 		zend_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 				return;
298 			} else if (type == IS_DOUBLE && php_json_is_valid_double(d)) {
299 				php_json_encode_double(buf, d, options);
300 				return;
301 			}
302 		}
303 
304 	}
305 
306 	if (options & PHP_JSON_UNESCAPED_UNICODE) {
307 		/* validate UTF-8 string first */
308 		if (php_json_utf8_to_utf16(NULL, s, len) < 0) {
309 			JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
310 			smart_str_appendl(buf, "null", 4);
311 			return;
312 		}
313 	}
314 
315 	pos = 0;
316 	checkpoint = buf->s ? ZSTR_LEN(buf->s) : 0;
317 
318 	/* pre-allocate for string length plus 2 quotes */
319 	smart_str_alloc(buf, len+2, 0);
320 	smart_str_appendc(buf, '"');
321 
322 	do {
323 		us = (unsigned char)s[pos];
324 		if (us >= 0x80 && !(options & PHP_JSON_UNESCAPED_UNICODE)) {
325 			/* UTF-8 character */
326 			us = php_next_utf8_char((const unsigned char *)s, len, &pos, &status);
327 			if (status != SUCCESS) {
328 				if (buf->s) {
329 					ZSTR_LEN(buf->s) = checkpoint;
330 				}
331 				JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
332 				smart_str_appendl(buf, "null", 4);
333 				return;
334 			}
335 			/* From http://en.wikipedia.org/wiki/UTF16 */
336 			if (us >= 0x10000) {
337 				unsigned int next_us;
338 				us -= 0x10000;
339 				next_us = (unsigned short)((us & 0x3ff) | 0xdc00);
340 				us = (unsigned short)((us >> 10) | 0xd800);
341 				smart_str_appendl(buf, "\\u", 2);
342 				smart_str_appendc(buf, digits[(us & 0xf000) >> 12]);
343 				smart_str_appendc(buf, digits[(us & 0xf00)  >> 8]);
344 				smart_str_appendc(buf, digits[(us & 0xf0)   >> 4]);
345 				smart_str_appendc(buf, digits[(us & 0xf)]);
346 				us = next_us;
347 			}
348 			smart_str_appendl(buf, "\\u", 2);
349 			smart_str_appendc(buf, digits[(us & 0xf000) >> 12]);
350 			smart_str_appendc(buf, digits[(us & 0xf00)  >> 8]);
351 			smart_str_appendc(buf, digits[(us & 0xf0)   >> 4]);
352 			smart_str_appendc(buf, digits[(us & 0xf)]);
353 		} else {
354 			pos++;
355 
356 			switch (us) {
357 				case '"':
358 					if (options & PHP_JSON_HEX_QUOT) {
359 						smart_str_appendl(buf, "\\u0022", 6);
360 					} else {
361 						smart_str_appendl(buf, "\\\"", 2);
362 					}
363 					break;
364 
365 				case '\\':
366 					smart_str_appendl(buf, "\\\\", 2);
367 					break;
368 
369 				case '/':
370 					if (options & PHP_JSON_UNESCAPED_SLASHES) {
371 						smart_str_appendc(buf, '/');
372 					} else {
373 						smart_str_appendl(buf, "\\/", 2);
374 					}
375 					break;
376 
377 				case '\b':
378 					smart_str_appendl(buf, "\\b", 2);
379 					break;
380 
381 				case '\f':
382 					smart_str_appendl(buf, "\\f", 2);
383 					break;
384 
385 				case '\n':
386 					smart_str_appendl(buf, "\\n", 2);
387 					break;
388 
389 				case '\r':
390 					smart_str_appendl(buf, "\\r", 2);
391 					break;
392 
393 				case '\t':
394 					smart_str_appendl(buf, "\\t", 2);
395 					break;
396 
397 				case '<':
398 					if (options & PHP_JSON_HEX_TAG) {
399 						smart_str_appendl(buf, "\\u003C", 6);
400 					} else {
401 						smart_str_appendc(buf, '<');
402 					}
403 					break;
404 
405 				case '>':
406 					if (options & PHP_JSON_HEX_TAG) {
407 						smart_str_appendl(buf, "\\u003E", 6);
408 					} else {
409 						smart_str_appendc(buf, '>');
410 					}
411 					break;
412 
413 				case '&':
414 					if (options & PHP_JSON_HEX_AMP) {
415 						smart_str_appendl(buf, "\\u0026", 6);
416 					} else {
417 						smart_str_appendc(buf, '&');
418 					}
419 					break;
420 
421 				case '\'':
422 					if (options & PHP_JSON_HEX_APOS) {
423 						smart_str_appendl(buf, "\\u0027", 6);
424 					} else {
425 						smart_str_appendc(buf, '\'');
426 					}
427 					break;
428 
429 				default:
430 					if (us >= ' ') {
431 						smart_str_appendc(buf, (unsigned char) us);
432 					} else {
433 						smart_str_appendl(buf, "\\u00", sizeof("\\u00")-1);
434 						smart_str_appendc(buf, digits[(us & 0xf0)   >> 4]);
435 						smart_str_appendc(buf, digits[(us & 0xf)]);
436 					}
437 					break;
438 			}
439 		}
440 	} while (pos < len);
441 
442 	smart_str_appendc(buf, '"');
443 }
444 /* }}} */
445 
php_json_encode_serializable_object(smart_str * buf,zval * val,int options)446 static void php_json_encode_serializable_object(smart_str *buf, zval *val, int options) /* {{{ */
447 {
448 	zend_class_entry *ce = Z_OBJCE_P(val);
449 	zval retval, fname;
450 	HashTable* myht;
451 	int origin_error_code;
452 
453 	if (Z_TYPE_P(val) == IS_ARRAY) {
454 		myht = Z_ARRVAL_P(val);
455 	} else {
456 		myht = Z_OBJPROP_P(val);
457 	}
458 
459 	if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
460 		JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
461 		smart_str_appendl(buf, "null", 4);
462 		return;
463 	}
464 
465 
466 	ZVAL_STRING(&fname, "jsonSerialize");
467 
468 	origin_error_code = JSON_G(error_code);
469 	if (FAILURE == call_user_function_ex(EG(function_table), val, &fname, &retval, 0, NULL, 1, NULL) || Z_TYPE(retval) == IS_UNDEF) {
470 		if (!EG(exception)) {
471 			zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name));
472 		}
473 		smart_str_appendl(buf, "null", sizeof("null") - 1);
474 		zval_ptr_dtor(&fname);
475 		return;
476 	}
477 
478 	JSON_G(error_code) = origin_error_code;
479 	if (EG(exception)) {
480 		/* Error already raised */
481 		zval_ptr_dtor(&retval);
482 		zval_ptr_dtor(&fname);
483 		smart_str_appendl(buf, "null", sizeof("null") - 1);
484 		return;
485 	}
486 
487 	if ((Z_TYPE(retval) == IS_OBJECT) &&
488 		(Z_OBJ(retval) == Z_OBJ_P(val))) {
489 		/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
490 		php_json_encode_array(buf, &retval, options);
491 	} else {
492 		/* All other types, encode as normal */
493 		php_json_encode(buf, &retval, options);
494 	}
495 
496 	zval_ptr_dtor(&retval);
497 	zval_ptr_dtor(&fname);
498 }
499 /* }}} */
500 
php_json_encode_zval(smart_str * buf,zval * val,int options)501 void php_json_encode_zval(smart_str *buf, zval *val, int options) /* {{{ */
502 {
503 again:
504 	switch (Z_TYPE_P(val))
505 	{
506 		case IS_NULL:
507 			smart_str_appendl(buf, "null", 4);
508 			break;
509 
510 		case IS_TRUE:
511 			smart_str_appendl(buf, "true", 4);
512 			break;
513 		case IS_FALSE:
514 			smart_str_appendl(buf, "false", 5);
515 			break;
516 
517 		case IS_LONG:
518 			smart_str_append_long(buf, Z_LVAL_P(val));
519 			break;
520 
521 		case IS_DOUBLE:
522 			if (php_json_is_valid_double(Z_DVAL_P(val))) {
523 				php_json_encode_double(buf, Z_DVAL_P(val), options);
524 			} else {
525 				JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN;
526 				smart_str_appendc(buf, '0');
527 			}
528 			break;
529 
530 		case IS_STRING:
531 			php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options);
532 			break;
533 
534 		case IS_OBJECT:
535 			if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
536 				php_json_encode_serializable_object(buf, val, options);
537 				break;
538 			}
539 			/* fallthrough -- Non-serializable object */
540 		case IS_ARRAY:
541 			php_json_encode_array(buf, val, options);
542 			break;
543 
544 		case IS_REFERENCE:
545 			val = Z_REFVAL_P(val);
546 			goto again;
547 
548 		default:
549 			JSON_G(error_code) = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
550 			smart_str_appendl(buf, "null", 4);
551 			break;
552 	}
553 
554 	return;
555 }
556 /* }}} */
557 
558 /*
559  * Local variables:
560  * tab-width: 4
561  * c-basic-offset: 4
562  * End:
563  * vim600: noet sw=4 ts=4 fdm=marker
564  * vim<600: noet sw=4 ts=4
565  */
566