File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / json / json.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:03:49 2014 UTC (10 years, 1 month ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>