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);
38
39static const char digits[] = "0123456789abcdef";
40
41PHP_JSON_API zend_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, need_comma = 0;
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_G(encoder_depth);
246
247    i = myht ? zend_hash_num_elements(myht) : 0;
248
249    if (i > 0)
250    {
251        char *key;
252        zval **data;
253        ulong index;
254        uint key_len;
255        HashPosition pos;
256        HashTable *tmp_ht;
257
258        zend_hash_internal_pointer_reset_ex(myht, &pos);
259        for (;; zend_hash_move_forward_ex(myht, &pos)) {
260            i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
261            if (i == HASH_KEY_NON_EXISTANT)
262                break;
263
264            if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
265                tmp_ht = HASH_OF(*data);
266                if (tmp_ht) {
267                    tmp_ht->nApplyCount++;
268                }
269
270                if (r == PHP_JSON_OUTPUT_ARRAY) {
271                    if (need_comma) {
272                        smart_str_appendc(buf, ',');
273                    } else {
274                        need_comma = 1;
275                    }
276
277                    json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
278                    json_pretty_print_indent(buf, options TSRMLS_CC);
279                    php_json_encode(buf, *data, options TSRMLS_CC);
280                } else if (r == PHP_JSON_OUTPUT_OBJECT) {
281                    if (i == HASH_KEY_IS_STRING) {
282                        if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
283                            /* Skip protected and private members. */
284                            if (tmp_ht) {
285                                tmp_ht->nApplyCount--;
286                            }
287                            continue;
288                        }
289
290                        if (need_comma) {
291                            smart_str_appendc(buf, ',');
292                        } else {
293                            need_comma = 1;
294                        }
295
296                        json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
297                        json_pretty_print_indent(buf, options TSRMLS_CC);
298
299                        json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
300                        smart_str_appendc(buf, ':');
301
302                        json_pretty_print_char(buf, options, ' ' TSRMLS_CC);
303
304                        php_json_encode(buf, *data, options TSRMLS_CC);
305                    } else {
306                        if (need_comma) {
307                            smart_str_appendc(buf, ',');
308                        } else {
309                            need_comma = 1;
310                        }
311
312                        json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
313                        json_pretty_print_indent(buf, options TSRMLS_CC);
314
315                        smart_str_appendc(buf, '"');
316                        smart_str_append_long(buf, (long) index);
317                        smart_str_appendc(buf, '"');
318                        smart_str_appendc(buf, ':');
319
320                        json_pretty_print_char(buf, options, ' ' TSRMLS_CC);
321
322                        php_json_encode(buf, *data, options TSRMLS_CC);
323                    }
324                }
325
326                if (tmp_ht) {
327                    tmp_ht->nApplyCount--;
328                }
329            }
330        }
331    }
332
333    --JSON_G(encoder_depth);
334
335    /* Only keep closing bracket on same line for empty arrays/objects */
336    if (need_comma) {
337        json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
338        json_pretty_print_indent(buf, options TSRMLS_CC);
339    }
340
341    if (r == PHP_JSON_OUTPUT_ARRAY) {
342        smart_str_appendc(buf, ']');
343    } else {
344        smart_str_appendc(buf, '}');
345    }
346}
347/* }}} */
348
349static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{{ */
350{
351    size_t pos = 0, us;
352    int j, status;
353
354    if (utf16) {
355        /* really convert the utf8 string */
356        for (j=0 ; pos < len ; j++) {
357            us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
358            if (status != SUCCESS) {
359                return -1;
360            }
361            /* From http://en.wikipedia.org/wiki/UTF16 */
362            if (us >= 0x10000) {
363                us -= 0x10000;
364                utf16[j++] = (unsigned short)((us >> 10) | 0xd800);
365                utf16[j] = (unsigned short)((us & 0x3ff) | 0xdc00);
366            } else {
367                utf16[j] = (unsigned short)us;
368            }
369        }
370    } else {
371        /* Only check if utf8 string is valid, and compute utf16 length */
372        for (j=0 ; pos < len ; j++) {
373            us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
374            if (status != SUCCESS) {
375                return -1;
376            }
377            if (us >= 0x10000) {
378                j++;
379            }
380        }
381    }
382    return j;
383}
384/* }}} */
385
386
387static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
388{
389    int pos = 0, ulen = 0;
390    unsigned short us;
391    unsigned short *utf16;
392    size_t newlen;
393
394    if (len == 0) {
395        smart_str_appendl(buf, "\"\"", 2);
396        return;
397    }
398
399    if (options & PHP_JSON_NUMERIC_CHECK) {
400        double d;
401        int type;
402        long p;
403
404        if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
405            if (type == IS_LONG) {
406                smart_str_append_long(buf, p);
407            } else if (type == IS_DOUBLE) {
408                if (!zend_isinf(d) && !zend_isnan(d)) {
409                    char *tmp;
410                    int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
411                    smart_str_appendl(buf, tmp, l);
412                    efree(tmp);
413                } else {
414                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d);
415                    smart_str_appendc(buf, '0');
416                }
417            }
418            return;
419        }
420
421    }
422
423    utf16 = (options & PHP_JSON_UNESCAPED_UNICODE) ? NULL : (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
424    ulen = json_utf8_to_utf16(utf16, s, len);
425    if (ulen <= 0) {
426        if (utf16) {
427            efree(utf16);
428        }
429        if (ulen < 0) {
430            JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
431            if (!PG(display_errors)) {
432                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
433            }
434            smart_str_appendl(buf, "null", 4);
435        } else {
436            smart_str_appendl(buf, "\"\"", 2);
437        }
438        return;
439    }
440    if (!(options & PHP_JSON_UNESCAPED_UNICODE)) {
441        len = ulen;
442    }
443
444    /* pre-allocate for string length plus 2 quotes */
445    smart_str_alloc(buf, len+2, 0);
446    smart_str_appendc(buf, '"');
447
448    while (pos < len)
449    {
450        us = (options & PHP_JSON_UNESCAPED_UNICODE) ? s[pos++] : utf16[pos++];
451
452        switch (us)
453        {
454            case '"':
455                if (options & PHP_JSON_HEX_QUOT) {
456                    smart_str_appendl(buf, "\\u0022", 6);
457                } else {
458                    smart_str_appendl(buf, "\\\"", 2);
459                }
460                break;
461
462            case '\\':
463                smart_str_appendl(buf, "\\\\", 2);
464                break;
465
466            case '/':
467                if (options & PHP_JSON_UNESCAPED_SLASHES) {
468                    smart_str_appendc(buf, '/');
469                } else {
470                    smart_str_appendl(buf, "\\/", 2);
471                }
472                break;
473
474            case '\b':
475                smart_str_appendl(buf, "\\b", 2);
476                break;
477
478            case '\f':
479                smart_str_appendl(buf, "\\f", 2);
480                break;
481
482            case '\n':
483                smart_str_appendl(buf, "\\n", 2);
484                break;
485
486            case '\r':
487                smart_str_appendl(buf, "\\r", 2);
488                break;
489
490            case '\t':
491                smart_str_appendl(buf, "\\t", 2);
492                break;
493
494            case '<':
495                if (options & PHP_JSON_HEX_TAG) {
496                    smart_str_appendl(buf, "\\u003C", 6);
497                } else {
498                    smart_str_appendc(buf, '<');
499                }
500                break;
501
502            case '>':
503                if (options & PHP_JSON_HEX_TAG) {
504                    smart_str_appendl(buf, "\\u003E", 6);
505                } else {
506                    smart_str_appendc(buf, '>');
507                }
508                break;
509
510            case '&':
511                if (options & PHP_JSON_HEX_AMP) {
512                    smart_str_appendl(buf, "\\u0026", 6);
513                } else {
514                    smart_str_appendc(buf, '&');
515                }
516                break;
517
518            case '\'':
519                if (options & PHP_JSON_HEX_APOS) {
520                    smart_str_appendl(buf, "\\u0027", 6);
521                } else {
522                    smart_str_appendc(buf, '\'');
523                }
524                break;
525
526            default:
527                if (us >= ' ' && ((options & PHP_JSON_UNESCAPED_UNICODE) || (us & 127) == us)) {
528                    smart_str_appendc(buf, (unsigned char) us);
529                } else {
530                    smart_str_appendl(buf, "\\u", 2);
531                    smart_str_appendc(buf, digits[(us & 0xf000) >> 12]);
532                    smart_str_appendc(buf, digits[(us & 0xf00)  >> 8]);
533                    smart_str_appendc(buf, digits[(us & 0xf0)   >> 4]);
534                    smart_str_appendc(buf, digits[(us & 0xf)]);
535                }
536                break;
537        }
538    }
539
540    smart_str_appendc(buf, '"');
541    if (utf16) {
542        efree(utf16);
543    }
544}
545/* }}} */
546
547
548static void json_encode_serializable_object(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
549{
550    zend_class_entry *ce = Z_OBJCE_P(val);
551    zval *retval = NULL, fname;
552    HashTable* myht;
553
554    if (Z_TYPE_P(val) == IS_ARRAY) {
555        myht = HASH_OF(val);
556    } else {
557        myht = Z_OBJPROP_P(val);
558    }
559
560    if (myht && myht->nApplyCount > 1) {
561        php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
562        smart_str_appendl(buf, "null", 4);
563        return;
564    }
565
566    ZVAL_STRING(&fname, "jsonSerialize", 0);
567
568    if (FAILURE == call_user_function_ex(EG(function_table), &val, &fname, &retval, 0, NULL, 1, NULL TSRMLS_CC) || !retval) {
569        zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Failed calling %s::jsonSerialize()", ce->name);
570        smart_str_appendl(buf, "null", sizeof("null") - 1);
571        return;
572    }
573
574    if (EG(exception)) {
575        /* Error already raised */
576        zval_ptr_dtor(&retval);
577        smart_str_appendl(buf, "null", sizeof("null") - 1);
578        return;
579    }
580
581    if ((Z_TYPE_P(retval) == IS_OBJECT) &&
582        (Z_OBJ_HANDLE_P(retval) == Z_OBJ_HANDLE_P(val))) {
583        /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
584        json_encode_array(buf, &retval, options TSRMLS_CC);
585    } else {
586        /* All other types, encode as normal */
587        php_json_encode(buf, retval, options TSRMLS_CC);
588    }
589
590    zval_ptr_dtor(&retval);
591}
592/* }}} */
593
594PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
595{
596    switch (Z_TYPE_P(val))
597    {
598        case IS_NULL:
599            smart_str_appendl(buf, "null", 4);
600            break;
601
602        case IS_BOOL:
603            if (Z_BVAL_P(val)) {
604                smart_str_appendl(buf, "true", 4);
605            } else {
606                smart_str_appendl(buf, "false", 5);
607            }
608            break;
609
610        case IS_LONG:
611            smart_str_append_long(buf, Z_LVAL_P(val));
612            break;
613
614        case IS_DOUBLE:
615            {
616                char *d = NULL;
617                int len;
618                double dbl = Z_DVAL_P(val);
619
620                if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
621                    len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
622                    smart_str_appendl(buf, d, len);
623                    efree(d);
624                } else {
625                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl);
626                    smart_str_appendc(buf, '0');
627                }
628            }
629            break;
630
631        case IS_STRING:
632            json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
633            break;
634
635        case IS_OBJECT:
636            if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce TSRMLS_CC)) {
637                json_encode_serializable_object(buf, val, options TSRMLS_CC);
638                break;
639            }
640            /* fallthrough -- Non-serializable object */
641        case IS_ARRAY:
642            json_encode_array(buf, &val, options TSRMLS_CC);
643            break;
644
645        default:
646            php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null");
647            smart_str_appendl(buf, "null", 4);
648            break;
649    }
650
651    return;
652}
653/* }}} */
654
655PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */
656{
657    int utf16_len;
658    zval *z;
659    unsigned short *utf16;
660    JSON_parser jp;
661
662    utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
663
664    utf16_len = json_utf8_to_utf16(utf16, str, str_len);
665    if (utf16_len <= 0) {
666        if (utf16) {
667            efree(utf16);
668        }
669        JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
670        RETURN_NULL();
671    }
672
673    if (depth <= 0) {
674        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero");
675        efree(utf16);
676        RETURN_NULL();
677    }
678
679    ALLOC_INIT_ZVAL(z);
680    jp = new_JSON_parser(depth);
681    if (parse_JSON_ex(jp, z, utf16, utf16_len, options TSRMLS_CC)) {
682        *return_value = *z;
683    }
684    else
685    {
686        double d;
687        int type, overflow_info;
688        long p;
689        char *trim = str;
690        int trim_len = str_len;
691
692        /* Increment trimmed string pointer to strip leading whitespace */
693        /* JSON RFC says to consider as whitespace: space, tab, LF or CR */
694        while (trim_len && (*trim == ' ' || *trim == '\t' || *trim == '\n' || *trim == '\r')) {
695            trim++;
696            trim_len--;
697        }
698
699        /* Decrement trimmed string length to strip trailing whitespace */
700        while (trim_len && (trim[trim_len - 1] == ' ' || trim[trim_len - 1] == '\t' || trim[trim_len - 1] == '\n' || trim[trim_len - 1] == '\r')) {
701            trim_len--;
702        }
703
704        RETVAL_NULL();
705        if (trim_len == 4) {
706            if (!strncasecmp(trim, "null", trim_len)) {
707                /* We need to explicitly clear the error because its an actual NULL and not an error */
708                jp->error_code = PHP_JSON_ERROR_NONE;
709                RETVAL_NULL();
710            } else if (!strncasecmp(trim, "true", trim_len)) {
711                RETVAL_BOOL(1);
712            }
713        } else if (trim_len == 5 && !strncasecmp(trim, "false", trim_len)) {
714            RETVAL_BOOL(0);
715        }
716
717        if ((type = is_numeric_string_ex(trim, trim_len, &p, &d, 0, &overflow_info)) != 0) {
718            if (type == IS_LONG) {
719                RETVAL_LONG(p);
720            } else if (type == IS_DOUBLE) {
721                if (options & PHP_JSON_BIGINT_AS_STRING && overflow_info) {
722                    /* Within an object or array, a numeric literal is assumed
723                     * to be an integer if and only if it's entirely made up of
724                     * digits (exponent notation will result in the number
725                     * being treated as a double). We'll match that behaviour
726                     * here. */
727                    int i;
728                    zend_bool is_float = 0;
729
730                    for (i = (trim[0] == '-' ? 1 : 0); i < trim_len; i++) {
731                        /* Not using isdigit() because it's locale specific,
732                         * but we expect JSON input to always be UTF-8. */
733                        if (trim[i] < '0' || trim[i] > '9') {
734                            is_float = 1;
735                            break;
736                        }
737                    }
738
739                    if (is_float) {
740                        RETVAL_DOUBLE(d);
741                    } else {
742                        RETVAL_STRINGL(trim, trim_len, 1);
743                    }
744                } else {
745                    RETVAL_DOUBLE(d);
746                }
747            }
748        }
749
750        if (Z_TYPE_P(return_value) != IS_NULL) {
751            jp->error_code = PHP_JSON_ERROR_NONE;
752        }
753
754        zval_dtor(z);
755    }
756    FREE_ZVAL(z);
757    efree(utf16);
758    JSON_G(error_code) = jp->error_code;
759    free_JSON_parser(jp);
760}
761/* }}} */
762
763
764/* {{{ proto string json_encode(mixed data [, int options])
765   Returns the JSON representation of a value */
766static PHP_FUNCTION(json_encode)
767{
768    zval *parameter;
769    smart_str buf = {0};
770    long options = 0;
771
772    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &parameter, &options) == FAILURE) {
773        return;
774    }
775
776    JSON_G(error_code) = PHP_JSON_ERROR_NONE;
777
778    php_json_encode(&buf, parameter, options TSRMLS_CC);
779
780    ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
781
782    smart_str_free(&buf);
783}
784/* }}} */
785
786/* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]])
787   Decodes the JSON representation into a PHP value */
788static PHP_FUNCTION(json_decode)
789{
790    char *str;
791    int str_len;
792    zend_bool assoc = 0; /* return JS objects as PHP objects by default */
793    long depth = JSON_PARSER_DEFAULT_DEPTH;
794    long options = 0;
795
796    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) {
797        return;
798    }
799
800    JSON_G(error_code) = 0;
801
802    if (!str_len) {
803        RETURN_NULL();
804    }
805
806    /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */
807    if (assoc) {
808        options |=  PHP_JSON_OBJECT_AS_ARRAY;
809    } else {
810        options &= ~PHP_JSON_OBJECT_AS_ARRAY;
811    }
812
813    php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC);
814}
815/* }}} */
816
817/* {{{ proto int json_last_error()
818   Returns the error code of the last json_decode(). */
819static PHP_FUNCTION(json_last_error)
820{
821    if (zend_parse_parameters_none() == FAILURE) {
822        return;
823    }
824
825    RETURN_LONG(JSON_G(error_code));
826}
827/* }}} */
828
829/*
830 * Local variables:
831 * tab-width: 4
832 * c-basic-offset: 4
833 * End:
834 * vim600: noet sw=4 ts=4 fdm=marker
835 * vim<600: noet sw=4 ts=4
836 */
837