Back to index

php5  5.3.10
json.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 1997-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Author: Omar Kilani <omar@php.net>                                   |
00016   +----------------------------------------------------------------------+
00017 */
00018 
00019 /* $Id: json.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024 
00025 #include "php.h"
00026 #include "php_ini.h"
00027 #include "ext/standard/info.h"
00028 #include "ext/standard/php_smart_str.h"
00029 #include "utf8_to_utf16.h"
00030 #include "JSON_parser.h"
00031 #include "php_json.h"
00032 
00033 static PHP_MINFO_FUNCTION(json);
00034 static PHP_FUNCTION(json_encode);
00035 static PHP_FUNCTION(json_decode);
00036 static PHP_FUNCTION(json_last_error);
00037 
00038 static const char digits[] = "0123456789abcdef";
00039 
00040 ZEND_DECLARE_MODULE_GLOBALS(json)
00041 
00042 /* {{{ arginfo */
00043 ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1)
00044        ZEND_ARG_INFO(0, value)
00045        ZEND_ARG_INFO(0, options)
00046 ZEND_END_ARG_INFO()
00047 
00048 ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1)
00049        ZEND_ARG_INFO(0, json)
00050        ZEND_ARG_INFO(0, assoc)
00051        ZEND_ARG_INFO(0, depth)
00052 ZEND_END_ARG_INFO()
00053 
00054 ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0)
00055 ZEND_END_ARG_INFO()
00056 /* }}} */
00057 
00058 /* {{{ json_functions[] */
00059 static const function_entry json_functions[] = {
00060        PHP_FE(json_encode, arginfo_json_encode)
00061        PHP_FE(json_decode, arginfo_json_decode)
00062        PHP_FE(json_last_error, arginfo_json_last_error)
00063        PHP_FE_END
00064 };
00065 /* }}} */
00066 
00067 /* {{{ MINIT */
00068 static PHP_MINIT_FUNCTION(json)
00069 {
00070        REGISTER_LONG_CONSTANT("JSON_HEX_TAG",  PHP_JSON_HEX_TAG,  CONST_CS | CONST_PERSISTENT);
00071        REGISTER_LONG_CONSTANT("JSON_HEX_AMP",  PHP_JSON_HEX_AMP,  CONST_CS | CONST_PERSISTENT);
00072        REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT);
00073        REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT);
00074        REGISTER_LONG_CONSTANT("JSON_FORCE_OBJECT", PHP_JSON_FORCE_OBJECT, CONST_CS | CONST_PERSISTENT);
00075        REGISTER_LONG_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK, CONST_CS | CONST_PERSISTENT);
00076 
00077        REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT);
00078        REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT);
00079        REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT);
00080        REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT);
00081        REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT);
00082        REGISTER_LONG_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8, CONST_CS | CONST_PERSISTENT);
00083 
00084        return SUCCESS;
00085 }
00086 /* }}} */
00087 
00088 /* {{{ PHP_GINIT_FUNCTION
00089 */
00090 static PHP_GINIT_FUNCTION(json)
00091 {
00092        json_globals->error_code = 0;
00093 }
00094 /* }}} */
00095 
00096 
00097 /* {{{ json_module_entry
00098  */
00099 zend_module_entry json_module_entry = {
00100        STANDARD_MODULE_HEADER,
00101        "json",
00102        json_functions,
00103        PHP_MINIT(json),
00104        NULL,
00105        NULL,
00106        NULL,
00107        PHP_MINFO(json),
00108        PHP_JSON_VERSION,
00109        PHP_MODULE_GLOBALS(json),
00110        PHP_GINIT(json),
00111        NULL,
00112        NULL,
00113        STANDARD_MODULE_PROPERTIES_EX
00114 };
00115 /* }}} */
00116 
00117 #ifdef COMPILE_DL_JSON
00118 ZEND_GET_MODULE(json)
00119 #endif
00120 
00121 /* {{{ PHP_MINFO_FUNCTION
00122  */
00123 static PHP_MINFO_FUNCTION(json)
00124 {
00125        php_info_print_table_start();
00126        php_info_print_table_row(2, "json support", "enabled");
00127        php_info_print_table_row(2, "json version", PHP_JSON_VERSION);
00128        php_info_print_table_end();
00129 }
00130 /* }}} */
00131 
00132 static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC);
00133 
00134 static int json_determine_array_type(zval **val TSRMLS_DC) /* {{{ */
00135 {
00136        int i;
00137        HashTable *myht = HASH_OF(*val);
00138 
00139        i = myht ? zend_hash_num_elements(myht) : 0;
00140        if (i > 0) {
00141               char *key;
00142               ulong index, idx;
00143               uint key_len;
00144               HashPosition pos;
00145 
00146               zend_hash_internal_pointer_reset_ex(myht, &pos);
00147               idx = 0;
00148               for (;; zend_hash_move_forward_ex(myht, &pos)) {
00149                      i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
00150                      if (i == HASH_KEY_NON_EXISTANT)
00151                             break;
00152 
00153                      if (i == HASH_KEY_IS_STRING) {
00154                             return 1;
00155                      } else {
00156                             if (index != idx) {
00157                                    return 1;
00158                             }
00159                      }
00160                      idx++;
00161               }
00162        }
00163 
00164        return PHP_JSON_OUTPUT_ARRAY;
00165 }
00166 /* }}} */
00167 
00168 static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */
00169 {
00170        int i, r;
00171        HashTable *myht;
00172 
00173        if (Z_TYPE_PP(val) == IS_ARRAY) {
00174               myht = HASH_OF(*val);
00175               r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC);
00176        } else {
00177               myht = Z_OBJPROP_PP(val);
00178               r = PHP_JSON_OUTPUT_OBJECT;
00179        }
00180 
00181        if (myht && myht->nApplyCount > 1) {
00182               php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
00183               smart_str_appendl(buf, "null", 4);
00184               return;
00185        }
00186 
00187        if (r == PHP_JSON_OUTPUT_ARRAY) {
00188               smart_str_appendc(buf, '[');
00189        } else {
00190               smart_str_appendc(buf, '{');
00191        }
00192 
00193        i = myht ? zend_hash_num_elements(myht) : 0;
00194 
00195        if (i > 0)
00196        {
00197               char *key;
00198               zval **data;
00199               ulong index;
00200               uint key_len;
00201               HashPosition pos;
00202               HashTable *tmp_ht;
00203               int need_comma = 0;
00204 
00205               zend_hash_internal_pointer_reset_ex(myht, &pos);
00206               for (;; zend_hash_move_forward_ex(myht, &pos)) {
00207                      i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
00208                      if (i == HASH_KEY_NON_EXISTANT)
00209                             break;
00210 
00211                      if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) {
00212                             tmp_ht = HASH_OF(*data);
00213                             if (tmp_ht) {
00214                                    tmp_ht->nApplyCount++;
00215                             }
00216 
00217                             if (r == PHP_JSON_OUTPUT_ARRAY) {
00218                                    if (need_comma) {
00219                                           smart_str_appendc(buf, ',');
00220                                    } else {
00221                                           need_comma = 1;
00222                                    }
00223  
00224                                    php_json_encode(buf, *data, options TSRMLS_CC);
00225                             } else if (r == PHP_JSON_OUTPUT_OBJECT) {
00226                                    if (i == HASH_KEY_IS_STRING) {
00227                                           if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) {
00228                                                  /* Skip protected and private members. */
00229                                                  if (tmp_ht) {
00230                                                         tmp_ht->nApplyCount--;
00231                                                  }
00232                                                  continue;
00233                                           }
00234 
00235                                           if (need_comma) {
00236                                                  smart_str_appendc(buf, ',');
00237                                           } else {
00238                                                  need_comma = 1;
00239                                           }
00240 
00241                                           json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
00242                                           smart_str_appendc(buf, ':');
00243 
00244                                           php_json_encode(buf, *data, options TSRMLS_CC);
00245                                    } else {
00246                                           if (need_comma) {
00247                                                  smart_str_appendc(buf, ',');
00248                                           } else {
00249                                                  need_comma = 1;
00250                                           }
00251 
00252                                           smart_str_appendc(buf, '"');
00253                                           smart_str_append_long(buf, (long) index);
00254                                           smart_str_appendc(buf, '"');
00255                                           smart_str_appendc(buf, ':');
00256 
00257                                           php_json_encode(buf, *data, options TSRMLS_CC);
00258                                    }
00259                             }
00260 
00261                             if (tmp_ht) {
00262                                    tmp_ht->nApplyCount--;
00263                             }
00264                      }
00265               }
00266        }
00267 
00268        if (r == PHP_JSON_OUTPUT_ARRAY) {
00269               smart_str_appendc(buf, ']');
00270        } else {
00271               smart_str_appendc(buf, '}');
00272        }
00273 }
00274 /* }}} */
00275 
00276 #define REVERSE16(us) (((us & 0xf) << 12) | (((us >> 4) & 0xf) << 8) | (((us >> 8) & 0xf) << 4) | ((us >> 12) & 0xf))
00277 
00278 static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
00279 {
00280        int pos = 0;
00281        unsigned short us;
00282        unsigned short *utf16;
00283 
00284        if (len == 0) {
00285               smart_str_appendl(buf, "\"\"", 2);
00286               return;
00287        }
00288 
00289        if (options & PHP_JSON_NUMERIC_CHECK) {
00290               double d;
00291               int type;
00292               long p;
00293 
00294               if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
00295                      if (type == IS_LONG) {
00296                             smart_str_append_long(buf, p);
00297                      } else if (type == IS_DOUBLE) {
00298                             if (!zend_isinf(d) && !zend_isnan(d)) {
00299                                    char *tmp;
00300                                    int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d);
00301                                    smart_str_appendl(buf, tmp, l);
00302                                    efree(tmp);
00303                             } else {
00304                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d);
00305                                    smart_str_appendc(buf, '0');
00306                             }
00307                      }
00308                      return;
00309               }
00310               
00311        }
00312 
00313        utf16 = (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0);
00314 
00315        len = utf8_to_utf16(utf16, s, len);
00316        if (len <= 0) {
00317               if (utf16) {
00318                      efree(utf16);
00319               }
00320               if (len < 0) {
00321                      JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
00322                      if (!PG(display_errors)) {
00323                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument");
00324                      }
00325                      smart_str_appendl(buf, "null", 4);
00326               } else {
00327                      smart_str_appendl(buf, "\"\"", 2);
00328               }
00329               return;
00330        }
00331 
00332        smart_str_appendc(buf, '"');
00333 
00334        while (pos < len)
00335        {
00336               us = utf16[pos++];
00337 
00338               switch (us)
00339               {
00340                      case '"':
00341                             if (options & PHP_JSON_HEX_QUOT) {
00342                                    smart_str_appendl(buf, "\\u0022", 6);
00343                             } else {
00344                                    smart_str_appendl(buf, "\\\"", 2);
00345                             }
00346                             break;
00347 
00348                      case '\\':
00349                             smart_str_appendl(buf, "\\\\", 2);
00350                             break;
00351 
00352                      case '/':
00353                             smart_str_appendl(buf, "\\/", 2);
00354                             break;
00355 
00356                      case '\b':
00357                             smart_str_appendl(buf, "\\b", 2);
00358                             break;
00359 
00360                      case '\f':
00361                             smart_str_appendl(buf, "\\f", 2);
00362                             break;
00363 
00364                      case '\n':
00365                             smart_str_appendl(buf, "\\n", 2);
00366                             break;
00367 
00368                      case '\r':
00369                             smart_str_appendl(buf, "\\r", 2);
00370                             break;
00371 
00372                      case '\t':
00373                             smart_str_appendl(buf, "\\t", 2);
00374                             break;
00375 
00376                      case '<':
00377                             if (options & PHP_JSON_HEX_TAG) {
00378                                    smart_str_appendl(buf, "\\u003C", 6);
00379                             } else {
00380                                    smart_str_appendc(buf, '<');
00381                             }
00382                             break;
00383 
00384                      case '>':
00385                             if (options & PHP_JSON_HEX_TAG) {
00386                                    smart_str_appendl(buf, "\\u003E", 6);
00387                             } else {
00388                                    smart_str_appendc(buf, '>');
00389                             }
00390                             break;
00391 
00392                      case '&':
00393                             if (options & PHP_JSON_HEX_AMP) {
00394                                    smart_str_appendl(buf, "\\u0026", 6);
00395                             } else {
00396                                    smart_str_appendc(buf, '&');
00397                             }
00398                             break;
00399 
00400                      case '\'':
00401                             if (options & PHP_JSON_HEX_APOS) {
00402                                    smart_str_appendl(buf, "\\u0027", 6);
00403                             } else {
00404                                    smart_str_appendc(buf, '\'');
00405                             }
00406                             break;
00407 
00408                      default:
00409                             if (us >= ' ' && (us & 127) == us) {
00410                                    smart_str_appendc(buf, (unsigned char) us);
00411                             } else {
00412                                    smart_str_appendl(buf, "\\u", 2);
00413                                    us = REVERSE16(us);
00414 
00415                                    smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
00416                                    us >>= 4;
00417                                    smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
00418                                    us >>= 4;
00419                                    smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
00420                                    us >>= 4;
00421                                    smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]);
00422                             }
00423                             break;
00424               }
00425        }
00426 
00427        smart_str_appendc(buf, '"');
00428        efree(utf16);
00429 }
00430 /* }}} */
00431 
00432 PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */
00433 {
00434        switch (Z_TYPE_P(val))
00435        {
00436               case IS_NULL:
00437                      smart_str_appendl(buf, "null", 4);
00438                      break;
00439 
00440               case IS_BOOL:
00441                      if (Z_BVAL_P(val)) {
00442                             smart_str_appendl(buf, "true", 4);
00443                      } else {
00444                             smart_str_appendl(buf, "false", 5);
00445                      }
00446                      break;
00447 
00448               case IS_LONG:
00449                      smart_str_append_long(buf, Z_LVAL_P(val));
00450                      break;
00451 
00452               case IS_DOUBLE:
00453                      {
00454                             char *d = NULL;
00455                             int len;
00456                             double dbl = Z_DVAL_P(val);
00457 
00458                             if (!zend_isinf(dbl) && !zend_isnan(dbl)) {
00459                                    len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl);
00460                                    smart_str_appendl(buf, d, len);
00461                                    efree(d);
00462                             } else {
00463                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl);
00464                                    smart_str_appendc(buf, '0');
00465                             }
00466                      }
00467                      break;
00468 
00469               case IS_STRING:
00470                      json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC);
00471                      break;
00472 
00473               case IS_ARRAY:
00474               case IS_OBJECT:
00475                      json_encode_array(buf, &val, options TSRMLS_CC);
00476                      break;
00477 
00478               default:
00479                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null");
00480                      smart_str_appendl(buf, "null", 4);
00481                      break;
00482        }
00483 
00484        return;
00485 }
00486 /* }}} */
00487 
00488 PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) /* {{{ */
00489 {
00490        int utf16_len;
00491        zval *z;
00492        unsigned short *utf16;
00493        JSON_parser jp;
00494 
00495        utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
00496 
00497        utf16_len = utf8_to_utf16(utf16, str, str_len);
00498        if (utf16_len <= 0) {
00499               if (utf16) {
00500                      efree(utf16);
00501               }
00502               JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
00503               RETURN_NULL();
00504        }
00505 
00506        if (depth <= 0) {
00507               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero");
00508               efree(utf16);
00509               RETURN_NULL();
00510        }
00511 
00512        ALLOC_INIT_ZVAL(z);
00513        jp = new_JSON_parser(depth);
00514        if (parse_JSON(jp, z, utf16, utf16_len, assoc TSRMLS_CC)) {
00515               *return_value = *z;
00516        }
00517        else
00518        {
00519               double d;
00520               int type;
00521               long p;
00522 
00523               RETVAL_NULL();
00524               if (str_len == 4) {
00525                      if (!strcasecmp(str, "null")) {
00526                             /* We need to explicitly clear the error because its an actual NULL and not an error */
00527                             jp->error_code = PHP_JSON_ERROR_NONE;
00528                             RETVAL_NULL();
00529                      } else if (!strcasecmp(str, "true")) {
00530                             RETVAL_BOOL(1);
00531                      }
00532               } else if (str_len == 5 && !strcasecmp(str, "false")) {
00533                      RETVAL_BOOL(0);
00534               }
00535 
00536               if ((type = is_numeric_string(str, str_len, &p, &d, 0)) != 0) {
00537                      if (type == IS_LONG) {
00538                             RETVAL_LONG(p);
00539                      } else if (type == IS_DOUBLE) {
00540                             RETVAL_DOUBLE(d);
00541                      }
00542               }
00543 
00544               if (Z_TYPE_P(return_value) != IS_NULL) {
00545                      jp->error_code = PHP_JSON_ERROR_NONE;
00546               }
00547 
00548               zval_dtor(z);
00549        }
00550        FREE_ZVAL(z);
00551        efree(utf16);
00552        JSON_G(error_code) = jp->error_code;
00553        free_JSON_parser(jp);
00554 }
00555 /* }}} */
00556 
00557 /* {{{ proto string json_encode(mixed data [, int options])
00558    Returns the JSON representation of a value */
00559 static PHP_FUNCTION(json_encode)
00560 {
00561        zval *parameter;
00562        smart_str buf = {0};
00563        long options = 0;
00564 
00565        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &parameter, &options) == FAILURE) {
00566               return;
00567        }
00568 
00569        JSON_G(error_code) = PHP_JSON_ERROR_NONE;
00570 
00571        php_json_encode(&buf, parameter, options TSRMLS_CC);
00572 
00573        ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
00574 
00575        smart_str_free(&buf);
00576 }
00577 /* }}} */
00578 
00579 /* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]])
00580    Decodes the JSON representation into a PHP value */
00581 static PHP_FUNCTION(json_decode)
00582 {
00583        char *str;
00584        int str_len;
00585        zend_bool assoc = 0; /* return JS objects as PHP objects by default */
00586        long depth = JSON_PARSER_DEFAULT_DEPTH;
00587 
00588        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &str, &str_len, &assoc, &depth) == FAILURE) {
00589               return;
00590        }
00591 
00592        JSON_G(error_code) = 0;
00593 
00594        if (!str_len) {
00595               RETURN_NULL();
00596        }
00597 
00598        php_json_decode(return_value, str, str_len, assoc, depth TSRMLS_CC);
00599 }
00600 /* }}} */
00601 
00602 /* {{{ proto int json_last_error()
00603    Returns the error code of the last json_decode(). */
00604 static PHP_FUNCTION(json_last_error)
00605 {
00606        if (zend_parse_parameters_none() == FAILURE) {
00607               return;
00608        }
00609 
00610        RETURN_LONG(JSON_G(error_code));
00611 }
00612 /* }}} */
00613 
00614 /*
00615  * Local variables:
00616  * tab-width: 4
00617  * c-basic-offset: 4
00618  * End:
00619  * vim600: noet sw=4 ts=4 fdm=marker
00620  * vim<600: noet sw=4 ts=4
00621  */