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