From 381438e5df4534d5b2f0d5c7008289d35d4c372a Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 11 Dec 2014 19:18:31 +0300 Subject: [PATCH] Improved array_merge() and array_replace() (1-st array may be added using simple procedure). --- ext/standard/array.c | 228 +++++++++++++++++++++++++-------------- ext/standard/php_array.h | 3 +- 2 files changed, 151 insertions(+), 80 deletions(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index bacd98cf30..1f03e5acb8 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2348,99 +2348,104 @@ PHP_FUNCTION(array_slice) } /* }}} */ -PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC) /* {{{ */ +PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src TSRMLS_DC) /* {{{ */ { zval *src_entry, *dest_entry; zend_string *string_key; - if (recursive) { - ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { - if (string_key) { - if ((dest_entry = zend_hash_find(dest, string_key)) != NULL) { - zval *src_zval = src_entry; - zval *dest_zval = dest_entry; - HashTable *thash; - zval tmp; - int ret; - - ZVAL_DEREF(src_zval); - ZVAL_DEREF(dest_zval); - thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL; - if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); - return 0; - } + ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { + if (string_key) { + if ((dest_entry = zend_hash_find(dest, string_key)) != NULL) { + zval *src_zval = src_entry; + zval *dest_zval = dest_entry; + HashTable *thash; + zval tmp; + int ret; - if (Z_ISREF_P(dest_entry)) { - if (Z_REFCOUNT_P(dest_entry) == 1) { - ZVAL_UNREF(dest_entry); - } else { - Z_DELREF_P(dest_entry); - ZVAL_DUP(dest_entry, dest_zval); - } - dest_zval = dest_entry; - } else { - SEPARATE_ZVAL(dest_zval); - } + ZVAL_DEREF(src_zval); + ZVAL_DEREF(dest_zval); + thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL; + if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); + return 0; + } - if (Z_TYPE_P(dest_zval) == IS_NULL) { - convert_to_array_ex(dest_zval); - add_next_index_null(dest_zval); + if (Z_ISREF_P(dest_entry)) { + if (Z_REFCOUNT_P(dest_entry) == 1) { + ZVAL_UNREF(dest_entry); } else { - convert_to_array_ex(dest_zval); + Z_DELREF_P(dest_entry); + ZVAL_DUP(dest_entry, dest_zval); + } + dest_zval = dest_entry; + } else { + SEPARATE_ZVAL(dest_zval); + } + if (Z_TYPE_P(dest_zval) == IS_NULL) { + convert_to_array_ex(dest_zval); + add_next_index_null(dest_zval); + } else { + convert_to_array_ex(dest_zval); + } + ZVAL_UNDEF(&tmp); + if (Z_TYPE_P(src_zval) == IS_OBJECT) { + ZVAL_DUP(&tmp, src_zval); + convert_to_array(&tmp); + src_zval = &tmp; + } + if (Z_TYPE_P(src_zval) == IS_ARRAY) { + if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { + thash->u.v.nApplyCount++; } - ZVAL_UNDEF(&tmp); - if (Z_TYPE_P(src_zval) == IS_OBJECT) { - ZVAL_DUP(&tmp, src_zval); - convert_to_array(&tmp); - src_zval = &tmp; + ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval) TSRMLS_CC); + if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { + thash->u.v.nApplyCount--; } - if (Z_TYPE_P(src_zval) == IS_ARRAY) { - if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { - thash->u.v.nApplyCount++; - } - ret = php_array_merge(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval), 1 TSRMLS_CC); - if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { - thash->u.v.nApplyCount--; - } - if (!ret) { - return 0; - } - } else { - if (Z_REFCOUNTED_P(src_entry)) { - Z_ADDREF_P(src_entry); - } - zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval); + if (!ret) { + return 0; } - zval_ptr_dtor(&tmp); } else { if (Z_REFCOUNTED_P(src_entry)) { Z_ADDREF_P(src_entry); } - zend_hash_add_new(dest, string_key, src_entry); + zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval); } + zval_ptr_dtor(&tmp); } else { if (Z_REFCOUNTED_P(src_entry)) { Z_ADDREF_P(src_entry); } - zend_hash_next_index_insert_new(dest, src_entry); + zend_hash_add_new(dest, string_key, src_entry); } - } ZEND_HASH_FOREACH_END(); - } else { - ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { - if (string_key) { - if (Z_REFCOUNTED_P(src_entry)) { - Z_ADDREF_P(src_entry); - } - zend_hash_update(dest, string_key, src_entry); - } else { - if (Z_REFCOUNTED_P(src_entry)) { - Z_ADDREF_P(src_entry); - } - zend_hash_next_index_insert_new(dest, src_entry); + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); } - } ZEND_HASH_FOREACH_END(); - } + zend_hash_next_index_insert_new(dest, src_entry); + } + } ZEND_HASH_FOREACH_END(); + return 1; +} +/* }}} */ + +PHPAPI int php_array_merge(HashTable *dest, HashTable *src TSRMLS_DC) /* {{{ */ +{ + zval *src_entry; + zend_string *string_key; + + ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { + if (string_key) { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_update(dest, string_key, src_entry); + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_next_index_insert_new(dest, src_entry); + } + } ZEND_HASH_FOREACH_END(); return 1; } /* }}} */ @@ -2521,6 +2526,7 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */ { zval *args = NULL; + zval *arg; int argc, i, init_size = 0; #ifndef FAST_ZPP @@ -2551,16 +2557,80 @@ static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int array_init_size(return_value, init_size); - for (i = 0; i < argc; i++) { - zval *arg = args + i; + if (replace) { + zend_string *string_key; + zval *src_entry; + zend_ulong idx; + HashTable *src, *dest; + /* copy first array */ + arg = args; ZVAL_DEREF(arg); - if (!replace) { - php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg), recursive TSRMLS_CC); - } else if (recursive && i > 0) { /* First array will be copied directly instead */ - php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg) TSRMLS_CC); + src = Z_ARRVAL_P(arg); + dest = Z_ARRVAL_P(return_value); + ZEND_HASH_FOREACH_KEY_VAL(src, idx, string_key, src_entry) { + if (string_key) { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_add_new(dest, string_key, src_entry); + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_index_add_new(dest, idx, src_entry); + } + } ZEND_HASH_FOREACH_END(); + + if (recursive) { + for (i = 1; i < argc; i++) { + arg = args + i; + ZVAL_DEREF(arg); + php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg) TSRMLS_CC); + } } else { - zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg), zval_add_ref, 1); + for (i = 1; i < argc; i++) { + arg = args + i; + ZVAL_DEREF(arg); + zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg), zval_add_ref, 1); + } + } + } else { + zend_string *string_key; + zval *src_entry; + HashTable *src, *dest; + + /* copy first array */ + arg = args; + ZVAL_DEREF(arg); + src = Z_ARRVAL_P(arg); + dest = Z_ARRVAL_P(return_value); + ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { + if (string_key) { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_add_new(dest, string_key, src_entry); + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_next_index_insert_new(dest, src_entry); + } + } ZEND_HASH_FOREACH_END(); + + if (recursive) { + for (i = 1; i < argc; i++) { + arg = args + i; + ZVAL_DEREF(arg); + php_array_merge_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg) TSRMLS_CC); + } + } else { + for (i = 1; i < argc; i++) { + arg = args + i; + ZVAL_DEREF(arg); + php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg) TSRMLS_CC); + } } } } diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index d86121b277..31955042eb 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -104,7 +104,8 @@ PHP_FUNCTION(array_chunk); PHP_FUNCTION(array_combine); PHPAPI HashTable* php_splice(HashTable *, int, int, zval *, int, HashTable *); -PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC); +PHPAPI int php_array_merge(HashTable *dest, HashTable *src TSRMLS_DC); +PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src TSRMLS_DC); PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC); PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC); PHPAPI zend_long php_count_recursive(zval *array, zend_long mode TSRMLS_DC); -- 2.40.0