diff options
Diffstat (limited to 'ext/json/json_encoder.c')
-rw-r--r-- | ext/json/json_encoder.c | 103 |
1 files changed, 81 insertions, 22 deletions
diff --git a/ext/json/json_encoder.c b/ext/json/json_encoder.c index 92e4a10933..c76ddaf0fd 100644 --- a/ext/json/json_encoder.c +++ b/ext/json/json_encoder.c @@ -27,6 +27,7 @@ #include "php_json.h" #include "php_json_encoder.h" #include <zend_exceptions.h> +#include "zend_enum.h" static const char digits[] = "0123456789abcdef"; @@ -36,29 +37,10 @@ static int php_json_escape_string( static int php_json_determine_array_type(zval *val) /* {{{ */ { - int i; - HashTable *myht = Z_ARRVAL_P(val); - - i = myht ? zend_hash_num_elements(myht) : 0; - if (i > 0) { - zend_string *key; - zend_ulong index, idx; + zend_array *myht = Z_ARRVAL_P(val); - if (HT_IS_PACKED(myht) && HT_IS_WITHOUT_HOLES(myht)) { - return PHP_JSON_OUTPUT_ARRAY; - } - - idx = 0; - ZEND_HASH_FOREACH_KEY(myht, index, key) { - if (key) { - return PHP_JSON_OUTPUT_OBJECT; - } else { - if (index != idx) { - return PHP_JSON_OUTPUT_OBJECT; - } - } - idx++; - } ZEND_HASH_FOREACH_END(); + if (myht) { + return zend_array_is_list(myht) ? PHP_JSON_OUTPUT_ARRAY : PHP_JSON_OUTPUT_OBJECT; } return PHP_JSON_OUTPUT_ARRAY; @@ -134,6 +116,67 @@ static int php_json_encode_array(smart_str *buf, zval *val, int options, php_jso myht = Z_ARRVAL_P(val); prop_ht = NULL; r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : php_json_determine_array_type(val); + } else if (Z_OBJ_P(val)->properties == NULL + && Z_OBJ_HT_P(val)->get_properties_for == NULL + && Z_OBJ_HT_P(val)->get_properties == zend_std_get_properties) { + /* Optimized version without rebulding properties HashTable */ + zend_object *obj = Z_OBJ_P(val); + zend_class_entry *ce = obj->ce; + zend_property_info *prop_info; + zval *prop; + int i; + + if (GC_IS_RECURSIVE(obj)) { + encoder->error_code = PHP_JSON_ERROR_RECURSION; + smart_str_appendl(buf, "null", 4); + return FAILURE; + } + + PHP_JSON_HASH_PROTECT_RECURSION(obj); + smart_str_appendc(buf, '{'); + for (i = 0; i < ce->default_properties_count; i++) { + prop_info = ce->properties_info_table[i]; + if (!prop_info) { + continue; + } + if (ZSTR_VAL(prop_info->name)[0] == '\0' && ZSTR_LEN(prop_info->name) > 0) { + /* Skip protected and private members. */ + continue; + } + prop = OBJ_PROP(obj, prop_info->offset); + if (Z_TYPE_P(prop) == IS_UNDEF) { + continue; + } + + if (need_comma) { + smart_str_appendc(buf, ','); + } else { + need_comma = 1; + } + + php_json_pretty_print_char(buf, options, '\n'); + php_json_pretty_print_indent(buf, options, encoder); + + if (php_json_escape_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name), + 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); + } + + smart_str_appendc(buf, ':'); + php_json_pretty_print_char(buf, options, ' '); + + if (php_json_encode_zval(buf, prop, options, encoder) == FAILURE && + !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { + PHP_JSON_HASH_UNPROTECT_RECURSION(obj); + return FAILURE; + } + } + smart_str_appendc(buf, '}'); + PHP_JSON_HASH_UNPROTECT_RECURSION(obj); + return SUCCESS; } else { prop_ht = myht = zend_get_properties_for(val, ZEND_PROP_PURPOSE_JSON); r = PHP_JSON_OUTPUT_OBJECT; @@ -528,6 +571,19 @@ static int php_json_encode_serializable_object(smart_str *buf, zval *val, int op } /* }}} */ +static int php_json_encode_serializable_enum(smart_str *buf, zval *val, int options, php_json_encoder *encoder) +{ + zend_class_entry *ce = Z_OBJCE_P(val); + if (ce->enum_backing_type == IS_UNDEF) { + encoder->error_code = PHP_JSON_ERROR_NON_BACKED_ENUM; + smart_str_appendc(buf, '0'); + return FAILURE; + } + + zval *value_zv = zend_enum_fetch_case_value(Z_OBJ_P(val)); + return php_json_encode_zval(buf, value_zv, options, encoder); +} + int php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */ { again: @@ -564,6 +620,9 @@ again: if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) { return php_json_encode_serializable_object(buf, val, options, encoder); } + if (Z_OBJCE_P(val)->ce_flags & ZEND_ACC_ENUM) { + return php_json_encode_serializable_enum(buf, val, options, encoder); + } /* fallthrough -- Non-serializable object */ case IS_ARRAY: { /* Avoid modifications (and potential freeing) of the array through a reference when a |