From: Johannes Schlüter Date: Thu, 31 Jul 2008 20:17:07 +0000 (+0000) Subject: - Add array_replace/array_replace_recursive (Mett Wilmas) X-Git-Tag: BEFORE_HEAD_NS_CHANGE~935 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e0e15347e2b6db62afc4ef93429fedd7be2e7c50;p=php - Add array_replace/array_replace_recursive (Mett Wilmas) --- diff --git a/ext/standard/array.c b/ext/standard/array.c index ebbbd9a545..6b9021d4da 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2500,10 +2500,75 @@ ukey: } /* }}} */ -static void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */ +PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC) /* {{{ */ +{ + zval **src_entry, **dest_entry; + zstr string_key; + uint string_key_len; + ulong num_key; + HashPosition pos; + + for (zend_hash_internal_pointer_reset_ex(src, &pos); + zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS; + zend_hash_move_forward_ex(src, &pos)) { + zend_uchar utype; + + switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) { + case HASH_KEY_IS_STRING: + utype = IS_STRING; + goto ukey; + case HASH_KEY_IS_UNICODE: + utype = IS_UNICODE; +ukey: + if (Z_TYPE_PP(src_entry) != IS_ARRAY || + zend_u_hash_find(dest, utype, string_key, string_key_len, (void **)&dest_entry) == FAILURE || + Z_TYPE_PP(dest_entry) != IS_ARRAY) { + + Z_ADDREF_PP(src_entry); + zend_u_hash_update(dest, utype, string_key, string_key_len, src_entry, sizeof(zval *), NULL); + + continue; + } + break; + + case HASH_KEY_IS_LONG: + if (Z_TYPE_PP(src_entry) != IS_ARRAY || + zend_hash_index_find(dest, num_key, (void **)&dest_entry) == FAILURE || + Z_TYPE_PP(dest_entry) != IS_ARRAY) { + + Z_ADDREF_PP(src_entry); + zend_hash_index_update(dest, num_key, src_entry, sizeof(zval *), NULL); + + continue; + } + break; + } + + if (Z_ARRVAL_PP(src_entry)->nApplyCount > 1 || Z_ARRVAL_PP(dest_entry)->nApplyCount > 1 || (*src_entry == *dest_entry && Z_ISREF_PP(dest_entry) && (Z_REFCOUNT_PP(dest_entry) % 2))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); + return 0; + } + SEPARATE_ZVAL(dest_entry); + Z_ARRVAL_PP(dest_entry)->nApplyCount++; + Z_ARRVAL_PP(src_entry)->nApplyCount++; + + if (!php_array_replace_recursive(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry) TSRMLS_CC)) { + Z_ARRVAL_PP(dest_entry)->nApplyCount--; + Z_ARRVAL_PP(src_entry)->nApplyCount--; + return 0; + } + Z_ARRVAL_PP(dest_entry)->nApplyCount--; + Z_ARRVAL_PP(src_entry)->nApplyCount--; + } + + return 1; +} +/* }}} */ + +static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */ { zval ***args = NULL; - int argc, i, params_ok = 1; + int argc, i, params_ok = 1, init_size = 0; /* Get the argument count and check it */ argc = ZEND_NUM_ARGS(); @@ -2522,6 +2587,12 @@ static void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) if (Z_TYPE_PP(args[i]) != IS_ARRAY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1); params_ok = 0; + } else { + int num = zend_hash_num_elements(Z_ARRVAL_PP(args[i])); + + if (num > init_size) { + init_size = num; + } } } if (params_ok == 0) { @@ -2529,12 +2600,16 @@ static void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) return; } - array_init(return_value); + array_init_size(return_value, init_size); - for (i=0; i 0) { /* First array will be copied directly instead */ + php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]) TSRMLS_CC); + } else { + zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1); + } } efree(args); @@ -2545,7 +2620,7 @@ static void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) Merges elements from passed arrays into one array */ PHP_FUNCTION(array_merge) { - php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0); } /* }}} */ @@ -2553,7 +2628,23 @@ PHP_FUNCTION(array_merge) Recursively merges elements from passed arrays into one array */ PHP_FUNCTION(array_merge_recursive) { - php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0); +} +/* }}} */ + +/* {{{ proto array array_replace(array arr1, array arr2 [, array ...]) U + Replaces elements from passed arrays into one array */ +PHP_FUNCTION(array_replace) +{ + php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1); +} +/* }}} */ + +/* {{{ proto array array_replace_recursive(array arr1, array arr2 [, array ...]) U + Recursively replaces elements from passed arrays into one array */ +PHP_FUNCTION(array_replace_recursive) +{ + php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 1); } /* }}} */ diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index a05d45ddab..e29e7caff7 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -458,6 +458,20 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_array_merge_recursive, 0, 0, 2) ZEND_ARG_INFO(0, ...) /* ARRAY_INFO(0, arg, 0) */ ZEND_END_ARG_INFO() +static +ZEND_BEGIN_ARG_INFO_EX(arginfo_array_replace, 0, 0, 2) + ZEND_ARG_INFO(0, arr1) /* ARRAY_INFO(0, arg, 0) */ + ZEND_ARG_INFO(0, arr2) /* ARRAY_INFO(0, arg, 0) */ + ZEND_ARG_INFO(0, ...) /* ARRAY_INFO(0, ..., 0) */ +ZEND_END_ARG_INFO() + +static +ZEND_BEGIN_ARG_INFO_EX(arginfo_array_replace_recursive, 0, 0, 2) + ZEND_ARG_INFO(0, arr1) /* ARRAY_INFO(0, arg, 0) */ + ZEND_ARG_INFO(0, arr2) /* ARRAY_INFO(0, arg, 0) */ + ZEND_ARG_INFO(0, ...) /* ARRAY_INFO(0, arg, 0) */ +ZEND_END_ARG_INFO() + static ZEND_BEGIN_ARG_INFO_EX(arginfo_array_keys, 0, 0, 1) ZEND_ARG_INFO(0, arg) /* ARRAY_INFO(0, arg, 0) */ @@ -3731,6 +3745,8 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(array_slice, arginfo_array_slice) PHP_FE(array_merge, arginfo_array_merge) PHP_FE(array_merge_recursive, arginfo_array_merge_recursive) + PHP_FE(array_replace, arginfo_array_replace) + PHP_FE(array_replace_recursive, arginfo_array_replace_recursive) PHP_FE(array_keys, arginfo_array_keys) PHP_FE(array_values, arginfo_array_values) PHP_FE(array_count_values, arginfo_array_count_values) diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index 85a0f43449..2a3764a5d2 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -66,6 +66,8 @@ PHP_FUNCTION(array_splice); PHP_FUNCTION(array_slice); PHP_FUNCTION(array_merge); PHP_FUNCTION(array_merge_recursive); +PHP_FUNCTION(array_replace); +PHP_FUNCTION(array_replace_recursive); PHP_FUNCTION(array_keys); PHP_FUNCTION(array_values); PHP_FUNCTION(array_count_values); @@ -102,6 +104,7 @@ 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_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC); PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC); PHPAPI int php_array_data_compare(const void *a, const void *b TSRMLS_DC); PHPAPI void php_set_compare_func(int sort_type TSRMLS_DC); diff --git a/ext/standard/tests/array_replace.phpt b/ext/standard/tests/array_replace.phpt new file mode 100644 index 0000000000..32cda7a4ff --- /dev/null +++ b/ext/standard/tests/array_replace.phpt @@ -0,0 +1,122 @@ +--TEST-- +Test array_replace and array_replace_recursive +--FILE-- + 'dontclobber', + '1' => 'unclobbered', + 'test2' => 0.0, + 'test3' => array( + 'testarray2' => true, + 1 => array( + 'testsubarray1' => 'dontclobber2', + 'testsubarray2' => 'dontclobber3', + ), + ), +); + +$array2 = array( + 1 => 'clobbered', + 'test3' => array( + 'testarray2' => false, + ), + 'test4' => array( + 'clobbered3' => array(0, 1, 2), + ), +); + +$array3 = array(array(array(array()))); + +$array4 = array(); +$array4[] = &$array4; + +echo " -- Testing array_replace() --\n"; +$data = array_replace($array1, $array2); + +var_dump($data); + +echo " -- Testing array_replace_recursive() --\n"; +$data = array_replace_recursive($array1, $array2); + +var_dump($data); + +echo " -- Testing array_replace_recursive() w/ endless recusrsion --\n"; +$data = array_replace_recursive($array3, $array4); + +var_dump($data); +?> +--EXPECTF-- + -- Testing array_replace() -- +array(5) { + [0]=> + unicode(11) "dontclobber" + [1]=> + unicode(9) "clobbered" + [u"test2"]=> + float(0) + [u"test3"]=> + array(1) { + [u"testarray2"]=> + bool(false) + } + [u"test4"]=> + array(1) { + [u"clobbered3"]=> + array(3) { + [0]=> + int(0) + [1]=> + int(1) + [2]=> + int(2) + } + } +} + -- Testing array_replace_recursive() -- +array(5) { + [0]=> + unicode(11) "dontclobber" + [1]=> + unicode(9) "clobbered" + [u"test2"]=> + float(0) + [u"test3"]=> + array(2) { + [u"testarray2"]=> + bool(false) + [1]=> + array(2) { + [u"testsubarray1"]=> + unicode(12) "dontclobber2" + [u"testsubarray2"]=> + unicode(12) "dontclobber3" + } + } + [u"test4"]=> + array(1) { + [u"clobbered3"]=> + array(3) { + [0]=> + int(0) + [1]=> + int(1) + [2]=> + int(2) + } + } +} + -- Testing array_replace_recursive() w/ endless recusrsion -- + +Warning: array_replace_recursive(): recursion detected in %s on line %d +array(1) { + [0]=> + array(1) { + [0]=> + array(1) { + [0]=> + array(0) { + } + } + } +}