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