1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | https://www.php.net/license/3_01.txt                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Author: Omar Kilani <omar@php.net>                                   |
14   |         Jakub Zelenka <bukka@php.net>                                |
15   +----------------------------------------------------------------------+
16 */
17 
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 
22 #include "php.h"
23 #include "php_ini.h"
24 #include "ext/standard/info.h"
25 #include "ext/standard/html.h"
26 #include "zend_smart_str.h"
27 #include "php_json.h"
28 #include "php_json_encoder.h"
29 #include <zend_exceptions.h>
30 #include "zend_enum.h"
31 
32 static const char digits[] = "0123456789abcdef";
33 
34 static int php_json_escape_string(
35 		smart_str *buf,	const char *s, size_t len,
36 		int options, php_json_encoder *encoder);
37 
php_json_determine_array_type(zval * val)38 static int php_json_determine_array_type(zval *val) /* {{{ */
39 {
40 	zend_array *myht = Z_ARRVAL_P(val);
41 
42 	if (myht) {
43 		return zend_array_is_list(myht) ? PHP_JSON_OUTPUT_ARRAY : PHP_JSON_OUTPUT_OBJECT;
44 	}
45 
46 	return PHP_JSON_OUTPUT_ARRAY;
47 }
48 /* }}} */
49 
50 /* {{{ Pretty printing support functions */
51 
php_json_pretty_print_char(smart_str * buf,int options,char c)52 static inline void php_json_pretty_print_char(smart_str *buf, int options, char c) /* {{{ */
53 {
54 	if (options & PHP_JSON_PRETTY_PRINT) {
55 		smart_str_appendc(buf, c);
56 	}
57 }
58 /* }}} */
59 
php_json_pretty_print_indent(smart_str * buf,int options,php_json_encoder * encoder)60 static inline void php_json_pretty_print_indent(smart_str *buf, int options, php_json_encoder *encoder) /* {{{ */
61 {
62 	int i;
63 
64 	if (options & PHP_JSON_PRETTY_PRINT) {
65 		for (i = 0; i < encoder->depth; ++i) {
66 			smart_str_appendl(buf, "    ", 4);
67 		}
68 	}
69 }
70 /* }}} */
71 
72 /* }}} */
73 
php_json_is_valid_double(double d)74 static inline int php_json_is_valid_double(double d) /* {{{ */
75 {
76 	return !zend_isinf(d) && !zend_isnan(d);
77 }
78 /* }}} */
79 
php_json_encode_double(smart_str * buf,double d,int options)80 static inline void php_json_encode_double(smart_str *buf, double d, int options) /* {{{ */
81 {
82 	size_t len;
83 	char num[ZEND_DOUBLE_MAX_LENGTH];
84 
85 	zend_gcvt(d, (int)PG(serialize_precision), '.', 'e', num);
86 	len = strlen(num);
87 	if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < ZEND_DOUBLE_MAX_LENGTH - 2) {
88 		num[len++] = '.';
89 		num[len++] = '0';
90 		num[len] = '\0';
91 	}
92 	smart_str_appendl(buf, num, len);
93 }
94 /* }}} */
95 
96 #define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \
97 	do { \
98 		if (_tmp_ht) { \
99 			GC_TRY_PROTECT_RECURSION(_tmp_ht); \
100 		} \
101 	} while (0)
102 
103 #define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \
104 	do { \
105 		if (_tmp_ht) { \
106 			GC_TRY_UNPROTECT_RECURSION(_tmp_ht); \
107 		} \
108 	} while (0)
109 
php_json_encode_array(smart_str * buf,zval * val,int options,php_json_encoder * encoder)110 static int php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
111 {
112 	int i, r, need_comma = 0;
113 	HashTable *myht, *prop_ht;
114 
115 	if (Z_TYPE_P(val) == IS_ARRAY) {
116 		myht = Z_ARRVAL_P(val);
117 		prop_ht = NULL;
118 		r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : php_json_determine_array_type(val);
119 	} else if (Z_OBJ_P(val)->properties == NULL
120 	 && Z_OBJ_HT_P(val)->get_properties_for == NULL
121 	 && Z_OBJ_HT_P(val)->get_properties == zend_std_get_properties) {
122 		/* Optimized version without rebuilding properties HashTable */
123 		zend_object *obj = Z_OBJ_P(val);
124 		zend_class_entry *ce = obj->ce;
125 		zend_property_info *prop_info;
126 		zval *prop;
127 		int i;
128 
129 		if (GC_IS_RECURSIVE(obj)) {
130 			encoder->error_code = PHP_JSON_ERROR_RECURSION;
131 			smart_str_appendl(buf, "null", 4);
132 			return FAILURE;
133 		}
134 
135 		PHP_JSON_HASH_PROTECT_RECURSION(obj);
136 
137 		smart_str_appendc(buf, '{');
138 
139 		++encoder->depth;
140 
141 		for (i = 0; i < ce->default_properties_count; i++) {
142 			prop_info = ce->properties_info_table[i];
143 			if (!prop_info) {
144 				continue;
145 			}
146 			if (ZSTR_VAL(prop_info->name)[0] == '\0' && ZSTR_LEN(prop_info->name) > 0) {
147 				/* Skip protected and private members. */
148 				continue;
149 			}
150 			prop = OBJ_PROP(obj, prop_info->offset);
151 			if (Z_TYPE_P(prop) == IS_UNDEF) {
152 				continue;
153 			}
154 
155 			if (need_comma) {
156 				smart_str_appendc(buf, ',');
157 			} else {
158 				need_comma = 1;
159 			}
160 
161 			php_json_pretty_print_char(buf, options, '\n');
162 			php_json_pretty_print_indent(buf, options, encoder);
163 
164 			if (php_json_escape_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name),
165 					options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE &&
166 					(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) &&
167 					buf->s) {
168 				ZSTR_LEN(buf->s) -= 4;
169 				smart_str_appendl(buf, "\"\"", 2);
170 			}
171 
172 			smart_str_appendc(buf, ':');
173 			php_json_pretty_print_char(buf, options, ' ');
174 
175 			if (php_json_encode_zval(buf, prop, options, encoder) == FAILURE &&
176 					!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
177 				PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
178 				return FAILURE;
179 			}
180 		}
181 
182 		PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
183 		if (encoder->depth > encoder->max_depth) {
184 			encoder->error_code = PHP_JSON_ERROR_DEPTH;
185 			if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
186 				return FAILURE;
187 			}
188 		}
189 		--encoder->depth;
190 
191 		if (need_comma) {
192 			php_json_pretty_print_char(buf, options, '\n');
193 			php_json_pretty_print_indent(buf, options, encoder);
194 		}
195 		smart_str_appendc(buf, '}');
196 		return SUCCESS;
197 	} else {
198 		prop_ht = myht = zend_get_properties_for(val, ZEND_PROP_PURPOSE_JSON);
199 		r = PHP_JSON_OUTPUT_OBJECT;
200 	}
201 
202 	if (myht && GC_IS_RECURSIVE(myht)) {
203 		encoder->error_code = PHP_JSON_ERROR_RECURSION;
204 		smart_str_appendl(buf, "null", 4);
205 		zend_release_properties(prop_ht);
206 		return FAILURE;
207 	}
208 
209 	PHP_JSON_HASH_PROTECT_RECURSION(myht);
210 
211 	if (r == PHP_JSON_OUTPUT_ARRAY) {
212 		smart_str_appendc(buf, '[');
213 	} else {
214 		smart_str_appendc(buf, '{');
215 	}
216 
217 	++encoder->depth;
218 
219 	i = myht ? zend_hash_num_elements(myht) : 0;
220 
221 	if (i > 0) {
222 		zend_string *key;
223 		zval *data;
224 		zend_ulong index;
225 
226 		ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
227 			if (r == PHP_JSON_OUTPUT_ARRAY) {
228 				if (need_comma) {
229 					smart_str_appendc(buf, ',');
230 				} else {
231 					need_comma = 1;
232 				}
233 
234 				php_json_pretty_print_char(buf, options, '\n');
235 				php_json_pretty_print_indent(buf, options, encoder);
236 			} else if (r == PHP_JSON_OUTPUT_OBJECT) {
237 				if (key) {
238 					if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) {
239 						/* Skip protected and private members. */
240 						continue;
241 					}
242 
243 					if (need_comma) {
244 						smart_str_appendc(buf, ',');
245 					} else {
246 						need_comma = 1;
247 					}
248 
249 					php_json_pretty_print_char(buf, options, '\n');
250 					php_json_pretty_print_indent(buf, options, encoder);
251 
252 					if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
253 								options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE &&
254 							(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) &&
255 							buf->s) {
256 						ZSTR_LEN(buf->s) -= 4;
257 						smart_str_appendl(buf, "\"\"", 2);
258 					}
259 				} else {
260 					if (need_comma) {
261 						smart_str_appendc(buf, ',');
262 					} else {
263 						need_comma = 1;
264 					}
265 
266 					php_json_pretty_print_char(buf, options, '\n');
267 					php_json_pretty_print_indent(buf, options, encoder);
268 
269 					smart_str_appendc(buf, '"');
270 					smart_str_append_long(buf, (zend_long) index);
271 					smart_str_appendc(buf, '"');
272 				}
273 
274 				smart_str_appendc(buf, ':');
275 				php_json_pretty_print_char(buf, options, ' ');
276 			}
277 
278 			if (php_json_encode_zval(buf, data, options, encoder) == FAILURE &&
279 					!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
280 				PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
281 				zend_release_properties(prop_ht);
282 				return FAILURE;
283 			}
284 		} ZEND_HASH_FOREACH_END();
285 	}
286 
287 	PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
288 
289 	if (encoder->depth > encoder->max_depth) {
290 		encoder->error_code = PHP_JSON_ERROR_DEPTH;
291 		if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
292 			zend_release_properties(prop_ht);
293 			return FAILURE;
294 		}
295 	}
296 	--encoder->depth;
297 
298 	/* Only keep closing bracket on same line for empty arrays/objects */
299 	if (need_comma) {
300 		php_json_pretty_print_char(buf, options, '\n');
301 		php_json_pretty_print_indent(buf, options, encoder);
302 	}
303 
304 	if (r == PHP_JSON_OUTPUT_ARRAY) {
305 		smart_str_appendc(buf, ']');
306 	} else {
307 		smart_str_appendc(buf, '}');
308 	}
309 
310 	zend_release_properties(prop_ht);
311 	return SUCCESS;
312 }
313 /* }}} */
314 
php_json_escape_string(smart_str * buf,const char * s,size_t len,int options,php_json_encoder * encoder)315 static int php_json_escape_string(
316 		smart_str *buf, const char *s, size_t len,
317 		int options, php_json_encoder *encoder) /* {{{ */
318 {
319 	int status;
320 	unsigned int us;
321 	size_t pos, checkpoint;
322 	char *dst;
323 
324 	if (len == 0) {
325 		smart_str_appendl(buf, "\"\"", 2);
326 		return SUCCESS;
327 	}
328 
329 	if (options & PHP_JSON_NUMERIC_CHECK) {
330 		double d;
331 		int type;
332 		zend_long p;
333 
334 		if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
335 			if (type == IS_LONG) {
336 				smart_str_append_long(buf, p);
337 				return SUCCESS;
338 			} else if (type == IS_DOUBLE && php_json_is_valid_double(d)) {
339 				php_json_encode_double(buf, d, options);
340 				return SUCCESS;
341 			}
342 		}
343 
344 	}
345 	checkpoint = buf->s ? ZSTR_LEN(buf->s) : 0;
346 
347 	/* pre-allocate for string length plus 2 quotes */
348 	smart_str_alloc(buf, len+2, 0);
349 	smart_str_appendc(buf, '"');
350 
351 	pos = 0;
352 
353 	do {
354 		static const uint32_t charmap[8] = {
355 			0xffffffff, 0x500080c4, 0x10000000, 0x00000000,
356 			0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
357 
358 		us = (unsigned char)s[pos];
359 		if (EXPECTED(!ZEND_BIT_TEST(charmap, us))) {
360 			pos++;
361 			len--;
362 			if (len == 0) {
363 				smart_str_appendl(buf, s, pos);
364 				break;
365 			}
366 		} else {
367 			if (pos) {
368 				smart_str_appendl(buf, s, pos);
369 				s += pos;
370 				pos = 0;
371 			}
372 			us = (unsigned char)s[0];
373 			if (UNEXPECTED(us >= 0x80)) {
374 
375 				us = php_next_utf8_char((unsigned char *)s, len, &pos, &status);
376 
377 				/* check whether UTF8 character is correct */
378 				if (UNEXPECTED(status != SUCCESS)) {
379 					if (options & PHP_JSON_INVALID_UTF8_IGNORE) {
380 						/* ignore invalid UTF8 character */
381 					} else if (options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
382 						/* Use Unicode character 'REPLACEMENT CHARACTER' (U+FFFD) */
383 						if (options & PHP_JSON_UNESCAPED_UNICODE) {
384 							smart_str_appendl(buf, "\xef\xbf\xbd", 3);
385 						} else {
386 							smart_str_appendl(buf, "\\ufffd", 6);
387 						}
388 					} else {
389 						ZSTR_LEN(buf->s) = checkpoint;
390 						encoder->error_code = PHP_JSON_ERROR_UTF8;
391 						if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
392 							smart_str_appendl(buf, "null", 4);
393 						}
394 						return FAILURE;
395 					}
396 
397 				/* Escape U+2028/U+2029 line terminators, UNLESS both
398 				   JSON_UNESCAPED_UNICODE and
399 				   JSON_UNESCAPED_LINE_TERMINATORS were provided */
400 				} else if ((options & PHP_JSON_UNESCAPED_UNICODE)
401 				    && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS)
402 						|| us < 0x2028 || us > 0x2029)) {
403 					smart_str_appendl(buf, s, pos);
404 				} else {
405 					/* From http://en.wikipedia.org/wiki/UTF16 */
406 					if (us >= 0x10000) {
407 						unsigned int next_us;
408 
409 						us -= 0x10000;
410 						next_us = (unsigned short)((us & 0x3ff) | 0xdc00);
411 						us = (unsigned short)((us >> 10) | 0xd800);
412 						dst = smart_str_extend(buf, 6);
413 						dst[0] = '\\';
414 						dst[1] = 'u';
415 						dst[2] = digits[(us >> 12) & 0xf];
416 						dst[3] = digits[(us >> 8) & 0xf];
417 						dst[4] = digits[(us >> 4) & 0xf];
418 						dst[5] = digits[us & 0xf];
419 						us = next_us;
420 					}
421 					dst = smart_str_extend(buf, 6);
422 					dst[0] = '\\';
423 					dst[1] = 'u';
424 					dst[2] = digits[(us >> 12) & 0xf];
425 					dst[3] = digits[(us >> 8) & 0xf];
426 					dst[4] = digits[(us >> 4) & 0xf];
427 					dst[5] = digits[us & 0xf];
428 				}
429 				s += pos;
430 				len -= pos;
431 				pos = 0;
432 			} else {
433 				s++;
434 				switch (us) {
435 					case '"':
436 						if (options & PHP_JSON_HEX_QUOT) {
437 							smart_str_appendl(buf, "\\u0022", 6);
438 						} else {
439 							smart_str_appendl(buf, "\\\"", 2);
440 						}
441 						break;
442 
443 					case '\\':
444 						smart_str_appendl(buf, "\\\\", 2);
445 						break;
446 
447 					case '/':
448 						if (options & PHP_JSON_UNESCAPED_SLASHES) {
449 							smart_str_appendc(buf, '/');
450 						} else {
451 							smart_str_appendl(buf, "\\/", 2);
452 						}
453 						break;
454 
455 					case '\b':
456 						smart_str_appendl(buf, "\\b", 2);
457 						break;
458 
459 					case '\f':
460 						smart_str_appendl(buf, "\\f", 2);
461 						break;
462 
463 					case '\n':
464 						smart_str_appendl(buf, "\\n", 2);
465 						break;
466 
467 					case '\r':
468 						smart_str_appendl(buf, "\\r", 2);
469 						break;
470 
471 					case '\t':
472 						smart_str_appendl(buf, "\\t", 2);
473 						break;
474 
475 					case '<':
476 						if (options & PHP_JSON_HEX_TAG) {
477 							smart_str_appendl(buf, "\\u003C", 6);
478 						} else {
479 							smart_str_appendc(buf, '<');
480 						}
481 						break;
482 
483 					case '>':
484 						if (options & PHP_JSON_HEX_TAG) {
485 							smart_str_appendl(buf, "\\u003E", 6);
486 						} else {
487 							smart_str_appendc(buf, '>');
488 						}
489 						break;
490 
491 					case '&':
492 						if (options & PHP_JSON_HEX_AMP) {
493 							smart_str_appendl(buf, "\\u0026", 6);
494 						} else {
495 							smart_str_appendc(buf, '&');
496 						}
497 						break;
498 
499 					case '\'':
500 						if (options & PHP_JSON_HEX_APOS) {
501 							smart_str_appendl(buf, "\\u0027", 6);
502 						} else {
503 							smart_str_appendc(buf, '\'');
504 						}
505 						break;
506 
507 					default:
508 						ZEND_ASSERT(us < ' ');
509 						dst = smart_str_extend(buf, 6);
510 						dst[0] = '\\';
511 						dst[1] = 'u';
512 						dst[2] = '0';
513 						dst[3] = '0';
514 						dst[4] = digits[(us >> 4) & 0xf];
515 						dst[5] = digits[us & 0xf];
516 						break;
517 				}
518 				len--;
519 			}
520 		}
521 	} while (len);
522 
523 	smart_str_appendc(buf, '"');
524 
525 	return SUCCESS;
526 }
527 /* }}} */
528 
php_json_encode_serializable_object(smart_str * buf,zval * val,int options,php_json_encoder * encoder)529 static int php_json_encode_serializable_object(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
530 {
531 	zend_class_entry *ce = Z_OBJCE_P(val);
532 	HashTable* myht = Z_OBJPROP_P(val);
533 	zval retval, fname;
534 	int return_code;
535 
536 	if (myht && GC_IS_RECURSIVE(myht)) {
537 		encoder->error_code = PHP_JSON_ERROR_RECURSION;
538 		if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
539 			smart_str_appendl(buf, "null", 4);
540 		}
541 		return FAILURE;
542 	}
543 
544 	PHP_JSON_HASH_PROTECT_RECURSION(myht);
545 
546 	ZVAL_STRING(&fname, "jsonSerialize");
547 
548 	if (FAILURE == call_user_function(NULL, val, &fname, &retval, 0, NULL) || Z_TYPE(retval) == IS_UNDEF) {
549 		if (!EG(exception)) {
550 			zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name));
551 		}
552 		zval_ptr_dtor(&fname);
553 
554 		if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
555 			smart_str_appendl(buf, "null", 4);
556 		}
557 		PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
558 		return FAILURE;
559 	}
560 
561 	if (EG(exception)) {
562 		/* Error already raised */
563 		zval_ptr_dtor(&retval);
564 		zval_ptr_dtor(&fname);
565 
566 		if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
567 			smart_str_appendl(buf, "null", 4);
568 		}
569 		PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
570 		return FAILURE;
571 	}
572 
573 	if ((Z_TYPE(retval) == IS_OBJECT) &&
574 		(Z_OBJ(retval) == Z_OBJ_P(val))) {
575 		/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
576 		PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
577 		return_code = php_json_encode_array(buf, &retval, options, encoder);
578 	} else {
579 		/* All other types, encode as normal */
580 		return_code = php_json_encode_zval(buf, &retval, options, encoder);
581 		PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
582 	}
583 
584 	zval_ptr_dtor(&retval);
585 	zval_ptr_dtor(&fname);
586 
587 	return return_code;
588 }
589 /* }}} */
590 
php_json_encode_serializable_enum(smart_str * buf,zval * val,int options,php_json_encoder * encoder)591 static int php_json_encode_serializable_enum(smart_str *buf, zval *val, int options, php_json_encoder *encoder)
592 {
593 	zend_class_entry *ce = Z_OBJCE_P(val);
594 	if (ce->enum_backing_type == IS_UNDEF) {
595 		encoder->error_code = PHP_JSON_ERROR_NON_BACKED_ENUM;
596 		smart_str_appendc(buf, '0');
597 		return FAILURE;
598 	}
599 
600 	zval *value_zv = zend_enum_fetch_case_value(Z_OBJ_P(val));
601 	return php_json_encode_zval(buf, value_zv, options, encoder);
602 }
603 
php_json_encode_zval(smart_str * buf,zval * val,int options,php_json_encoder * encoder)604 int php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
605 {
606 again:
607 	switch (Z_TYPE_P(val))
608 	{
609 		case IS_NULL:
610 			smart_str_appendl(buf, "null", 4);
611 			break;
612 
613 		case IS_TRUE:
614 			smart_str_appendl(buf, "true", 4);
615 			break;
616 		case IS_FALSE:
617 			smart_str_appendl(buf, "false", 5);
618 			break;
619 
620 		case IS_LONG:
621 			smart_str_append_long(buf, Z_LVAL_P(val));
622 			break;
623 
624 		case IS_DOUBLE:
625 			if (php_json_is_valid_double(Z_DVAL_P(val))) {
626 				php_json_encode_double(buf, Z_DVAL_P(val), options);
627 			} else {
628 				encoder->error_code = PHP_JSON_ERROR_INF_OR_NAN;
629 				smart_str_appendc(buf, '0');
630 			}
631 			break;
632 
633 		case IS_STRING:
634 			return php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options, encoder);
635 
636 		case IS_OBJECT:
637 			if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
638 				return php_json_encode_serializable_object(buf, val, options, encoder);
639 			}
640 			if (Z_OBJCE_P(val)->ce_flags & ZEND_ACC_ENUM) {
641 				return php_json_encode_serializable_enum(buf, val, options, encoder);
642 			}
643 			/* fallthrough -- Non-serializable object */
644 			ZEND_FALLTHROUGH;
645 		case IS_ARRAY: {
646 			/* Avoid modifications (and potential freeing) of the array through a reference when a
647 			 * jsonSerialize() method is invoked. */
648 			zval zv;
649 			int res;
650 			ZVAL_COPY(&zv, val);
651 			res = php_json_encode_array(buf, &zv, options, encoder);
652 			zval_ptr_dtor_nogc(&zv);
653 			return res;
654 		}
655 
656 		case IS_REFERENCE:
657 			val = Z_REFVAL_P(val);
658 			goto again;
659 
660 		default:
661 			encoder->error_code = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
662 			if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
663 				smart_str_appendl(buf, "null", 4);
664 			}
665 			return FAILURE;
666 	}
667 
668 	return SUCCESS;
669 }
670 /* }}} */
671