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

Generated by: LCOV version 1.10

Generated at Mon, 26 Jan 2015 14:46:46 +0000 (32 hours ago)

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