]> granicus.if.org Git - php/commitdiff
- Add array_replace/array_replace_recursive (Mett Wilmas)
authorJohannes Schlüter <johannes@php.net>
Thu, 31 Jul 2008 20:17:07 +0000 (20:17 +0000)
committerJohannes Schlüter <johannes@php.net>
Thu, 31 Jul 2008 20:17:07 +0000 (20:17 +0000)
ext/standard/array.c
ext/standard/basic_functions.c
ext/standard/php_array.h
ext/standard/tests/array_replace.phpt [new file with mode: 0644]

index ebbbd9a545d96d89299fc4a7cd609775b881cf1c..6b9021d4da06daf5aeb72e6096fd0bf2d95dfa3d 100644 (file)
@@ -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<argc; i++) {
-               SEPARATE_ZVAL(args[i]);
-               convert_to_array_ex(args[i]);
-               php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), recursive TSRMLS_CC);
+       for (i = 0; i < argc; i++) {
+               if (!replace) {
+                       php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), 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_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);
 }
 /* }}} */
 
index a05d45ddabdeca95ae502c1136349da6440d70e3..e29e7caff7b1d1ee2d8f0f4247e5fb53fd864dc5 100644 (file)
@@ -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)
index 85a0f4344985e9d505e755378e1565c3bab6858a..2a3764a5d2263ea0db8d34cb9b4d5598ae8f790f 100644 (file)
@@ -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 (file)
index 0000000..32cda7a
--- /dev/null
@@ -0,0 +1,122 @@
+--TEST--
+Test array_replace and array_replace_recursive
+--FILE--
+<?php
+
+$array1 = array(
+       0 => '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) {
+      }
+    }
+  }
+}