1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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/php_smart_str.h" 29#include "utf8_to_utf16.h" 30#include "JSON_parser.h" 31#include "php_json.h" 32 33static PHP_MINFO_FUNCTION(json); 34static PHP_FUNCTION(json_encode); 35static PHP_FUNCTION(json_decode); 36static PHP_FUNCTION(json_last_error); 37 38static const char digits[] = "0123456789abcdef"; 39 40ZEND_DECLARE_MODULE_GLOBALS(json) 41 42/* {{{ arginfo */ 43ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1) 44 ZEND_ARG_INFO(0, value) 45 ZEND_ARG_INFO(0, options) 46ZEND_END_ARG_INFO() 47 48ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1) 49 ZEND_ARG_INFO(0, json) 50 ZEND_ARG_INFO(0, assoc) 51 ZEND_ARG_INFO(0, depth) 52ZEND_END_ARG_INFO() 53 54ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0) 55ZEND_END_ARG_INFO() 56/* }}} */ 57 58/* {{{ json_functions[] */ 59static const function_entry json_functions[] = { 60 PHP_FE(json_encode, arginfo_json_encode) 61 PHP_FE(json_decode, arginfo_json_decode) 62 PHP_FE(json_last_error, arginfo_json_last_error) 63 PHP_FE_END 64}; 65/* }}} */ 66 67/* {{{ MINIT */ 68static PHP_MINIT_FUNCTION(json) 69{ 70 REGISTER_LONG_CONSTANT("JSON_HEX_TAG", PHP_JSON_HEX_TAG, CONST_CS | CONST_PERSISTENT); 71 REGISTER_LONG_CONSTANT("JSON_HEX_AMP", PHP_JSON_HEX_AMP, CONST_CS | CONST_PERSISTENT); 72 REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT); 73 REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT); 74 REGISTER_LONG_CONSTANT("JSON_FORCE_OBJECT", PHP_JSON_FORCE_OBJECT, CONST_CS | CONST_PERSISTENT); 75 REGISTER_LONG_CONSTANT("JSON_NUMERIC_CHECK", PHP_JSON_NUMERIC_CHECK, CONST_CS | CONST_PERSISTENT); 76 77 REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT); 78 REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT); 79 REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT); 80 REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT); 81 REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT); 82 REGISTER_LONG_CONSTANT("JSON_ERROR_UTF8", PHP_JSON_ERROR_UTF8, CONST_CS | CONST_PERSISTENT); 83 84 return SUCCESS; 85} 86/* }}} */ 87 88/* {{{ PHP_GINIT_FUNCTION 89*/ 90static PHP_GINIT_FUNCTION(json) 91{ 92 json_globals->error_code = 0; 93} 94/* }}} */ 95 96 97/* {{{ json_module_entry 98 */ 99zend_module_entry json_module_entry = { 100 STANDARD_MODULE_HEADER, 101 "json", 102 json_functions, 103 PHP_MINIT(json), 104 NULL, 105 NULL, 106 NULL, 107 PHP_MINFO(json), 108 PHP_JSON_VERSION, 109 PHP_MODULE_GLOBALS(json), 110 PHP_GINIT(json), 111 NULL, 112 NULL, 113 STANDARD_MODULE_PROPERTIES_EX 114}; 115/* }}} */ 116 117#ifdef COMPILE_DL_JSON 118ZEND_GET_MODULE(json) 119#endif 120 121/* {{{ PHP_MINFO_FUNCTION 122 */ 123static PHP_MINFO_FUNCTION(json) 124{ 125 php_info_print_table_start(); 126 php_info_print_table_row(2, "json support", "enabled"); 127 php_info_print_table_row(2, "json version", PHP_JSON_VERSION); 128 php_info_print_table_end(); 129} 130/* }}} */ 131 132static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC); 133 134static int json_determine_array_type(zval **val TSRMLS_DC) /* {{{ */ 135{ 136 int i; 137 HashTable *myht = HASH_OF(*val); 138 139 i = myht ? zend_hash_num_elements(myht) : 0; 140 if (i > 0) { 141 char *key; 142 ulong index, idx; 143 uint key_len; 144 HashPosition pos; 145 146 zend_hash_internal_pointer_reset_ex(myht, &pos); 147 idx = 0; 148 for (;; zend_hash_move_forward_ex(myht, &pos)) { 149 i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); 150 if (i == HASH_KEY_NON_EXISTANT) { 151 break; 152 } 153 154 if (i == HASH_KEY_IS_STRING) { 155 return 1; 156 } else { 157 if (index != idx) { 158 return 1; 159 } 160 } 161 idx++; 162 } 163 } 164 165 return PHP_JSON_OUTPUT_ARRAY; 166} 167/* }}} */ 168 169static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */ 170{ 171 int i, r; 172 HashTable *myht; 173 174 if (Z_TYPE_PP(val) == IS_ARRAY) { 175 myht = HASH_OF(*val); 176 r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : json_determine_array_type(val TSRMLS_CC); 177 } else { 178 myht = Z_OBJPROP_PP(val); 179 r = PHP_JSON_OUTPUT_OBJECT; 180 } 181 182 if (myht && myht->nApplyCount > 1) { 183 php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); 184 smart_str_appendl(buf, "null", 4); 185 return; 186 } 187 188 if (r == PHP_JSON_OUTPUT_ARRAY) { 189 smart_str_appendc(buf, '['); 190 } else { 191 smart_str_appendc(buf, '{'); 192 } 193 194 i = myht ? zend_hash_num_elements(myht) : 0; 195 196 if (i > 0) 197 { 198 char *key; 199 zval **data; 200 ulong index; 201 uint key_len; 202 HashPosition pos; 203 HashTable *tmp_ht; 204 int need_comma = 0; 205 206 zend_hash_internal_pointer_reset_ex(myht, &pos); 207 for (;; zend_hash_move_forward_ex(myht, &pos)) { 208 i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); 209 if (i == HASH_KEY_NON_EXISTANT) 210 break; 211 212 if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) == SUCCESS) { 213 tmp_ht = HASH_OF(*data); 214 if (tmp_ht) { 215 tmp_ht->nApplyCount++; 216 } 217 218 if (r == PHP_JSON_OUTPUT_ARRAY) { 219 if (need_comma) { 220 smart_str_appendc(buf, ','); 221 } else { 222 need_comma = 1; 223 } 224 225 php_json_encode(buf, *data, options TSRMLS_CC); 226 } else if (r == PHP_JSON_OUTPUT_OBJECT) { 227 if (i == HASH_KEY_IS_STRING) { 228 if (key[0] == '\0' && Z_TYPE_PP(val) == IS_OBJECT) { 229 /* Skip protected and private members. */ 230 if (tmp_ht) { 231 tmp_ht->nApplyCount--; 232 } 233 continue; 234 } 235 236 if (need_comma) { 237 smart_str_appendc(buf, ','); 238 } else { 239 need_comma = 1; 240 } 241 242 json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC); 243 smart_str_appendc(buf, ':'); 244 245 php_json_encode(buf, *data, options TSRMLS_CC); 246 } else { 247 if (need_comma) { 248 smart_str_appendc(buf, ','); 249 } else { 250 need_comma = 1; 251 } 252 253 smart_str_appendc(buf, '"'); 254 smart_str_append_long(buf, (long) index); 255 smart_str_appendc(buf, '"'); 256 smart_str_appendc(buf, ':'); 257 258 php_json_encode(buf, *data, options TSRMLS_CC); 259 } 260 } 261 262 if (tmp_ht) { 263 tmp_ht->nApplyCount--; 264 } 265 } 266 } 267 } 268 269 if (r == PHP_JSON_OUTPUT_ARRAY) { 270 smart_str_appendc(buf, ']'); 271 } else { 272 smart_str_appendc(buf, '}'); 273 } 274} 275/* }}} */ 276 277#define REVERSE16(us) (((us & 0xf) << 12) | (((us >> 4) & 0xf) << 8) | (((us >> 8) & 0xf) << 4) | ((us >> 12) & 0xf)) 278 279static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */ 280{ 281 int pos = 0; 282 unsigned short us; 283 unsigned short *utf16; 284 285 if (len == 0) { 286 smart_str_appendl(buf, "\"\"", 2); 287 return; 288 } 289 290 if (options & PHP_JSON_NUMERIC_CHECK) { 291 double d; 292 int type; 293 long p; 294 295 if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) { 296 if (type == IS_LONG) { 297 smart_str_append_long(buf, p); 298 } else if (type == IS_DOUBLE) { 299 if (!zend_isinf(d) && !zend_isnan(d)) { 300 char *tmp; 301 int l = spprintf(&tmp, 0, "%.*k", (int) EG(precision), d); 302 smart_str_appendl(buf, tmp, l); 303 efree(tmp); 304 } else { 305 php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", d); 306 smart_str_appendc(buf, '0'); 307 } 308 } 309 return; 310 } 311 312 } 313 314 utf16 = (unsigned short *) safe_emalloc(len, sizeof(unsigned short), 0); 315 316 len = utf8_to_utf16(utf16, s, len); 317 if (len <= 0) { 318 if (utf16) { 319 efree(utf16); 320 } 321 if (len < 0) { 322 JSON_G(error_code) = PHP_JSON_ERROR_UTF8; 323 if (!PG(display_errors)) { 324 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid UTF-8 sequence in argument"); 325 } 326 smart_str_appendl(buf, "null", 4); 327 } else { 328 smart_str_appendl(buf, "\"\"", 2); 329 } 330 return; 331 } 332 333 smart_str_appendc(buf, '"'); 334 335 while (pos < len) 336 { 337 us = utf16[pos++]; 338 339 switch (us) 340 { 341 case '"': 342 if (options & PHP_JSON_HEX_QUOT) { 343 smart_str_appendl(buf, "\\u0022", 6); 344 } else { 345 smart_str_appendl(buf, "\\\"", 2); 346 } 347 break; 348 349 case '\\': 350 smart_str_appendl(buf, "\\\\", 2); 351 break; 352 353 case '/': 354 smart_str_appendl(buf, "\\/", 2); 355 break; 356 357 case '\b': 358 smart_str_appendl(buf, "\\b", 2); 359 break; 360 361 case '\f': 362 smart_str_appendl(buf, "\\f", 2); 363 break; 364 365 case '\n': 366 smart_str_appendl(buf, "\\n", 2); 367 break; 368 369 case '\r': 370 smart_str_appendl(buf, "\\r", 2); 371 break; 372 373 case '\t': 374 smart_str_appendl(buf, "\\t", 2); 375 break; 376 377 case '<': 378 if (options & PHP_JSON_HEX_TAG) { 379 smart_str_appendl(buf, "\\u003C", 6); 380 } else { 381 smart_str_appendc(buf, '<'); 382 } 383 break; 384 385 case '>': 386 if (options & PHP_JSON_HEX_TAG) { 387 smart_str_appendl(buf, "\\u003E", 6); 388 } else { 389 smart_str_appendc(buf, '>'); 390 } 391 break; 392 393 case '&': 394 if (options & PHP_JSON_HEX_AMP) { 395 smart_str_appendl(buf, "\\u0026", 6); 396 } else { 397 smart_str_appendc(buf, '&'); 398 } 399 break; 400 401 case '\'': 402 if (options & PHP_JSON_HEX_APOS) { 403 smart_str_appendl(buf, "\\u0027", 6); 404 } else { 405 smart_str_appendc(buf, '\''); 406 } 407 break; 408 409 default: 410 if (us >= ' ' && (us & 127) == us) { 411 smart_str_appendc(buf, (unsigned char) us); 412 } else { 413 smart_str_appendl(buf, "\\u", 2); 414 us = REVERSE16(us); 415 416 smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]); 417 us >>= 4; 418 smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]); 419 us >>= 4; 420 smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]); 421 us >>= 4; 422 smart_str_appendc(buf, digits[us & ((1 << 4) - 1)]); 423 } 424 break; 425 } 426 } 427 428 smart_str_appendc(buf, '"'); 429 efree(utf16); 430} 431/* }}} */ 432 433PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC) /* {{{ */ 434{ 435 switch (Z_TYPE_P(val)) 436 { 437 case IS_NULL: 438 smart_str_appendl(buf, "null", 4); 439 break; 440 441 case IS_BOOL: 442 if (Z_BVAL_P(val)) { 443 smart_str_appendl(buf, "true", 4); 444 } else { 445 smart_str_appendl(buf, "false", 5); 446 } 447 break; 448 449 case IS_LONG: 450 smart_str_append_long(buf, Z_LVAL_P(val)); 451 break; 452 453 case IS_DOUBLE: 454 { 455 char *d = NULL; 456 int len; 457 double dbl = Z_DVAL_P(val); 458 459 if (!zend_isinf(dbl) && !zend_isnan(dbl)) { 460 len = spprintf(&d, 0, "%.*k", (int) EG(precision), dbl); 461 smart_str_appendl(buf, d, len); 462 efree(d); 463 } else { 464 php_error_docref(NULL TSRMLS_CC, E_WARNING, "double %.9g does not conform to the JSON spec, encoded as 0", dbl); 465 smart_str_appendc(buf, '0'); 466 } 467 } 468 break; 469 470 case IS_STRING: 471 json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options TSRMLS_CC); 472 break; 473 474 case IS_ARRAY: 475 case IS_OBJECT: 476 json_encode_array(buf, &val, options TSRMLS_CC); 477 break; 478 479 default: 480 php_error_docref(NULL TSRMLS_CC, E_WARNING, "type is unsupported, encoded as null"); 481 smart_str_appendl(buf, "null", 4); 482 break; 483 } 484 485 return; 486} 487/* }}} */ 488 489PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) /* {{{ */ 490{ 491 int utf16_len; 492 zval *z; 493 unsigned short *utf16; 494 JSON_parser jp; 495 496 utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1); 497 498 utf16_len = utf8_to_utf16(utf16, str, str_len); 499 if (utf16_len <= 0) { 500 if (utf16) { 501 efree(utf16); 502 } 503 JSON_G(error_code) = PHP_JSON_ERROR_UTF8; 504 RETURN_NULL(); 505 } 506 507 if (depth <= 0) { 508 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Depth must be greater than zero"); 509 efree(utf16); 510 RETURN_NULL(); 511 } 512 513 ALLOC_INIT_ZVAL(z); 514 jp = new_JSON_parser(depth); 515 if (parse_JSON(jp, z, utf16, utf16_len, assoc TSRMLS_CC)) { 516 *return_value = *z; 517 } 518 else 519 { 520 double d; 521 int type; 522 long p; 523 524 RETVAL_NULL(); 525 if (str_len == 4) { 526 if (!strcasecmp(str, "null")) { 527 /* We need to explicitly clear the error because its an actual NULL and not an error */ 528 jp->error_code = PHP_JSON_ERROR_NONE; 529 RETVAL_NULL(); 530 } else if (!strcasecmp(str, "true")) { 531 RETVAL_BOOL(1); 532 } 533 } else if (str_len == 5 && !strcasecmp(str, "false")) { 534 RETVAL_BOOL(0); 535 } 536 537 if ((type = is_numeric_string(str, str_len, &p, &d, 0)) != 0) { 538 if (type == IS_LONG) { 539 RETVAL_LONG(p); 540 } else if (type == IS_DOUBLE) { 541 RETVAL_DOUBLE(d); 542 } 543 } 544 545 if (Z_TYPE_P(return_value) != IS_NULL) { 546 jp->error_code = PHP_JSON_ERROR_NONE; 547 } 548 549 zval_dtor(z); 550 } 551 FREE_ZVAL(z); 552 efree(utf16); 553 JSON_G(error_code) = jp->error_code; 554 free_JSON_parser(jp); 555} 556/* }}} */ 557 558/* {{{ proto string json_encode(mixed data [, int options]) 559 Returns the JSON representation of a value */ 560static PHP_FUNCTION(json_encode) 561{ 562 zval *parameter; 563 smart_str buf = {0}; 564 long options = 0; 565 566 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", ¶meter, &options) == FAILURE) { 567 return; 568 } 569 570 JSON_G(error_code) = PHP_JSON_ERROR_NONE; 571 572 php_json_encode(&buf, parameter, options TSRMLS_CC); 573 574 ZVAL_STRINGL(return_value, buf.c, buf.len, 1); 575 576 smart_str_free(&buf); 577} 578/* }}} */ 579 580/* {{{ proto mixed json_decode(string json [, bool assoc [, long depth]]) 581 Decodes the JSON representation into a PHP value */ 582static PHP_FUNCTION(json_decode) 583{ 584 char *str; 585 int str_len; 586 zend_bool assoc = 0; /* return JS objects as PHP objects by default */ 587 long depth = JSON_PARSER_DEFAULT_DEPTH; 588 589 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &str, &str_len, &assoc, &depth) == FAILURE) { 590 return; 591 } 592 593 JSON_G(error_code) = 0; 594 595 if (!str_len) { 596 RETURN_NULL(); 597 } 598 599 php_json_decode(return_value, str, str_len, assoc, depth TSRMLS_CC); 600} 601/* }}} */ 602 603/* {{{ proto int json_last_error() 604 Returns the error code of the last json_decode(). */ 605static PHP_FUNCTION(json_last_error) 606{ 607 if (zend_parse_parameters_none() == FAILURE) { 608 return; 609 } 610 611 RETURN_LONG(JSON_G(error_code)); 612} 613/* }}} */ 614 615/* 616 * Local variables: 617 * tab-width: 4 618 * c-basic-offset: 4 619 * End: 620 * vim600: noet sw=4 ts=4 fdm=marker 621 * vim<600: noet sw=4 ts=4 622 */ 623