PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/json - json.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 310 334 92.8 %
Date: 2014-10-30 Functions: 16 16 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Thu, 30 Oct 2014 07:41:32 +0000 (2 days ago)

Copyright © 2005-2014 The PHP Group
All rights reserved.