Add initial failure checking for json_encode

This commit is contained in:
Jakub Zelenka 2016-08-24 20:46:33 +01:00
parent 0e709fe42d
commit 12a8c3f6e7
3 changed files with 57 additions and 43 deletions

View file

@ -186,9 +186,7 @@ static PHP_MINFO_FUNCTION(json)
PHP_JSON_API int php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */ PHP_JSON_API int php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */
{ {
php_json_encode_zval(buf, val, options); return php_json_encode_zval(buf, val, options);
return JSON_G(error_code) > 0 ? FAILURE : SUCCESS;
} }
/* }}} */ /* }}} */

View file

@ -33,7 +33,7 @@
static const char digits[] = "0123456789abcdef"; static const char digits[] = "0123456789abcdef";
static void php_json_escape_string(smart_str *buf, char *s, size_t len, int options); static int php_json_escape_string(smart_str *buf, char *s, size_t len, int options);
static int php_json_determine_array_type(zval *val) /* {{{ */ static int php_json_determine_array_type(zval *val) /* {{{ */
{ {
@ -108,7 +108,7 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options)
} }
/* }}} */ /* }}} */
static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{ */ static int php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{ */
{ {
int i, r, need_comma = 0; int i, r, need_comma = 0;
HashTable *myht; HashTable *myht;
@ -124,7 +124,7 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) { if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
JSON_G(error_code) = PHP_JSON_ERROR_RECURSION; JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
smart_str_appendl(buf, "null", 4); smart_str_appendl(buf, "null", 4);
return; return FAILURE;
} }
if (r == PHP_JSON_OUTPUT_ARRAY) { if (r == PHP_JSON_OUTPUT_ARRAY) {
@ -159,7 +159,6 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
php_json_pretty_print_char(buf, options, '\n'); php_json_pretty_print_char(buf, options, '\n');
php_json_pretty_print_indent(buf, options); php_json_pretty_print_indent(buf, options);
php_json_encode(buf, data, options);
} else if (r == PHP_JSON_OUTPUT_OBJECT) { } else if (r == PHP_JSON_OUTPUT_OBJECT) {
if (key) { if (key) {
if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) { if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) {
@ -180,11 +179,6 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
php_json_pretty_print_indent(buf, options); php_json_pretty_print_indent(buf, options);
php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key), options & ~PHP_JSON_NUMERIC_CHECK); php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key), options & ~PHP_JSON_NUMERIC_CHECK);
smart_str_appendc(buf, ':');
php_json_pretty_print_char(buf, options, ' ');
php_json_encode(buf, data, options);
} else { } else {
if (need_comma) { if (need_comma) {
smart_str_appendc(buf, ','); smart_str_appendc(buf, ',');
@ -198,12 +192,14 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
smart_str_appendc(buf, '"'); smart_str_appendc(buf, '"');
smart_str_append_long(buf, (zend_long) index); smart_str_append_long(buf, (zend_long) index);
smart_str_appendc(buf, '"'); smart_str_appendc(buf, '"');
smart_str_appendc(buf, ':');
php_json_pretty_print_char(buf, options, ' ');
php_json_encode(buf, data, options);
} }
smart_str_appendc(buf, ':');
php_json_pretty_print_char(buf, options, ' ');
}
if (php_json_encode(buf, data, options) == FAILURE && (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
return FAILURE;
} }
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) { if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
@ -214,6 +210,9 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
if (JSON_G(encoder_depth) > JSON_G(encode_max_depth)) { if (JSON_G(encoder_depth) > JSON_G(encode_max_depth)) {
JSON_G(error_code) = PHP_JSON_ERROR_DEPTH; JSON_G(error_code) = PHP_JSON_ERROR_DEPTH;
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
return FAILURE;
}
} }
--JSON_G(encoder_depth); --JSON_G(encoder_depth);
@ -228,6 +227,8 @@ static void php_json_encode_array(smart_str *buf, zval *val, int options) /* {{{
} else { } else {
smart_str_appendc(buf, '}'); smart_str_appendc(buf, '}');
} }
return SUCCESS;
} }
/* }}} */ /* }}} */
@ -268,7 +269,7 @@ static int php_json_utf8_to_utf16(unsigned short *utf16, char utf8[], size_t len
} }
/* }}} */ /* }}} */
static void php_json_escape_string(smart_str *buf, char *s, size_t len, int options) /* {{{ */ static int php_json_escape_string(smart_str *buf, char *s, size_t len, int options) /* {{{ */
{ {
int status; int status;
unsigned int us; unsigned int us;
@ -276,7 +277,7 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
if (len == 0) { if (len == 0) {
smart_str_appendl(buf, "\"\"", 2); smart_str_appendl(buf, "\"\"", 2);
return; return SUCCESS;
} }
if (options & PHP_JSON_NUMERIC_CHECK) { if (options & PHP_JSON_NUMERIC_CHECK) {
@ -287,10 +288,10 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) { if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
if (type == IS_LONG) { if (type == IS_LONG) {
smart_str_append_long(buf, p); smart_str_append_long(buf, p);
return; return SUCCESS;
} else if (type == IS_DOUBLE && php_json_is_valid_double(d)) { } else if (type == IS_DOUBLE && php_json_is_valid_double(d)) {
php_json_encode_double(buf, d, options); php_json_encode_double(buf, d, options);
return; return SUCCESS;
} }
} }
@ -300,8 +301,10 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
/* validate UTF-8 string first */ /* validate UTF-8 string first */
if (php_json_utf8_to_utf16(NULL, s, len) < 0) { if (php_json_utf8_to_utf16(NULL, s, len) < 0) {
JSON_G(error_code) = PHP_JSON_ERROR_UTF8; JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
smart_str_appendl(buf, "null", 4); if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
return; smart_str_appendl(buf, "null", 4);
}
return FAILURE;
} }
} }
@ -322,8 +325,10 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
ZSTR_LEN(buf->s) = checkpoint; ZSTR_LEN(buf->s) = checkpoint;
} }
JSON_G(error_code) = PHP_JSON_ERROR_UTF8; JSON_G(error_code) = PHP_JSON_ERROR_UTF8;
smart_str_appendl(buf, "null", 4); if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
return; smart_str_appendl(buf, "null", 4);
}
return FAILURE;
} }
/* Escape U+2028/U+2029 line terminators, UNLESS both /* Escape U+2028/U+2029 line terminators, UNLESS both
JSON_UNESCAPED_UNICODE and JSON_UNESCAPED_UNICODE and
@ -442,10 +447,12 @@ static void php_json_escape_string(smart_str *buf, char *s, size_t len, int opti
} while (pos < len); } while (pos < len);
smart_str_appendc(buf, '"'); smart_str_appendc(buf, '"');
return SUCCESS;
} }
/* }}} */ /* }}} */
static void php_json_encode_serializable_object(smart_str *buf, zval *val, int options) /* {{{ */ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int options) /* {{{ */
{ {
zend_class_entry *ce = Z_OBJCE_P(val); zend_class_entry *ce = Z_OBJCE_P(val);
zval retval, fname; zval retval, fname;
@ -460,8 +467,10 @@ static void php_json_encode_serializable_object(smart_str *buf, zval *val, int o
if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) { if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 1) {
JSON_G(error_code) = PHP_JSON_ERROR_RECURSION; JSON_G(error_code) = PHP_JSON_ERROR_RECURSION;
smart_str_appendl(buf, "null", 4); if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
return; smart_str_appendl(buf, "null", 4);
}
return FAILURE;
} }
@ -470,9 +479,12 @@ static void php_json_encode_serializable_object(smart_str *buf, zval *val, int o
origin_error_code = JSON_G(error_code); origin_error_code = JSON_G(error_code);
if (FAILURE == call_user_function_ex(EG(function_table), val, &fname, &retval, 0, NULL, 1, NULL) || Z_TYPE(retval) == IS_UNDEF) { if (FAILURE == call_user_function_ex(EG(function_table), val, &fname, &retval, 0, NULL, 1, NULL) || Z_TYPE(retval) == IS_UNDEF) {
zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name)); zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name));
smart_str_appendl(buf, "null", sizeof("null") - 1);
zval_ptr_dtor(&fname); zval_ptr_dtor(&fname);
return;
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
return FAILURE;
} }
JSON_G(error_code) = origin_error_code; JSON_G(error_code) = origin_error_code;
@ -480,8 +492,11 @@ static void php_json_encode_serializable_object(smart_str *buf, zval *val, int o
/* Error already raised */ /* Error already raised */
zval_ptr_dtor(&retval); zval_ptr_dtor(&retval);
zval_ptr_dtor(&fname); zval_ptr_dtor(&fname);
smart_str_appendl(buf, "null", sizeof("null") - 1);
return; if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
smart_str_appendl(buf, "null", 4);
}
return FAILURE;
} }
if ((Z_TYPE(retval) == IS_OBJECT) && if ((Z_TYPE(retval) == IS_OBJECT) &&
@ -495,10 +510,12 @@ static void php_json_encode_serializable_object(smart_str *buf, zval *val, int o
zval_ptr_dtor(&retval); zval_ptr_dtor(&retval);
zval_ptr_dtor(&fname); zval_ptr_dtor(&fname);
return SUCCESS;
} }
/* }}} */ /* }}} */
void php_json_encode_zval(smart_str *buf, zval *val, int options) /* {{{ */ int php_json_encode_zval(smart_str *buf, zval *val, int options) /* {{{ */
{ {
again: again:
switch (Z_TYPE_P(val)) switch (Z_TYPE_P(val))
@ -528,18 +545,15 @@ again:
break; break;
case IS_STRING: case IS_STRING:
php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options); return php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options);
break;
case IS_OBJECT: case IS_OBJECT:
if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) { if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
php_json_encode_serializable_object(buf, val, options); return php_json_encode_serializable_object(buf, val, options);
break;
} }
/* fallthrough -- Non-serializable object */ /* fallthrough -- Non-serializable object */
case IS_ARRAY: case IS_ARRAY:
php_json_encode_array(buf, val, options); return php_json_encode_array(buf, val, options);
break;
case IS_REFERENCE: case IS_REFERENCE:
val = Z_REFVAL_P(val); val = Z_REFVAL_P(val);
@ -547,11 +561,13 @@ again:
default: default:
JSON_G(error_code) = PHP_JSON_ERROR_UNSUPPORTED_TYPE; JSON_G(error_code) = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
smart_str_appendl(buf, "null", 4); if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
break; smart_str_appendl(buf, "null", 4);
}
return FAILURE;
} }
return; return SUCCESS;
} }
/* }}} */ /* }}} */

View file

@ -22,6 +22,6 @@
#include "php.h" #include "php.h"
#include "zend_smart_str.h" #include "zend_smart_str.h"
void php_json_encode_zval(smart_str *buf, zval *val, int options); int php_json_encode_zval(smart_str *buf, zval *val, int options);
#endif /* PHP_JSON_ENCODER_H */ #endif /* PHP_JSON_ENCODER_H */