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