summaryrefslogtreecommitdiff
path: root/ext/json/json_encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/json/json_encoder.c')
-rw-r--r--ext/json/json_encoder.c300
1 files changed, 135 insertions, 165 deletions
diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c
index a7453bf926..8e3eecc0d8 100644
--- a/ext/json/json_encoder.c
+++ b/ext/json/json_encoder.c
@@ -17,8 +17,6 @@
+----------------------------------------------------------------------+
*/
-/* $Id$ */
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -117,14 +115,14 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options)
#define PHP_JSON_HASH_APPLY_PROTECTION_INC(_tmp_ht) \
do { \
- if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
+ if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
ZEND_HASH_INC_APPLY_COUNT(_tmp_ht); \
} \
} while (0)
#define PHP_JSON_HASH_APPLY_PROTECTION_DEC(_tmp_ht) \
do { \
- if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
+ if (_tmp_ht && ZEND_HASH_APPLY_PROTECTION(_tmp_ht)) { \
ZEND_HASH_DEC_APPLY_COUNT(_tmp_ht); \
} \
} while (0)
@@ -142,12 +140,14 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
r = PHP_JSON_OUTPUT_OBJECT;
}
- if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
+ if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) {
encoder->error_code = PHP_JSON_ERROR_RECURSION;
smart_str_appendl(buf, "null", 4);
return FAILURE;
}
+ PHP_JSON_HASH_APPLY_PROTECTION_INC(myht);
+
if (r == PHP_JSON_OUTPUT_ARRAY) {
smart_str_appendc(buf, '[');
} else {
@@ -162,13 +162,8 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
zend_string *key;
zval *data;
zend_ulong index;
- HashTable *tmp_ht;
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
- ZVAL_DEREF(data);
- tmp_ht = HASH_OF(data);
- PHP_JSON_HASH_APPLY_PROTECTION_INC(tmp_ht);
-
if (r == PHP_JSON_OUTPUT_ARRAY) {
if (need_comma) {
smart_str_appendc(buf, ',');
@@ -182,7 +177,6 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
if (key) {
if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) {
/* Skip protected and private members. */
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(tmp_ht);
continue;
}
@@ -195,8 +189,13 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
php_json_pretty_print_char(buf, options, '\n');
php_json_pretty_print_indent(buf, options, encoder);
- php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
- options & ~PHP_JSON_NUMERIC_CHECK, encoder);
+ if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
+ options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE &&
+ (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) &&
+ buf->s) {
+ ZSTR_LEN(buf->s) -= 4;
+ smart_str_appendl(buf, "\"\"", 2);
+ }
} else {
if (need_comma) {
smart_str_appendc(buf, ',');
@@ -218,14 +217,14 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
if (php_json_encode_zval(buf, data, options, encoder) == FAILURE &&
!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(tmp_ht);
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
return FAILURE;
}
-
- PHP_JSON_HASH_APPLY_PROTECTION_DEC(tmp_ht);
} ZEND_HASH_FOREACH_END();
}
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
+
if (encoder->depth > encoder->max_depth) {
encoder->error_code = PHP_JSON_ERROR_DEPTH;
if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
@@ -250,43 +249,6 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso
}
/* }}} */
-static int php_json_utf8_to_utf16(unsigned short *utf16, char utf8[], size_t len) /* {{{ */
-{
- size_t pos = 0, us;
- int j, status;
-
- if (utf16) {
- /* really convert the utf8 string */
- for (j=0 ; pos < len ; j++) {
- us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
- if (status != SUCCESS) {
- return -1;
- }
- /* From http://en.wikipedia.org/wiki/UTF16 */
- if (us >= 0x10000) {
- us -= 0x10000;
- utf16[j++] = (unsigned short)((us >> 10) | 0xd800);
- utf16[j] = (unsigned short)((us & 0x3ff) | 0xdc00);
- } else {
- utf16[j] = (unsigned short)us;
- }
- }
- } else {
- /* Only check if utf8 string is valid, and compute utf16 length */
- for (j=0 ; pos < len ; j++) {
- us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
- if (status != SUCCESS) {
- return -1;
- }
- if (us >= 0x10000) {
- j++;
- }
- }
- }
- return j;
-}
-/* }}} */
-
static int php_json_escape_string(
smart_str *buf, char *s, size_t len,
int options, php_json_encoder *encoder) /* {{{ */
@@ -316,18 +278,6 @@ static int php_json_escape_string(
}
}
-
- if (options & PHP_JSON_UNESCAPED_UNICODE) {
- /* validate UTF-8 string first */
- if (php_json_utf8_to_utf16(NULL, s, len) < 0) {
- encoder->error_code = PHP_JSON_ERROR_UTF8;
- if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
- smart_str_appendl(buf, "null", 4);
- }
- return FAILURE;
- }
- }
-
pos = 0;
checkpoint = buf->s ? ZSTR_LEN(buf->s) : 0;
@@ -337,26 +287,44 @@ static int php_json_escape_string(
do {
us = (unsigned char)s[pos];
- if (us >= 0x80 && (!(options & PHP_JSON_UNESCAPED_UNICODE) || us == 0xE2)) {
- /* UTF-8 character */
- us = php_next_utf8_char((const unsigned char *)s, len, &pos, &status);
+ if (us >= 0x80) {
+ int utf8_sub = 0;
+ size_t prev_pos = pos;
+
+ us = php_next_utf8_char((unsigned char *)s, len, &pos, &status);
+
+ /* check whether UTF8 character is correct */
if (status != SUCCESS) {
- if (buf->s) {
- ZSTR_LEN(buf->s) = checkpoint;
- }
- encoder->error_code = PHP_JSON_ERROR_UTF8;
- if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
- smart_str_appendl(buf, "null", 4);
+ if (options & PHP_JSON_INVALID_UTF8_IGNORE) {
+ /* ignore invalid UTF8 character */
+ continue;
+ } else if (options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
+ /* Use Unicode character 'REPLACEMENT CHARACTER' (U+FFFD) */
+ us = 0xfffd;
+ utf8_sub = 1;
+ } else {
+ if (buf->s) {
+ ZSTR_LEN(buf->s) = checkpoint;
+ }
+ encoder->error_code = PHP_JSON_ERROR_UTF8;
+ if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
+ smart_str_appendl(buf, "null", 4);
+ }
+ return FAILURE;
}
- return FAILURE;
}
+
/* Escape U+2028/U+2029 line terminators, UNLESS both
JSON_UNESCAPED_UNICODE and
JSON_UNESCAPED_LINE_TERMINATORS were provided */
if ((options & PHP_JSON_UNESCAPED_UNICODE)
- && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS)
+ && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS)
|| us < 0x2028 || us > 0x2029)) {
- smart_str_appendl(buf, &s[pos - 3], 3);
+ if (utf8_sub) {
+ smart_str_appendl(buf, "\xef\xbf\xbd", 3);
+ } else {
+ smart_str_appendl(buf, s + prev_pos, pos - prev_pos);
+ }
continue;
}
/* From http://en.wikipedia.org/wiki/UTF16 */
@@ -378,90 +346,93 @@ static int php_json_escape_string(
smart_str_appendc(buf, digits[(us & 0xf0) >> 4]);
smart_str_appendc(buf, digits[(us & 0xf)]);
} else {
- pos++;
-
- switch (us) {
- case '"':
- if (options & PHP_JSON_HEX_QUOT) {
- smart_str_appendl(buf, "\\u0022", 6);
- } else {
- smart_str_appendl(buf, "\\\"", 2);
- }
- break;
-
- case '\\':
- smart_str_appendl(buf, "\\\\", 2);
- break;
-
- case '/':
- if (options & PHP_JSON_UNESCAPED_SLASHES) {
- smart_str_appendc(buf, '/');
- } else {
- smart_str_appendl(buf, "\\/", 2);
- }
- break;
-
- case '\b':
- smart_str_appendl(buf, "\\b", 2);
- break;
-
- case '\f':
- smart_str_appendl(buf, "\\f", 2);
- break;
-
- case '\n':
- smart_str_appendl(buf, "\\n", 2);
- break;
-
- case '\r':
- smart_str_appendl(buf, "\\r", 2);
- break;
-
- case '\t':
- smart_str_appendl(buf, "\\t", 2);
- break;
-
- case '<':
- if (options & PHP_JSON_HEX_TAG) {
- smart_str_appendl(buf, "\\u003C", 6);
- } else {
- smart_str_appendc(buf, '<');
- }
- break;
+ static const uint32_t charmap[4] = {
+ 0xffffffff, 0x500080c4, 0x10000000, 0x00000000};
- case '>':
- if (options & PHP_JSON_HEX_TAG) {
- smart_str_appendl(buf, "\\u003E", 6);
- } else {
- smart_str_appendc(buf, '>');
- }
- break;
-
- case '&':
- if (options & PHP_JSON_HEX_AMP) {
- smart_str_appendl(buf, "\\u0026", 6);
- } else {
- smart_str_appendc(buf, '&');
- }
- break;
-
- case '\'':
- if (options & PHP_JSON_HEX_APOS) {
- smart_str_appendl(buf, "\\u0027", 6);
- } else {
- smart_str_appendc(buf, '\'');
- }
- break;
-
- default:
- if (us >= ' ') {
- smart_str_appendc(buf, (unsigned char) us);
- } else {
+ pos++;
+ if (EXPECTED(!(charmap[us >> 5] & (1 << (us & 0x1f))))) {
+ smart_str_appendc(buf, (unsigned char) us);
+ } else {
+ switch (us) {
+ case '"':
+ if (options & PHP_JSON_HEX_QUOT) {
+ smart_str_appendl(buf, "\\u0022", 6);
+ } else {
+ smart_str_appendl(buf, "\\\"", 2);
+ }
+ break;
+
+ case '\\':
+ smart_str_appendl(buf, "\\\\", 2);
+ break;
+
+ case '/':
+ if (options & PHP_JSON_UNESCAPED_SLASHES) {
+ smart_str_appendc(buf, '/');
+ } else {
+ smart_str_appendl(buf, "\\/", 2);
+ }
+ break;
+
+ case '\b':
+ smart_str_appendl(buf, "\\b", 2);
+ break;
+
+ case '\f':
+ smart_str_appendl(buf, "\\f", 2);
+ break;
+
+ case '\n':
+ smart_str_appendl(buf, "\\n", 2);
+ break;
+
+ case '\r':
+ smart_str_appendl(buf, "\\r", 2);
+ break;
+
+ case '\t':
+ smart_str_appendl(buf, "\\t", 2);
+ break;
+
+ case '<':
+ if (options & PHP_JSON_HEX_TAG) {
+ smart_str_appendl(buf, "\\u003C", 6);
+ } else {
+ smart_str_appendc(buf, '<');
+ }
+ break;
+
+ case '>':
+ if (options & PHP_JSON_HEX_TAG) {
+ smart_str_appendl(buf, "\\u003E", 6);
+ } else {
+ smart_str_appendc(buf, '>');
+ }
+ break;
+
+ case '&':
+ if (options & PHP_JSON_HEX_AMP) {
+ smart_str_appendl(buf, "\\u0026", 6);
+ } else {
+ smart_str_appendc(buf, '&');
+ }
+ break;
+
+ case '\'':
+ if (options & PHP_JSON_HEX_APOS) {
+ smart_str_appendl(buf, "\\u0027", 6);
+ } else {
+ smart_str_appendc(buf, '\'');
+ }
+ break;
+
+ default:
+ ZEND_ASSERT(us < ' ');
smart_str_appendl(buf, "\\u00", sizeof("\\u00")-1);
smart_str_appendc(buf, digits[(us & 0xf0) >> 4]);
smart_str_appendc(buf, digits[(us & 0xf)]);
- }
- break;
+ break;
+ }
}
}
} while (pos < len);
@@ -475,17 +446,11 @@ static int php_json_escape_string(
static int php_json_encode_serializable_object(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(val);
+ HashTable* myht = Z_OBJPROP_P(val);
zval retval, fname;
- HashTable* myht;
int return_code;
- if (Z_TYPE_P(val) == IS_ARRAY) {
- myht = Z_ARRVAL_P(val);
- } else {
- myht = Z_OBJPROP_P(val);
- }
-
- if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
+ if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) {
encoder->error_code = PHP_JSON_ERROR_RECURSION;
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
@@ -493,6 +458,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
return FAILURE;
}
+ PHP_JSON_HASH_APPLY_PROTECTION_INC(myht);
ZVAL_STRING(&fname, "jsonSerialize");
@@ -505,6 +471,7 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
return FAILURE;
}
@@ -516,16 +483,19 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
return FAILURE;
}
if ((Z_TYPE(retval) == IS_OBJECT) &&
(Z_OBJ(retval) == Z_OBJ_P(val))) {
/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
return_code = php_json_encode_array(buf, &retval, options, encoder);
} else {
/* All other types, encode as normal */
return_code = php_json_encode_zval(buf, &retval, options, encoder);
+ PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht);
}
zval_ptr_dtor(&retval);