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