1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2013 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
42zend_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_EXISTANT) {
189                break;
190            }
191
192            if (i == HASH_KEY_IS_STRING) {
193                return 1;
194            } else {
195                if (index != idx) {
196                    return 1;
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;
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_pretty_print_char(buf, options, '\n' TSRMLS_CC);
257    ++JSON_G(encoder_depth);
258
259    i = myht ? zend_hash_num_elements(myht) : 0;
260
261    if (i > 0)
262    {
263        char *key;
264        zval **data;
265        ulong index;
266        uint key_len;
267        HashPosition pos;
268        HashTable *tmp_ht;
269        int need_comma = 0;
270
271        zend_hash_internal_pointer_reset_ex(myht, &pos);
272        for (;; zend_hash_move_forward_ex(myht, &pos)) {
273            i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
274            if (i == HASH_KEY_NON_EXISTANT)
275                break;
276
277            if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
278                tmp_ht = HASH_OF(*data);
279                if (tmp_ht) {
280                    tmp_ht->nApplyCount++;
281                }
282
283                if (r == PHP_JSON_OUTPUT_ARRAY) {
284                    if (need_comma) {
285                        smart_str_appendc(buf, ',');
286                        json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
287                    } else {
288                        need_comma = 1;
289                    }
290
291                    json_pretty_print_indent(buf, options TSRMLS_CC);
292                    php_json_encode(buf, *data, options TSRMLS_CC);
293                } else if (r == PHP_JSON_OUTPUT_OBJECT) {
294                    if (i == HASH_KEY_IS_STRING) {
295                        if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
296                            /* Skip protected and private members. */
297                            if (tmp_ht) {
298                                tmp_ht->nApplyCount--;
299                            }
300                            continue;
301                        }
302
303                        if (need_comma) {
304                            smart_str_appendc(buf, ',');
305                            json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
306                        } else {
307                            need_comma = 1;
308                        }
309
310                        json_pretty_print_indent(buf, options TSRMLS_CC);
311
312                        json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
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                    } else {
319                        if (need_comma) {
320                            smart_str_appendc(buf, ',');
321                            json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
322                        } else {
323                            need_comma = 1;
324                        }
325
326                        json_pretty_print_indent(buf, options TSRMLS_CC);
327
328                        smart_str_appendc(buf, '"');
329                        smart_str_append_long(buf, (long) index);
330                        smart_str_appendc(buf, '"');
331                        smart_str_appendc(buf, ':');
332
333                        json_pretty_print_char(buf, options, ' ' TSRMLS_CC);
334
335                        php_json_encode(buf, *data, options TSRMLS_CC);
336                    }
337                }
338
339                if (tmp_ht) {
340                    tmp_ht->nApplyCount--;
341                }
342            }
343        }
344    }
345
346    if (JSON_G(encoder_depth) > JSON_G(encode_max_depth)) {
347        JSON_G(error_code) = PHP_JSON_ERROR_DEPTH;
348    }
349    --JSON_G(encoder_depth);
350    json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
351    json_pretty_print_indent(buf, options TSRMLS_CC);
352
353    if (r == PHP_JSON_OUTPUT_ARRAY) {
354        smart_str_appendc(buf, ']');
355    } else {
356        smart_str_appendc(buf, '}');
357    }
358}
359/* }}} */
360
361static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{{ */
362{
363    size_t pos = 0, us;
364    int j, status;
365
366    if (utf16) {
367        /* really convert the utf8 string */
368        for (j=0 ; pos < len ; j++) {
369            us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
370            if (status != SUCCESS) {
371                return -1;
372            }
373            /* From http://en.wikipedia.org/wiki/UTF16 */
374            if (us >= 0x10000) {
375                us -= 0x10000;
376                utf16[j++] = (unsigned short)((us >> 10) | 0xd800);
377                utf16[j] = (unsigned short)((us & 0x3ff) | 0xdc00);
378            } else {
379                utf16[j] = (unsigned short)us;
380            }
381        }
382    } else {
383        /* Only check if utf8 string is valid, and compute utf16 lenght */
384        for (j=0 ; pos < len ; j++) {
385            us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
386            if (status != SUCCESS) {
387                return -1;
388            }
389            if (us >= 0x10000) {
390                j++;
391            }
392        }
393    }
394    return j;
395}
396/* }}} */
397
398
399static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
400{
401    int pos = 0, ulen = 0;
402    unsigned short us;
403    unsigned short *utf16;
404    size_t newlen;
405
406    if (len == 0) {
407        smart_str_appendl(buf, "\"\"", 2);
408        return;
409    }
410
411    if (options & PHP_JSON_NUMERIC_CHECK) {
412        double d;
413        int type;
414        long p;
415
416        if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
417            if (type == IS_LONG) {
418                smart_str_append_long(buf, p);
419            } else if (type == IS_DOUBLE) {
420                if (!zend_isinf(d) && !zend_isnan(d)) {
421                    char *tmp;
422                    int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
423                    smart_str_appendl(buf, tmp, l);
424                    efree(tmp);
425                } else {
426                    JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN;
427                    smart_str_appendc(buf, '0');
428                }
429            }
430            return;
431        }
432
433    }
434
435    utf16 = (options & PHP_JSON_UNESCAPED_UNICODE) ? NULL : (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
436    ulen = json_utf8_to_utf16(utf16, s, len);
437    if (ulen <= 0) {
438        if (utf16) {
439            efree(utf16);
440        }
441        if (ulen < 0) {
442            JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
443            smart_str_appendl(buf, "null", 4);
444        } else {
445            smart_str_appendl(buf, "\"\"", 2);
446        }
447        return;
448    }
449    if (!(options & PHP_JSON_UNESCAPED_UNICODE)) {
450        len = ulen;
451    }
452
453    /* pre-allocate for string length plus 2 quotes */
454    smart_str_alloc(buf, len+2, 0);
455    smart_str_appendc(buf, '"');
456
457    while (pos < len)
458    {
459        us = (options & PHP_JSON_UNESCAPED_UNICODE) ? s[pos++] : utf16[pos++];
460
461        switch (us)
462        {
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    }
548
549    smart_str_appendc(buf, '"');
550    if (utf16) {
551        efree(utf16);
552    }
553}
554/* }}} */
555
556
557static void json_encode_serializable_object(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
558{
559    zend_class_entry *ce = Z_OBJCE_P(val);
560    zval *retval = NULL, fname;
561    HashTable* myht;
562
563    if (Z_TYPE_P(val) == IS_ARRAY) {
564        myht = HASH_OF(val);
565    } else {
566        myht = Z_OBJPROP_P(val);
567    }
568
569    if (myht && myht->nApplyCount > 1) {
570        JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
571        smart_str_appendl(buf, "null", 4);
572        return;
573    }
574
575    ZVAL_STRING(&fname, "jsonSerialize", 0);
576
577    if (FAILURE == call_user_function_ex(EG(function_table), &val, &fname, &retval, 0, NULL, 1, NULL TSRMLS_CC) || !retval) {
578        zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Failed calling %s::jsonSerialize()", ce->name);
579        smart_str_appendl(buf, "null", sizeof("null") - 1);
580        return;
581    }
582
583    if (EG(exception)) {
584        /* Error already raised */
585        zval_ptr_dtor(&retval);
586        smart_str_appendl(buf, "null", sizeof("null") - 1);
587        return;
588    }
589
590    if ((Z_TYPE_P(retval) == IS_OBJECT) &&
591        (Z_OBJ_HANDLE_P(retval) == Z_OBJ_HANDLE_P(val))) {
592        /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
593        json_encode_array(buf, &retval, options TSRMLS_CC);
594    } else {
595        /* All other types, encode as normal */
596        php_json_encode(buf, retval, options TSRMLS_CC);
597    }
598
599    zval_ptr_dtor(&retval);
600}
601/* }}} */
602
603PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
604{
605    switch (Z_TYPE_P(val))
606    {
607        case IS_NULL:
608            smart_str_appendl(buf, "null", 4);
609            break;
610
611        case IS_BOOL:
612            if (Z_BVAL_P(val)) {
613                smart_str_appendl(buf, "true", 4);
614            } else {
615                smart_str_appendl(buf, "false", 5);
616            }
617            break;
618
619        case IS_LONG:
620            smart_str_append_long(buf, Z_LVAL_P(val));
621            break;
622
623        case IS_DOUBLE:
624            {
625                char *d = NULL;
626                int len;
627                double dbl = Z_DVAL_P(val);
628
629                if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
630                    len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
631                    smart_str_appendl(buf, d, len);
632                    efree(d);
633                } else {
634                    JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN;
635                    smart_str_appendc(buf, '0');
636                }
637            }
638            break;
639
640        case IS_STRING:
641            json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
642            break;
643
644        case IS_OBJECT:
645            if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce TSRMLS_CC)) {
646                json_encode_serializable_object(buf, val, options TSRMLS_CC);
647                break;
648            }
649            /* fallthrough -- Non-serializable object */
650        case IS_ARRAY:
651            json_encode_array(buf, &val, options TSRMLS_CC);
652            break;
653
654        default:
655            JSON_G(error_code) = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
656            smart_str_appendl(buf, "null", 4);
657            break;
658    }
659
660    return;
661}
662/* }}} */
663
664PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */
665{
666    int utf16_len;
667    zval *z;
668    unsigned short *utf16;
669    JSON_parser jp;
670
671    utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
672
673    utf16_len = json_utf8_to_utf16(utf16, str, str_len);
674    if (utf16_len <= 0) {
675        if (utf16) {
676            efree(utf16);
677        }
678        JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
679        RETURN_NULL();
680    }
681
682    if (depth <= 0) {
683        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero");
684        efree(utf16);
685        RETURN_NULL();
686    }
687
688    ALLOC_INIT_ZVAL(z);
689    jp = new_JSON_parser(depth);
690    if (parse_JSON_ex(jp, z, utf16, utf16_len, options TSRMLS_CC)) {
691        *return_value = *z;
692    }
693    else
694    {
695        double d;
696        int type, overflow_info;
697        long p;
698
699        RETVAL_NULL();
700        if (str_len == 4) {
701            if (!strcasecmp(str, "null")) {
702                /* We need to explicitly clear the error because its an actual NULL and not an error */
703                jp->error_code = PHP_JSON_ERROR_NONE;
704                RETVAL_NULL();
705            } else if (!strcasecmp(str, "true")) {
706                RETVAL_BOOL(1);
707            }
708        } else if (str_len == 5 && !strcasecmp(str, "false")) {
709            RETVAL_BOOL(0);
710        }
711
712        if ((type = is_numeric_string_ex(str, str_len, &p, &d, 0, &overflow_info)) != 0) {
713            if (type == IS_LONG) {
714                RETVAL_LONG(p);
715            } else if (type == IS_DOUBLE) {
716                if (options & PHP_JSON_BIGINT_AS_STRING && overflow_info) {
717                    /* Within an object or array, a numeric literal is assumed
718                     * to be an integer if and only if it's entirely made up of
719                     * digits (exponent notation will result in the number
720                     * being treated as a double). We'll match that behaviour
721                     * here. */
722                    int i;
723                    zend_bool is_float = 0;
724
725                    for (i = (str[0] == '-' ? 1 : 0); i < str_len; i++) {
726                        /* Not using isdigit() because it's locale specific,
727                         * but we expect JSON input to always be UTF-8. */
728                        if (str[i] < '0' || str[i] > '9') {
729                            is_float = 1;
730                            break;
731                        }
732                    }
733
734                    if (is_float) {
735                        RETVAL_DOUBLE(d);
736                    } else {
737                        RETVAL_STRINGL(str, str_len, 1);
738                    }
739                } else {
740                    RETVAL_DOUBLE(d);
741                }
742            }
743        }
744
745        if (Z_TYPE_P(return_value) != IS_NULL) {
746            jp->error_code = PHP_JSON_ERROR_NONE;
747        }
748
749        zval_dtor(z);
750    }
751    FREE_ZVAL(z);
752    efree(utf16);
753    JSON_G(error_code) = jp->error_code;
754    free_JSON_parser(jp);
755}
756/* }}} */
757
758
759/* {{{ proto string json_encode(mixed data [, int options[, int depth]])
760   Returns the JSON representation of a value */
761static PHP_FUNCTION(json_encode)
762{
763    zval *parameter;
764    smart_str buf = {0};
765    long options = 0;
766    long depth = JSON_PARSER_DEFAULT_DEPTH;
767
768    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &parameter, &options, &depth) == FAILURE) {
769        return;
770    }
771
772    JSON_G(error_code) = PHP_JSON_ERROR_NONE;
773
774    JSON_G(encode_max_depth) = depth;
775
776    php_json_encode(&buf, parameter, options TSRMLS_CC);
777
778    if (JSON_G(error_code) != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
779        ZVAL_FALSE(return_value);
780    } else {
781        ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
782    }
783
784    smart_str_free(&buf);
785}
786/* }}} */
787
788/* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]])
789   Decodes the JSON representation into a PHP value */
790static PHP_FUNCTION(json_decode)
791{
792    char *str;
793    int str_len;
794    zend_bool assoc = 0; /* return JS objects as PHP objects by default */
795    long depth = JSON_PARSER_DEFAULT_DEPTH;
796    long options = 0;
797
798    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) {
799        return;
800    }
801
802    JSON_G(error_code) = 0;
803
804    if (!str_len) {
805        RETURN_NULL();
806    }
807
808    /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */
809    if (assoc) {
810        options |=  PHP_JSON_OBJECT_AS_ARRAY;
811    } else {
812        options &= ~PHP_JSON_OBJECT_AS_ARRAY;
813    }
814
815    php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC);
816}
817/* }}} */
818
819/* {{{ proto int json_last_error()
820   Returns the error code of the last json_encode() or json_decode() call. */
821static PHP_FUNCTION(json_last_error)
822{
823    if (zend_parse_parameters_none() == FAILURE) {
824        return;
825    }
826
827    RETURN_LONG(JSON_G(error_code));
828}
829/* }}} */
830
831/* {{{ proto string json_last_error_msg()
832   Returns the error string of the last json_encode() or json_decode() call. */
833static PHP_FUNCTION(json_last_error_msg)
834{
835    if (zend_parse_parameters_none() == FAILURE) {
836        return;
837    }
838
839    switch(JSON_G(error_code)) {
840        case PHP_JSON_ERROR_NONE:
841            RETURN_STRING("No error", 1);
842        case PHP_JSON_ERROR_DEPTH:
843            RETURN_STRING("Maximum stack depth exceeded", 1);
844        case PHP_JSON_ERROR_STATE_MISMATCH:
845            RETURN_STRING("State mismatch (invalid or malformed JSON)", 1);
846        case PHP_JSON_ERROR_CTRL_CHAR:
847            RETURN_STRING("Control character error, possibly incorrectly encoded", 1);
848        case PHP_JSON_ERROR_SYNTAX:
849            RETURN_STRING("Syntax error", 1);
850        case PHP_JSON_ERROR_UTF8:
851            RETURN_STRING("Malformed UTF-8 characters, possibly incorrectly encoded", 1);
852        case PHP_JSON_ERROR_RECURSION:
853            RETURN_STRING("Recursion detected", 1);
854        case PHP_JSON_ERROR_INF_OR_NAN:
855            RETURN_STRING("Inf and NaN cannot be JSON encoded", 1);
856        case PHP_JSON_ERROR_UNSUPPORTED_TYPE:
857            RETURN_STRING("Type is not supported", 1);
858        default:
859            RETURN_STRING("Unknown error", 1);
860    }
861
862}
863/* }}} */
864
865/*
866 * Local variables:
867 * tab-width: 4
868 * c-basic-offset: 4
869 * End:
870 * vim600: noet sw=4 ts=4 fdm=marker
871 * vim<600: noet sw=4 ts=4
872 */
873