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
php_json_check_stack_limit(void)34 static zend_always_inline bool php_json_check_stack_limit(void)
35 {
36 #ifdef ZEND_CHECK_STACK_LIMIT
37 return zend_call_stack_overflowed(EG(stack_limit));
38 #else
39 return false;
40 #endif
41 }
42
php_json_determine_array_type(zval * val)43 static int php_json_determine_array_type(zval *val) /* {{{ */
44 {
45 zend_array *myht = Z_ARRVAL_P(val);
46
47 if (myht) {
48 return zend_array_is_list(myht) ? PHP_JSON_OUTPUT_ARRAY : PHP_JSON_OUTPUT_OBJECT;
49 }
50
51 return PHP_JSON_OUTPUT_ARRAY;
52 }
53 /* }}} */
54
55 /* {{{ Pretty printing support functions */
56
php_json_pretty_print_char(smart_str * buf,int options,char c)57 static inline void php_json_pretty_print_char(smart_str *buf, int options, char c) /* {{{ */
58 {
59 if (options & PHP_JSON_PRETTY_PRINT) {
60 smart_str_appendc(buf, c);
61 }
62 }
63 /* }}} */
64
php_json_pretty_print_indent(smart_str * buf,int options,php_json_encoder * encoder)65 static inline void php_json_pretty_print_indent(smart_str *buf, int options, php_json_encoder *encoder) /* {{{ */
66 {
67 int i;
68
69 if (options & PHP_JSON_PRETTY_PRINT) {
70 for (i = 0; i < encoder->depth; ++i) {
71 smart_str_appendl(buf, " ", 4);
72 }
73 }
74 }
75 /* }}} */
76
77 /* }}} */
78
79 static
80 #if defined(_MSC_VER) && defined(_M_ARM64)
81 // MSVC bug: https://developercommunity.visualstudio.com/t/corrupt-optimization-on-arm64-with-Ox-/10102551
82 zend_never_inline
83 #else
84 inline
85 #endif
php_json_is_valid_double(double d)86 bool php_json_is_valid_double(double d) /* {{{ */
87 {
88 return !zend_isinf(d) && !zend_isnan(d);
89 }
90 /* }}} */
91
php_json_encode_double(smart_str * buf,double d,int options)92 static inline void php_json_encode_double(smart_str *buf, double d, int options) /* {{{ */
93 {
94 size_t len;
95 char num[ZEND_DOUBLE_MAX_LENGTH];
96
97 zend_gcvt(d, (int)PG(serialize_precision), '.', 'e', num);
98 len = strlen(num);
99 if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < ZEND_DOUBLE_MAX_LENGTH - 2) {
100 num[len++] = '.';
101 num[len++] = '0';
102 num[len] = '\0';
103 }
104 smart_str_appendl(buf, num, len);
105 }
106 /* }}} */
107
108 #define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \
109 do { \
110 if (_tmp_ht) { \
111 GC_TRY_PROTECT_RECURSION(_tmp_ht); \
112 } \
113 } while (0)
114
115 #define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \
116 do { \
117 if (_tmp_ht) { \
118 GC_TRY_UNPROTECT_RECURSION(_tmp_ht); \
119 } \
120 } while (0)
121
php_json_encode_array(smart_str * buf,zval * val,int options,php_json_encoder * encoder)122 static zend_result php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
123 {
124 int i, r, need_comma = 0;
125 HashTable *myht, *prop_ht;
126
127 if (php_json_check_stack_limit()) {
128 encoder->error_code = PHP_JSON_ERROR_DEPTH;
129 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
130 smart_str_appendl(buf, "null", 4);
131 }
132 return FAILURE;
133 }
134
135 if (Z_TYPE_P(val) == IS_ARRAY) {
136 myht = Z_ARRVAL_P(val);
137 prop_ht = NULL;
138 r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : php_json_determine_array_type(val);
139 } else if (Z_OBJ_P(val)->properties == NULL
140 && Z_OBJ_HT_P(val)->get_properties_for == NULL
141 && Z_OBJ_HT_P(val)->get_properties == zend_std_get_properties) {
142 /* Optimized version without rebuilding properties HashTable */
143 zend_object *obj = Z_OBJ_P(val);
144 zend_class_entry *ce = obj->ce;
145 zend_property_info *prop_info;
146 zval *prop;
147 int i;
148
149 if (GC_IS_RECURSIVE(obj)) {
150 encoder->error_code = PHP_JSON_ERROR_RECURSION;
151 smart_str_appendl(buf, "null", 4);
152 return FAILURE;
153 }
154
155 PHP_JSON_HASH_PROTECT_RECURSION(obj);
156
157 smart_str_appendc(buf, '{');
158
159 ++encoder->depth;
160
161 for (i = 0; i < ce->default_properties_count; i++) {
162 prop_info = ce->properties_info_table[i];
163 if (!prop_info) {
164 continue;
165 }
166 if (ZSTR_VAL(prop_info->name)[0] == '\0' && ZSTR_LEN(prop_info->name) > 0) {
167 /* Skip protected and private members. */
168 continue;
169 }
170 prop = OBJ_PROP(obj, prop_info->offset);
171 if (Z_TYPE_P(prop) == IS_UNDEF) {
172 continue;
173 }
174
175 if (need_comma) {
176 smart_str_appendc(buf, ',');
177 } else {
178 need_comma = 1;
179 }
180
181 php_json_pretty_print_char(buf, options, '\n');
182 php_json_pretty_print_indent(buf, options, encoder);
183
184 if (php_json_escape_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name),
185 options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE &&
186 (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) &&
187 buf->s) {
188 ZSTR_LEN(buf->s) -= 4;
189 smart_str_appendl(buf, "\"\"", 2);
190 }
191
192 smart_str_appendc(buf, ':');
193 php_json_pretty_print_char(buf, options, ' ');
194
195 if (php_json_encode_zval(buf, prop, options, encoder) == FAILURE &&
196 !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
197 PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
198 return FAILURE;
199 }
200 }
201
202 PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
203 if (encoder->depth > encoder->max_depth) {
204 encoder->error_code = PHP_JSON_ERROR_DEPTH;
205 if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
206 return FAILURE;
207 }
208 }
209 --encoder->depth;
210
211 if (need_comma) {
212 php_json_pretty_print_char(buf, options, '\n');
213 php_json_pretty_print_indent(buf, options, encoder);
214 }
215 smart_str_appendc(buf, '}');
216 return SUCCESS;
217 } else {
218 prop_ht = myht = zend_get_properties_for(val, ZEND_PROP_PURPOSE_JSON);
219 r = PHP_JSON_OUTPUT_OBJECT;
220 }
221
222 if (myht && GC_IS_RECURSIVE(myht)) {
223 encoder->error_code = PHP_JSON_ERROR_RECURSION;
224 smart_str_appendl(buf, "null", 4);
225 zend_release_properties(prop_ht);
226 return FAILURE;
227 }
228
229 PHP_JSON_HASH_PROTECT_RECURSION(myht);
230
231 if (r == PHP_JSON_OUTPUT_ARRAY) {
232 smart_str_appendc(buf, '[');
233 } else {
234 smart_str_appendc(buf, '{');
235 }
236
237 ++encoder->depth;
238
239 i = myht ? zend_hash_num_elements(myht) : 0;
240
241 if (i > 0) {
242 zend_string *key;
243 zval *data;
244 zend_ulong index;
245
246 ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
247 if (r == PHP_JSON_OUTPUT_ARRAY) {
248 if (need_comma) {
249 smart_str_appendc(buf, ',');
250 } else {
251 need_comma = 1;
252 }
253
254 php_json_pretty_print_char(buf, options, '\n');
255 php_json_pretty_print_indent(buf, options, encoder);
256 } else if (r == PHP_JSON_OUTPUT_OBJECT) {
257 if (key) {
258 if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) {
259 /* Skip protected and private members. */
260 continue;
261 }
262
263 if (need_comma) {
264 smart_str_appendc(buf, ',');
265 } else {
266 need_comma = 1;
267 }
268
269 php_json_pretty_print_char(buf, options, '\n');
270 php_json_pretty_print_indent(buf, options, encoder);
271
272 if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
273 options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE &&
274 (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) &&
275 buf->s) {
276 ZSTR_LEN(buf->s) -= 4;
277 smart_str_appendl(buf, "\"\"", 2);
278 }
279 } else {
280 if (need_comma) {
281 smart_str_appendc(buf, ',');
282 } else {
283 need_comma = 1;
284 }
285
286 php_json_pretty_print_char(buf, options, '\n');
287 php_json_pretty_print_indent(buf, options, encoder);
288
289 smart_str_appendc(buf, '"');
290 smart_str_append_long(buf, (zend_long) index);
291 smart_str_appendc(buf, '"');
292 }
293
294 smart_str_appendc(buf, ':');
295 php_json_pretty_print_char(buf, options, ' ');
296 }
297
298 if (php_json_encode_zval(buf, data, options, encoder) == FAILURE &&
299 !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
300 PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
301 zend_release_properties(prop_ht);
302 return FAILURE;
303 }
304 } ZEND_HASH_FOREACH_END();
305 }
306
307 PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
308
309 if (encoder->depth > encoder->max_depth) {
310 encoder->error_code = PHP_JSON_ERROR_DEPTH;
311 if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
312 zend_release_properties(prop_ht);
313 return FAILURE;
314 }
315 }
316 --encoder->depth;
317
318 /* Only keep closing bracket on same line for empty arrays/objects */
319 if (need_comma) {
320 php_json_pretty_print_char(buf, options, '\n');
321 php_json_pretty_print_indent(buf, options, encoder);
322 }
323
324 if (r == PHP_JSON_OUTPUT_ARRAY) {
325 smart_str_appendc(buf, ']');
326 } else {
327 smart_str_appendc(buf, '}');
328 }
329
330 zend_release_properties(prop_ht);
331 return SUCCESS;
332 }
333 /* }}} */
334
php_json_escape_string(smart_str * buf,const char * s,size_t len,int options,php_json_encoder * encoder)335 zend_result php_json_escape_string(
336 smart_str *buf, const char *s, size_t len,
337 int options, php_json_encoder *encoder) /* {{{ */
338 {
339 unsigned int us;
340 size_t pos, checkpoint;
341 char *dst;
342
343 if (len == 0) {
344 smart_str_appendl(buf, "\"\"", 2);
345 return SUCCESS;
346 }
347
348 if (options & PHP_JSON_NUMERIC_CHECK) {
349 double d;
350 int type;
351 zend_long p;
352
353 if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
354 if (type == IS_LONG) {
355 smart_str_append_long(buf, p);
356 return SUCCESS;
357 } else if (type == IS_DOUBLE && php_json_is_valid_double(d)) {
358 php_json_encode_double(buf, d, options);
359 return SUCCESS;
360 }
361 }
362
363 }
364 checkpoint = buf->s ? ZSTR_LEN(buf->s) : 0;
365
366 /* pre-allocate for string length plus 2 quotes */
367 smart_str_alloc(buf, len+2, 0);
368 smart_str_appendc(buf, '"');
369
370 pos = 0;
371
372 do {
373 static const uint32_t charmap[8] = {
374 0xffffffff, 0x500080c4, 0x10000000, 0x00000000,
375 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
376
377 us = (unsigned char)s[pos];
378 if (EXPECTED(!ZEND_BIT_TEST(charmap, us))) {
379 pos++;
380 len--;
381 if (len == 0) {
382 smart_str_appendl(buf, s, pos);
383 break;
384 }
385 } else {
386 if (pos) {
387 smart_str_appendl(buf, s, pos);
388 s += pos;
389 pos = 0;
390 }
391 us = (unsigned char)s[0];
392 if (UNEXPECTED(us >= 0x80)) {
393 zend_result status;
394 us = php_next_utf8_char((unsigned char *)s, len, &pos, &status);
395
396 /* check whether UTF8 character is correct */
397 if (UNEXPECTED(status != SUCCESS)) {
398 if (options & PHP_JSON_INVALID_UTF8_IGNORE) {
399 /* ignore invalid UTF8 character */
400 } else if (options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
401 /* Use Unicode character 'REPLACEMENT CHARACTER' (U+FFFD) */
402 if (options & PHP_JSON_UNESCAPED_UNICODE) {
403 smart_str_appendl(buf, "\xef\xbf\xbd", 3);
404 } else {
405 smart_str_appendl(buf, "\\ufffd", 6);
406 }
407 } else {
408 ZSTR_LEN(buf->s) = checkpoint;
409 encoder->error_code = PHP_JSON_ERROR_UTF8;
410 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
411 smart_str_appendl(buf, "null", 4);
412 }
413 return FAILURE;
414 }
415
416 /* Escape U+2028/U+2029 line terminators, UNLESS both
417 JSON_UNESCAPED_UNICODE and
418 JSON_UNESCAPED_LINE_TERMINATORS were provided */
419 } else if ((options & PHP_JSON_UNESCAPED_UNICODE)
420 && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS)
421 || us < 0x2028 || us > 0x2029)) {
422 smart_str_appendl(buf, s, pos);
423 } else {
424 /* From http://en.wikipedia.org/wiki/UTF16 */
425 if (us >= 0x10000) {
426 unsigned int next_us;
427
428 us -= 0x10000;
429 next_us = (unsigned short)((us & 0x3ff) | 0xdc00);
430 us = (unsigned short)((us >> 10) | 0xd800);
431 dst = smart_str_extend(buf, 6);
432 dst[0] = '\\';
433 dst[1] = 'u';
434 dst[2] = digits[(us >> 12) & 0xf];
435 dst[3] = digits[(us >> 8) & 0xf];
436 dst[4] = digits[(us >> 4) & 0xf];
437 dst[5] = digits[us & 0xf];
438 us = next_us;
439 }
440 dst = smart_str_extend(buf, 6);
441 dst[0] = '\\';
442 dst[1] = 'u';
443 dst[2] = digits[(us >> 12) & 0xf];
444 dst[3] = digits[(us >> 8) & 0xf];
445 dst[4] = digits[(us >> 4) & 0xf];
446 dst[5] = digits[us & 0xf];
447 }
448 s += pos;
449 len -= pos;
450 pos = 0;
451 } else {
452 s++;
453 switch (us) {
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 ZEND_ASSERT(us < ' ');
528 dst = smart_str_extend(buf, 6);
529 dst[0] = '\\';
530 dst[1] = 'u';
531 dst[2] = '0';
532 dst[3] = '0';
533 dst[4] = digits[(us >> 4) & 0xf];
534 dst[5] = digits[us & 0xf];
535 break;
536 }
537 len--;
538 }
539 }
540 } while (len);
541
542 smart_str_appendc(buf, '"');
543
544 return SUCCESS;
545 }
546 /* }}} */
547
php_json_encode_serializable_object(smart_str * buf,zval * val,int options,php_json_encoder * encoder)548 static zend_result php_json_encode_serializable_object(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
549 {
550 zend_class_entry *ce = Z_OBJCE_P(val);
551 zend_object *obj = Z_OBJ_P(val);
552 uint32_t *guard = zend_get_recursion_guard(obj);
553 zval retval, fname;
554 zend_result return_code;
555
556 ZEND_ASSERT(guard != NULL);
557
558 if (ZEND_GUARD_IS_RECURSIVE(guard, JSON)) {
559 encoder->error_code = PHP_JSON_ERROR_RECURSION;
560 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
561 smart_str_appendl(buf, "null", 4);
562 }
563 return FAILURE;
564 }
565
566 ZEND_GUARD_PROTECT_RECURSION(guard, JSON);
567
568 ZVAL_STRING(&fname, "jsonSerialize");
569
570 if (FAILURE == call_user_function(NULL, val, &fname, &retval, 0, NULL) || Z_TYPE(retval) == IS_UNDEF) {
571 if (!EG(exception)) {
572 zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name));
573 }
574 zval_ptr_dtor(&fname);
575
576 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
577 smart_str_appendl(buf, "null", 4);
578 }
579 ZEND_GUARD_UNPROTECT_RECURSION(guard, JSON);
580 return FAILURE;
581 }
582
583 if (EG(exception)) {
584 /* Error already raised */
585 zval_ptr_dtor(&retval);
586 zval_ptr_dtor(&fname);
587
588 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
589 smart_str_appendl(buf, "null", 4);
590 }
591 ZEND_GUARD_UNPROTECT_RECURSION(guard, JSON);
592 return FAILURE;
593 }
594
595 if ((Z_TYPE(retval) == IS_OBJECT) &&
596 (Z_OBJ(retval) == Z_OBJ_P(val))) {
597 /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
598 ZEND_GUARD_UNPROTECT_RECURSION(guard, JSON);
599 return_code = php_json_encode_array(buf, &retval, options, encoder);
600 } else {
601 /* All other types, encode as normal */
602 return_code = php_json_encode_zval(buf, &retval, options, encoder);
603 ZEND_GUARD_UNPROTECT_RECURSION(guard, JSON);
604 }
605
606 zval_ptr_dtor(&retval);
607 zval_ptr_dtor(&fname);
608
609 return return_code;
610 }
611 /* }}} */
612
php_json_encode_serializable_enum(smart_str * buf,zval * val,int options,php_json_encoder * encoder)613 static zend_result php_json_encode_serializable_enum(smart_str *buf, zval *val, int options, php_json_encoder *encoder)
614 {
615 zend_class_entry *ce = Z_OBJCE_P(val);
616 if (ce->enum_backing_type == IS_UNDEF) {
617 encoder->error_code = PHP_JSON_ERROR_NON_BACKED_ENUM;
618 smart_str_appendc(buf, '0');
619 return FAILURE;
620 }
621
622 zval *value_zv = zend_enum_fetch_case_value(Z_OBJ_P(val));
623 return php_json_encode_zval(buf, value_zv, options, encoder);
624 }
625
php_json_encode_zval(smart_str * buf,zval * val,int options,php_json_encoder * encoder)626 zend_result php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
627 {
628 again:
629 switch (Z_TYPE_P(val))
630 {
631 case IS_NULL:
632 smart_str_appendl(buf, "null", 4);
633 break;
634
635 case IS_TRUE:
636 smart_str_appendl(buf, "true", 4);
637 break;
638 case IS_FALSE:
639 smart_str_appendl(buf, "false", 5);
640 break;
641
642 case IS_LONG:
643 smart_str_append_long(buf, Z_LVAL_P(val));
644 break;
645
646 case IS_DOUBLE:
647 if (php_json_is_valid_double(Z_DVAL_P(val))) {
648 php_json_encode_double(buf, Z_DVAL_P(val), options);
649 } else {
650 encoder->error_code = PHP_JSON_ERROR_INF_OR_NAN;
651 smart_str_appendc(buf, '0');
652 }
653 break;
654
655 case IS_STRING:
656 return php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options, encoder);
657
658 case IS_OBJECT:
659 if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
660 return php_json_encode_serializable_object(buf, val, options, encoder);
661 }
662 if (Z_OBJCE_P(val)->ce_flags & ZEND_ACC_ENUM) {
663 return php_json_encode_serializable_enum(buf, val, options, encoder);
664 }
665 /* fallthrough -- Non-serializable object */
666 ZEND_FALLTHROUGH;
667 case IS_ARRAY: {
668 /* Avoid modifications (and potential freeing) of the array through a reference when a
669 * jsonSerialize() method is invoked. */
670 zval zv;
671 int res;
672 ZVAL_COPY(&zv, val);
673 res = php_json_encode_array(buf, &zv, options, encoder);
674 zval_ptr_dtor_nogc(&zv);
675 return res;
676 }
677
678 case IS_REFERENCE:
679 val = Z_REFVAL_P(val);
680 goto again;
681
682 default:
683 encoder->error_code = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
684 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
685 smart_str_appendl(buf, "null", 4);
686 }
687 return FAILURE;
688 }
689
690 return SUCCESS;
691 }
692 /* }}} */
693