1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2015 The PHP Group                                |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 3.01 of the PHP license,      |
8  | that is bundled with this package in the file LICENSE, and is        |
9  | available through the world-wide-web at the following url:           |
10  | http://www.php.net/license/3_01.txt                                  |
11  | If you did not receive a copy of the PHP license and are unable to   |
12  | obtain it through the world-wide-web, please send a note to          |
13  | license@php.net so we can mail you a copy immediately.               |
14  +----------------------------------------------------------------------+
15  | Author: Omar Kilani <omar@php.net>                                   |
16  +----------------------------------------------------------------------+
17*/
18
19/* $Id$ */
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include "php.h"
26#include "php_ini.h"
27#include "ext/standard/info.h"
28#include "ext/standard/html.h"
29#include "zend_smart_str.h"
30#include "JSON_parser.h"
31#include "php_json.h"
32#include <zend_exceptions.h>
33
34#include <float.h>
35#if defined(DBL_MANT_DIG) && defined(DBL_MIN_EXP)
36#define NUM_BUF_SIZE (3 + DBL_MANT_DIG - DBL_MIN_EXP)
37#else
38#define NUM_BUF_SIZE 1080
39#endif
40
41
42static PHP_MINFO_FUNCTION(json);
43static PHP_FUNCTION(json_encode);
44static PHP_FUNCTION(json_decode);
45static PHP_FUNCTION(json_last_error);
46static PHP_FUNCTION(json_last_error_msg);
47
48static const char digits[] = "0123456789abcdef";
49
50PHP_JSON_API zend_class_entry *php_json_serializable_ce;
51
52ZEND_DECLARE_MODULE_GLOBALS(json)
53
54/* {{{ arginfo */
55ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1)
56    ZEND_ARG_INFO(0, value)
57    ZEND_ARG_INFO(0, options)
58    ZEND_ARG_INFO(0, depth)
59ZEND_END_ARG_INFO()
60
61ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1)
62    ZEND_ARG_INFO(0, json)
63    ZEND_ARG_INFO(0, assoc)
64    ZEND_ARG_INFO(0, depth)
65    ZEND_ARG_INFO(0, options)
66ZEND_END_ARG_INFO()
67
68ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0)
69ZEND_END_ARG_INFO()
70
71ZEND_BEGIN_ARG_INFO(arginfo_json_last_error_msg, 0)
72ZEND_END_ARG_INFO()
73/* }}} */
74
75/* {{{ json_functions[] */
76static const zend_function_entry json_functions[] = {
77    PHP_FE(json_encode, arginfo_json_encode)
78    PHP_FE(json_decode, arginfo_json_decode)
79    PHP_FE(json_last_error, arginfo_json_last_error)
80    PHP_FE(json_last_error_msg, arginfo_json_last_error_msg)
81    PHP_FE_END
82};
83/* }}} */
84
85/* {{{ JsonSerializable methods */
86ZEND_BEGIN_ARG_INFO(json_serialize_arginfo, 0)
87    /* No arguments */
88ZEND_END_ARG_INFO();
89
90static const zend_function_entry json_serializable_interface[] = {
91    PHP_ABSTRACT_ME(JsonSerializable, jsonSerialize, json_serialize_arginfo)
92    PHP_FE_END
93};
94/* }}} */
95
96/* {{{ MINIT */
97static PHP_MINIT_FUNCTION(json)
98{
99    zend_class_entry ce;
100
101    INIT_CLASS_ENTRY(ce, "JsonSerializable", json_serializable_interface);
102    php_json_serializable_ce = zend_register_internal_interface(&ce);
103
104    REGISTER_LONG_CONSTANT("JSON_HEX_TAG",  PHP_JSON_HEX_TAG,  CONST_CS | CONST_PERSISTENT);
105    REGISTER_LONG_CONSTANT("JSON_HEX_AMP",  PHP_JSON_HEX_AMP,  CONST_CS | CONST_PERSISTENT);
106    REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT);
107    REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT);
108    REGISTER_LONG_CONSTANT("JSON_FORCE_OBJECT", PHP_JSON_FORCE_OBJECT, CONST_CS | CONST_PERSISTENT);
109    REGISTER_LONG_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK, CONST_CS | CONST_PERSISTENT);
110    REGISTER_LONG_CONSTANT("JSON_UNESCAPED_SLASHES", PHP_JSON_UNESCAPED_SLASHES, CONST_CS | CONST_PERSISTENT);
111    REGISTER_LONG_CONSTANT("JSON_PRETTY_PRINT", PHP_JSON_PRETTY_PRINT, CONST_CS | CONST_PERSISTENT);
112    REGISTER_LONG_CONSTANT("JSON_UNESCAPED_UNICODE", PHP_JSON_UNESCAPED_UNICODE, CONST_CS | CONST_PERSISTENT);
113    REGISTER_LONG_CONSTANT("JSON_PARTIAL_OUTPUT_ON_ERROR", PHP_JSON_PARTIAL_OUTPUT_ON_ERROR, CONST_CS | CONST_PERSISTENT);
114    REGISTER_LONG_CONSTANT("JSON_PRESERVE_ZERO_FRACTION", PHP_JSON_PRESERVE_ZERO_FRACTION, CONST_CS | CONST_PERSISTENT);
115
116    REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT);
117    REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT);
118    REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT);
119    REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT);
120    REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT);
121    REGISTER_LONG_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8, CONST_CS | CONST_PERSISTENT);
122    REGISTER_LONG_CONSTANT("JSON_ERROR_RECURSION", PHP_JSON_ERROR_RECURSION, CONST_CS | CONST_PERSISTENT);
123    REGISTER_LONG_CONSTANT("JSON_ERROR_INF_OR_NAN", PHP_JSON_ERROR_INF_OR_NAN, CONST_CS | CONST_PERSISTENT);
124    REGISTER_LONG_CONSTANT("JSON_ERROR_UNSUPPORTED_TYPE", PHP_JSON_ERROR_UNSUPPORTED_TYPE, CONST_CS | CONST_PERSISTENT);
125
126    REGISTER_LONG_CONSTANT("JSON_OBJECT_AS_ARRAY",      PHP_JSON_OBJECT_AS_ARRAY,       CONST_CS | CONST_PERSISTENT);
127    REGISTER_LONG_CONSTANT("JSON_BIGINT_AS_STRING",     PHP_JSON_BIGINT_AS_STRING,      CONST_CS | CONST_PERSISTENT);
128
129    return SUCCESS;
130}
131/* }}} */
132
133/* {{{ PHP_GINIT_FUNCTION
134*/
135static PHP_GINIT_FUNCTION(json)
136{
137#if defined(COMPILE_DL_JSON) && defined(ZTS)
138    ZEND_TSRMLS_CACHE_UPDATE;
139#endif
140    json_globals->encoder_depth = 0;
141    json_globals->error_code = 0;
142    json_globals->encode_max_depth = 0;
143}
144/* }}} */
145
146
147/* {{{ json_module_entry
148 */
149zend_module_entry json_module_entry = {
150    STANDARD_MODULE_HEADER,
151    "json",
152    json_functions,
153    PHP_MINIT(json),
154    NULL,
155    NULL,
156    NULL,
157    PHP_MINFO(json),
158    PHP_JSON_VERSION,
159    PHP_MODULE_GLOBALS(json),
160    PHP_GINIT(json),
161    NULL,
162    NULL,
163    STANDARD_MODULE_PROPERTIES_EX
164};
165/* }}} */
166
167#ifdef COMPILE_DL_JSON
168#ifdef ZTS
169ZEND_TSRMLS_CACHE_DEFINE;
170#endif
171ZEND_GET_MODULE(json)
172#endif
173
174/* {{{ PHP_MINFO_FUNCTION
175 */
176static PHP_MINFO_FUNCTION(json)
177{
178    php_info_print_table_start();
179    php_info_print_table_row(2, "json support", "enabled");
180    php_info_print_table_row(2, "json version", PHP_JSON_VERSION);
181    php_info_print_table_end();
182}
183/* }}} */
184
185static void json_escape_string(smart_str *buf, char *s, size_t len, int options);
186
187static int json_determine_array_type(zval *val) /* {{{ */
188{
189    int i;
190    HashTable *myht = HASH_OF(val);
191
192    i = myht ? zend_hash_num_elements(myht) : 0;
193    if (i > 0) {
194        zend_string *key;
195        zend_ulong index, idx;
196
197        idx = 0;
198        ZEND_HASH_FOREACH_KEY(myht, index, key) {
199            if (key) {
200                return PHP_JSON_OUTPUT_OBJECT;
201            } else {
202                if (index != idx) {
203                    return PHP_JSON_OUTPUT_OBJECT;
204                }
205            }
206            idx++;
207        } ZEND_HASH_FOREACH_END();
208    }
209
210    return PHP_JSON_OUTPUT_ARRAY;
211}
212/* }}} */
213
214/* {{{ Pretty printing support functions */
215
216static inline void json_pretty_print_char(smart_str *buf, int options, char c) /* {{{ */
217{
218    if (options & PHP_JSON_PRETTY_PRINT) {
219        smart_str_appendc(buf, c);
220    }
221}
222/* }}} */
223
224static inline void json_pretty_print_indent(smart_str *buf, int options) /* {{{ */
225{
226    int i;
227
228    if (options & PHP_JSON_PRETTY_PRINT) {
229        for (i = 0; i < JSON_G(encoder_depth); ++i) {
230            smart_str_appendl(buf, "    ", 4);
231        }
232    }
233}
234/* }}} */
235
236/* }}} */
237
238static void json_encode_array(smart_str *buf, zval *val, int options) /* {{{ */
239{
240    int i, r, need_comma = 0;
241    HashTable *myht;
242
243    if (Z_TYPE_P(val) == IS_ARRAY) {
244        myht = HASH_OF(val);
245        r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val);
246    } else {
247        myht = Z_OBJPROP_P(val);
248        r = PHP_JSON_OUTPUT_OBJECT;
249    }
250
251    if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
252        JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
253        smart_str_appendl(buf, "null", 4);
254        return;
255    }
256
257    if (r == PHP_JSON_OUTPUT_ARRAY) {
258        smart_str_appendc(buf, '[');
259    } else {
260        smart_str_appendc(buf, '{');
261    }
262
263    ++JSON_G(encoder_depth);
264
265    i = myht ? zend_hash_num_elements(myht) : 0;
266
267    if (i > 0) {
268        zend_string *key;
269        zval *data;
270        zend_ulong index;
271        HashTable *tmp_ht;
272
273        ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
274            ZVAL_DEREF(data);
275            tmp_ht = HASH_OF(data);
276            if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
277                ZEND_HASH_INC_APPLY_COUNT(tmp_ht);
278            }
279
280            if (r == PHP_JSON_OUTPUT_ARRAY) {
281                if (need_comma) {
282                    smart_str_appendc(buf, ',');
283                } else {
284                    need_comma = 1;
285                }
286
287                json_pretty_print_char(buf, options, '\n');
288                json_pretty_print_indent(buf, options);
289                php_json_encode(buf, data, options);
290            } else if (r == PHP_JSON_OUTPUT_OBJECT) {
291                if (key) {
292                    if (key->val[0] == '\0' && Z_TYPE_P(val) == IS_OBJECT) {
293                        /* Skip protected and private members. */
294                        if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
295                            ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
296                        }
297                        continue;
298                    }
299
300                    if (need_comma) {
301                        smart_str_appendc(buf, ',');
302                    } else {
303                        need_comma = 1;
304                    }
305
306                    json_pretty_print_char(buf, options, '\n');
307                    json_pretty_print_indent(buf, options);
308
309                    json_escape_string(buf, key->val, key->len, options & ~PHP_JSON_NUMERIC_CHECK);
310                    smart_str_appendc(buf, ':');
311
312                    json_pretty_print_char(buf, options, ' ');
313
314                    php_json_encode(buf, data, options);
315                } else {
316                    if (need_comma) {
317                        smart_str_appendc(buf, ',');
318                    } else {
319                        need_comma = 1;
320                    }
321
322                    json_pretty_print_char(buf, options, '\n');
323                    json_pretty_print_indent(buf, options);
324
325                    smart_str_appendc(buf, '"');
326                    smart_str_append_long(buf, (zend_long) index);
327                    smart_str_appendc(buf, '"');
328                    smart_str_appendc(buf, ':');
329
330                    json_pretty_print_char(buf, options, ' ');
331
332                    php_json_encode(buf, data, options);
333                }
334            }
335
336            if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
337                ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
338            }
339        } ZEND_HASH_FOREACH_END();
340    }
341
342    if (JSON_G(encoder_depth) > JSON_G(encode_max_depth)) {
343        JSON_G(error_code) = PHP_JSON_ERROR_DEPTH;
344    }
345    --JSON_G(encoder_depth);
346
347    /* Only keep closing bracket on same line for empty arrays/objects */
348    if (need_comma) {
349        json_pretty_print_char(buf, options, '\n');
350        json_pretty_print_indent(buf, options);
351    }
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 length */
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
398static void json_escape_string(smart_str *buf, char *s, size_t len, int options) /* {{{ */
399{
400    int status;
401    unsigned int us, next_us = 0;
402    size_t pos, checkpoint;
403
404    if (len == 0) {
405        smart_str_appendl(buf, "\"\"", 2);
406        return;
407    }
408
409    if (options & PHP_JSON_NUMERIC_CHECK) {
410        double d;
411        int type;
412        zend_long p;
413
414        if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
415            if (type == IS_LONG) {
416                smart_str_append_long(buf, p);
417            } else if (type == IS_DOUBLE) {
418                if (!zend_isinf(d) && !zend_isnan(d)) {
419                    char num[NUM_BUF_SIZE];
420                    int l;
421
422                    php_gcvt(d, EG(precision), '.', 'e', (char *)num);
423                    l = strlen(num);
424                    if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && l < NUM_BUF_SIZE - 2) {
425                        num[l++] = '.';
426                        num[l++] = '0';
427                        num[l] = '\0';
428                    }
429                    smart_str_appendl(buf, num, l);
430                } else {
431                    JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN;
432                    smart_str_appendc(buf, '0');
433                }
434            }
435            return;
436        }
437
438    }
439
440    if (options & PHP_JSON_UNESCAPED_UNICODE) {
441        /* validate UTF-8 string first */
442        if (json_utf8_to_utf16(NULL, s, len) < 0) {
443            JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
444            smart_str_appendl(buf, "null", 4);
445            return;
446        }
447    }
448
449    pos = 0;
450    checkpoint = buf->s ? buf->s->len : 0;
451
452    /* pre-allocate for string length plus 2 quotes */
453    smart_str_alloc(buf, len+2, 0);
454    smart_str_appendc(buf, '"');
455
456    do {
457        if (UNEXPECTED(next_us)) {
458            us = next_us;
459            next_us = 0;
460        } else {
461            us = (unsigned char)s[pos];
462            if (!(options & PHP_JSON_UNESCAPED_UNICODE) && us >= 0x80) {
463                /* UTF-8 character */
464                us = php_next_utf8_char((const unsigned char *)s, len, &pos, &status);
465                if (status != SUCCESS) {
466                    if (buf->s) {
467                        buf->s->len = checkpoint;
468                    }
469                    JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
470                    smart_str_appendl(buf, "null", 4);
471                    return;
472                }
473                /* From http://en.wikipedia.org/wiki/UTF16 */
474                if (us >= 0x10000) {
475                    us -= 0x10000;
476                    next_us = (unsigned short)((us & 0x3ff) | 0xdc00);
477                    us = (unsigned short)((us >> 10) | 0xd800);
478                }
479            } else {
480                pos++;
481            }
482        }
483
484        switch (us) {
485            case '"':
486                if (options & PHP_JSON_HEX_QUOT) {
487                    smart_str_appendl(buf, "\\u0022", 6);
488                } else {
489                    smart_str_appendl(buf, "\\\"", 2);
490                }
491                break;
492
493            case '\\':
494                smart_str_appendl(buf, "\\\\", 2);
495                break;
496
497            case '/':
498                if (options & PHP_JSON_UNESCAPED_SLASHES) {
499                    smart_str_appendc(buf, '/');
500                } else {
501                    smart_str_appendl(buf, "\\/", 2);
502                }
503                break;
504
505            case '\b':
506                smart_str_appendl(buf, "\\b", 2);
507                break;
508
509            case '\f':
510                smart_str_appendl(buf, "\\f", 2);
511                break;
512
513            case '\n':
514                smart_str_appendl(buf, "\\n", 2);
515                break;
516
517            case '\r':
518                smart_str_appendl(buf, "\\r", 2);
519                break;
520
521            case '\t':
522                smart_str_appendl(buf, "\\t", 2);
523                break;
524
525            case '<':
526                if (options & PHP_JSON_HEX_TAG) {
527                    smart_str_appendl(buf, "\\u003C", 6);
528                } else {
529                    smart_str_appendc(buf, '<');
530                }
531                break;
532
533            case '>':
534                if (options & PHP_JSON_HEX_TAG) {
535                    smart_str_appendl(buf, "\\u003E", 6);
536                } else {
537                    smart_str_appendc(buf, '>');
538                }
539                break;
540
541            case '&':
542                if (options & PHP_JSON_HEX_AMP) {
543                    smart_str_appendl(buf, "\\u0026", 6);
544                } else {
545                    smart_str_appendc(buf, '&');
546                }
547                break;
548
549            case '\'':
550                if (options & PHP_JSON_HEX_APOS) {
551                    smart_str_appendl(buf, "\\u0027", 6);
552                } else {
553                    smart_str_appendc(buf, '\'');
554                }
555                break;
556
557            default:
558                if (us >= ' ' && ((options & PHP_JSON_UNESCAPED_UNICODE) || (us & 127) == us)) {
559                    smart_str_appendc(buf, (unsigned char) us);
560                } else {
561                    smart_str_appendl(buf, "\\u", 2);
562                    smart_str_appendc(buf, digits[(us & 0xf000) >> 12]);
563                    smart_str_appendc(buf, digits[(us & 0xf00)  >> 8]);
564                    smart_str_appendc(buf, digits[(us & 0xf0)   >> 4]);
565                    smart_str_appendc(buf, digits[(us & 0xf)]);
566                }
567                break;
568        }
569    } while (pos < len || next_us);
570
571    smart_str_appendc(buf, '"');
572}
573/* }}} */
574
575static void json_encode_serializable_object(smart_str *buf, zval *val, int options) /* {{{ */
576{
577    zend_class_entry *ce = Z_OBJCE_P(val);
578    zval retval, fname;
579    HashTable* myht;
580
581    if (Z_TYPE_P(val) == IS_ARRAY) {
582        myht = HASH_OF(val);
583    } else {
584        myht = Z_OBJPROP_P(val);
585    }
586
587    if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
588        JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
589        smart_str_appendl(buf, "null", 4);
590        return;
591    }
592
593    ZVAL_STRING(&fname, "jsonSerialize");
594
595    if (FAILURE == call_user_function_ex(EG(function_table), val, &fname, &retval, 0, NULL, 1, NULL) || Z_TYPE(retval) == IS_UNDEF) {
596        zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ce->name->val);
597        smart_str_appendl(buf, "null", sizeof("null") - 1);
598        zval_ptr_dtor(&fname);
599        return;
600    }
601
602    if (EG(exception)) {
603        /* Error already raised */
604        zval_ptr_dtor(&retval);
605        zval_ptr_dtor(&fname);
606        smart_str_appendl(buf, "null", sizeof("null") - 1);
607        return;
608    }
609
610    if ((Z_TYPE(retval) == IS_OBJECT) &&
611        (Z_OBJ_HANDLE(retval) == Z_OBJ_HANDLE_P(val))) {
612        /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
613        json_encode_array(buf, &retval, options);
614    } else {
615        /* All other types, encode as normal */
616        php_json_encode(buf, &retval, options);
617    }
618
619    zval_ptr_dtor(&retval);
620    zval_ptr_dtor(&fname);
621}
622/* }}} */
623
624PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */
625{
626again:
627    switch (Z_TYPE_P(val))
628    {
629        case IS_NULL:
630            smart_str_appendl(buf, "null", 4);
631            break;
632
633        case IS_TRUE:
634            smart_str_appendl(buf, "true", 4);
635            break;
636        case IS_FALSE:
637            smart_str_appendl(buf, "false", 5);
638            break;
639
640        case IS_LONG:
641            smart_str_append_long(buf, Z_LVAL_P(val));
642            break;
643
644        case IS_DOUBLE:
645            {
646                char num[NUM_BUF_SIZE];
647                int len;
648                double dbl = Z_DVAL_P(val);
649
650                if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
651                    php_gcvt(dbl, EG(precision), '.', 'e', (char *)num);
652                    len = strlen(num);
653                    if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < NUM_BUF_SIZE - 2) {
654                        num[len++] = '.';
655                        num[len++] = '0';
656                        num[len] = '\0';
657                    }
658                    smart_str_appendl(buf, num, len);
659                } else {
660                    JSON_G(error_code) = PHP_JSON_ERROR_INF_OR_NAN;
661                    smart_str_appendc(buf, '0');
662                }
663            }
664            break;
665
666        case IS_STRING:
667            json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options);
668            break;
669
670        case IS_OBJECT:
671            if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
672                json_encode_serializable_object(buf, val, options);
673                break;
674            }
675            /* fallthrough -- Non-serializable object */
676        case IS_ARRAY:
677            json_encode_array(buf, val, options);
678            break;
679
680        case IS_REFERENCE:
681            val = Z_REFVAL_P(val);
682            goto again;
683
684        default:
685            JSON_G(error_code) = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
686            smart_str_appendl(buf, "null", 4);
687            break;
688    }
689
690    return;
691}
692/* }}} */
693
694PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */
695{
696    size_t utf16_len;
697    unsigned short *utf16;
698    JSON_parser jp;
699
700    utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
701
702    utf16_len = json_utf8_to_utf16(utf16, str, str_len);
703    if (utf16_len <= 0) {
704        if (utf16) {
705            efree(utf16);
706        }
707        JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
708        RETURN_NULL();
709    }
710
711    if (depth <= 0) {
712        php_error_docref(NULL, E_WARNING, "Depth must be greater than zero");
713        efree(utf16);
714        RETURN_NULL();
715    }
716
717    jp = new_JSON_parser(depth);
718    if (!parse_JSON_ex(jp, return_value, utf16, utf16_len, options)) {
719        double d;
720        int type, overflow_info;
721        zend_long p;
722        char *trim = str;
723        int trim_len = str_len;
724
725        zval_dtor(return_value);
726
727        /* Increment trimmed string pointer to strip leading whitespace */
728        /* JSON RFC says to consider as whitespace: space, tab, LF or CR */
729        while (trim_len && (*trim == ' ' || *trim == '\t' || *trim == '\n' || *trim == '\r')) {
730            trim++;
731            trim_len--;
732        }
733
734        /* Decrement trimmed string length to strip trailing whitespace */
735        while (trim_len && (trim[trim_len - 1] == ' ' || trim[trim_len - 1] == '\t' || trim[trim_len - 1] == '\n' || trim[trim_len - 1] == '\r')) {
736            trim_len--;
737        }
738
739        RETVAL_NULL();
740        if (trim_len == 4) {
741            if (!strncmp(trim, "null", trim_len)) {
742                /* We need to explicitly clear the error because its an actual NULL and not an error */
743                jp->error_code = PHP_JSON_ERROR_NONE;
744                RETVAL_NULL();
745            } else if (!strncmp(trim, "true", trim_len)) {
746                RETVAL_BOOL(1);
747            }
748        } else if (trim_len == 5 && !strncmp(trim, "false", trim_len)) {
749            RETVAL_BOOL(0);
750        }
751
752        if ((type = is_numeric_string_ex(trim, trim_len, &p, &d, 0, &overflow_info)) != 0) {
753            if (type == IS_LONG) {
754                RETVAL_LONG(p);
755            } else if (type == IS_DOUBLE) {
756                if (options & PHP_JSON_BIGINT_AS_STRING && overflow_info) {
757                    /* Within an object or array, a numeric literal is assumed
758                     * to be an integer if and only if it's entirely made up of
759                     * digits (exponent notation will result in the number
760                     * being treated as a double). We'll match that behaviour
761                     * here. */
762                    int i;
763                    zend_bool is_float = 0;
764
765                    for (i = (trim[0] == '-' ? 1 : 0); i < trim_len; i++) {
766                        /* Not using isdigit() because it's locale specific,
767                         * but we expect JSON input to always be UTF-8. */
768                        if (trim[i] < '0' || trim[i] > '9') {
769                            is_float = 1;
770                            break;
771                        }
772                    }
773
774                    if (is_float) {
775                        RETVAL_DOUBLE(d);
776                    } else {
777                        RETVAL_STRINGL(trim, trim_len);
778                    }
779                } else {
780                    RETVAL_DOUBLE(d);
781                }
782            }
783        }
784
785        if (Z_TYPE_P(return_value) != IS_NULL) {
786            jp->error_code = PHP_JSON_ERROR_NONE;
787        }
788    }
789    efree(utf16);
790    JSON_G(error_code) = jp->error_code;
791    free_JSON_parser(jp);
792}
793/* }}} */
794
795/* {{{ proto string json_encode(mixed data [, int options[, int depth]])
796   Returns the JSON representation of a value */
797static PHP_FUNCTION(json_encode)
798{
799    zval *parameter;
800    smart_str buf = {0};
801    zend_long options = 0;
802    zend_long depth = JSON_PARSER_DEFAULT_DEPTH;
803
804    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|ll", &parameter, &options, &depth) == FAILURE) {
805        return;
806    }
807
808    JSON_G(error_code) = PHP_JSON_ERROR_NONE;
809
810    JSON_G(encode_max_depth) = depth;
811
812    php_json_encode(&buf, parameter, options);
813
814    if (JSON_G(error_code) != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
815        smart_str_free(&buf);
816        ZVAL_FALSE(return_value);
817    } else {
818        smart_str_0(&buf); /* copy? */
819        ZVAL_NEW_STR(return_value, buf.s);
820    }
821}
822/* }}} */
823
824/* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]])
825   Decodes the JSON representation into a PHP value */
826static PHP_FUNCTION(json_decode)
827{
828    char *str;
829    size_t str_len;
830    zend_bool assoc = 0; /* return JS objects as PHP objects by default */
831    zend_long depth = JSON_PARSER_DEFAULT_DEPTH;
832    zend_long options = 0;
833
834    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) {
835        return;
836    }
837
838    JSON_G(error_code) = 0;
839
840    if (!str_len) {
841        RETURN_NULL();
842    }
843
844    /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */
845    if (assoc) {
846        options |=  PHP_JSON_OBJECT_AS_ARRAY;
847    } else {
848        options &= ~PHP_JSON_OBJECT_AS_ARRAY;
849    }
850
851    php_json_decode_ex(return_value, str, str_len, options, depth);
852}
853/* }}} */
854
855/* {{{ proto int json_last_error()
856   Returns the error code of the last json_encode() or json_decode() call. */
857static PHP_FUNCTION(json_last_error)
858{
859    if (zend_parse_parameters_none() == FAILURE) {
860        return;
861    }
862
863    RETURN_LONG(JSON_G(error_code));
864}
865/* }}} */
866
867/* {{{ proto string json_last_error_msg()
868   Returns the error string of the last json_encode() or json_decode() call. */
869static PHP_FUNCTION(json_last_error_msg)
870{
871    if (zend_parse_parameters_none() == FAILURE) {
872        return;
873    }
874
875    switch(JSON_G(error_code)) {
876        case PHP_JSON_ERROR_NONE:
877            RETURN_STRING("No error");
878        case PHP_JSON_ERROR_DEPTH:
879            RETURN_STRING("Maximum stack depth exceeded");
880        case PHP_JSON_ERROR_STATE_MISMATCH:
881            RETURN_STRING("State mismatch (invalid or malformed JSON)");
882        case PHP_JSON_ERROR_CTRL_CHAR:
883            RETURN_STRING("Control character error, possibly incorrectly encoded");
884        case PHP_JSON_ERROR_SYNTAX:
885            RETURN_STRING("Syntax error");
886        case PHP_JSON_ERROR_UTF8:
887            RETURN_STRING("Malformed UTF-8 characters, possibly incorrectly encoded");
888        case PHP_JSON_ERROR_RECURSION:
889            RETURN_STRING("Recursion detected");
890        case PHP_JSON_ERROR_INF_OR_NAN:
891            RETURN_STRING("Inf and NaN cannot be JSON encoded");
892        case PHP_JSON_ERROR_UNSUPPORTED_TYPE:
893            RETURN_STRING("Type is not supported");
894        default:
895            RETURN_STRING("Unknown error");
896    }
897
898}
899/* }}} */
900
901/*
902 * Local variables:
903 * tab-width: 4
904 * c-basic-offset: 4
905 * End:
906 * vim600: noet sw=4 ts=4 fdm=marker
907 * vim<600: noet sw=4 ts=4
908 */
909