Index: includes/ajax.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/ajax.inc,v
retrieving revision 1.31
diff -u -p -r1.31 ajax.inc
--- includes/ajax.inc	30 Apr 2010 08:07:54 -0000	1.31
+++ includes/ajax.inc	7 May 2010 18:05:28 -0000
@@ -197,7 +197,7 @@ function ajax_render($commands = array()
   // them the first command.
   $scripts = drupal_add_js(NULL, NULL);
   if (!empty($scripts['settings'])) {
-    array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $scripts['settings']['data'])));
+    array_unshift($commands, ajax_command_settings(call_user_func_array('drupal_array_merge_recursive', $scripts['settings']['data'])));
   }
 
   // Allow modules to alter any AJAX response.
Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.385
diff -u -p -r1.385 bootstrap.inc
--- includes/bootstrap.inc	6 May 2010 05:59:30 -0000	1.385
+++ includes/bootstrap.inc	7 May 2010 18:05:28 -0000
@@ -1783,6 +1783,54 @@ function drupal_hash_base64($data) {
 }
 
 /**
+ * Merges multiple arrays, recursively, and returns the merged array.
+ *
+ * This function is similar to PHP's array_merge_recursive() function, but it
+ * handles non-array values differently. When merging values that are not both
+ * arrays, the latter value replaces the former rather than merging with it.
+ *
+ * Example:
+ * @code
+ * $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => t('X'), 'class' => array('a', 'b')));
+ * $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('c', 'd')));
+ *
+ * // This results in array('fragment' => array('x', 'y'), 'attributes' => array('title' => array(t('X'), t('Y')), 'class' => array('a', 'b', 'c', 'd'))).
+ * $incorrect = array_merge_recursive($link_options_1, $link_options_2);
+ *
+ * // This results in array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('a', 'b', 'c', 'd'))).
+ * $correct = drupal_array_merge_recursive($link_options_1, $link_options_2);
+ * @endcode
+ *
+ * @param ...
+ *   Arrays to merge.
+ *
+ * @return
+ *   The merged array.
+ */
+function drupal_array_merge_recursive() {
+  $result = array();
+  foreach (func_get_args() as $arg) {
+    foreach ($arg as $key => $value) {
+      // Renumber integer keys as array_merge_recursive() does. Note that PHP
+      // automatically converts array keys that are integer strings (e.g., '1')
+      // to integers.
+      if (is_integer($key)) {
+        $result[] = $value;
+      }
+      // Recurse when both values are arrays.
+      elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
+        $result[$key] = drupal_array_merge_recursive($result[$key], $value);
+      }
+      // Otherwise, use the latter value, overriding any previous value.
+      else {
+        $result[$key] = $value;
+      }
+    }
+  }
+  return $result;
+}
+
+/**
  * Generates a default anonymous $user object.
  *
  * @return Object - the user object.
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1160
diff -u -p -r1.1160 common.inc
--- includes/common.inc	4 May 2010 14:55:22 -0000	1.1160
+++ includes/common.inc	7 May 2010 18:05:29 -0000
@@ -3715,7 +3715,7 @@ function drupal_get_js($scope = 'header'
       case 'setting':
         $js_element = $element;
         $js_element['#value_prefix'] = $embed_prefix;
-        $js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(call_user_func_array('array_merge_recursive', $item['data'])) . ");";
+        $js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(call_user_func_array('drupal_array_merge_recursive', $item['data'])) . ");";
         $js_element['#value_suffix'] = $embed_suffix;
         $output .= theme('html_tag', array('element' => $js_element));
         break;
Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.191
diff -u -p -r1.191 module.inc
--- includes/module.inc	28 Apr 2010 12:36:26 -0000	1.191
+++ includes/module.inc	7 May 2010 18:05:30 -0000
@@ -641,7 +641,7 @@ function module_hook_info() {
         if (function_exists($function)) {
           $result = $function();
           if (isset($result) && is_array($result)) {
-            $hook_info = array_merge_recursive($hook_info, $result);
+            $hook_info = drupal_array_merge_recursive($hook_info, $result);
           }
         }
       }
@@ -720,7 +720,7 @@ function module_invoke_all() {
     if (function_exists($function)) {
       $result = call_user_func_array($function, $args);
       if (isset($result) && is_array($result)) {
-        $return = array_merge_recursive($return, $result);
+        $return = drupal_array_merge_recursive($return, $result);
       }
       elseif (isset($result)) {
         $return[] = $result;
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.597
diff -u -p -r1.597 theme.inc
--- includes/theme.inc	6 May 2010 05:59:30 -0000	1.597
+++ includes/theme.inc	7 May 2010 18:05:31 -0000
@@ -2552,8 +2552,7 @@ function template_process_username(&$var
     // $variables['link_attributes'] contains attributes that should only be
     // applied if a link is being rendered. Preprocess functions are encouraged
     // to use the former unless they want to add attributes on the link only.
-    // If a link is being rendered, these need to be merged. Some attributes are
-    // themselves arrays, so the merging needs to be recursive.
-    $variables['link_options']['attributes'] = array_merge_recursive($variables['link_attributes'], $variables['attributes_array']);
+    // If a link is being rendered, these need to be merged.
+    $variables['link_options']['attributes'] = drupal_array_merge_recursive($variables['link_attributes'], $variables['attributes_array']);
   }
 }
Index: includes/update.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/update.inc,v
retrieving revision 1.47
diff -u -p -r1.47 update.inc
--- includes/update.inc	1 May 2010 08:12:22 -0000	1.47
+++ includes/update.inc	7 May 2010 18:05:31 -0000
@@ -1176,13 +1176,12 @@ function update_retrieve_dependencies() 
     if (function_exists($function)) {
       $result = $function();
       // Each implementation of hook_update_dependencies() returns a
-      // multidimensional, associative array containing some keys that
-      // represent module names (which are strings) and other keys that
-      // represent update function numbers (which are integers). We cannot use
-      // array_merge_recursive() to properly merge these results, since it
-      // treats strings and integers differently. Therefore, we have to
-      // explicitly loop through the expected array structure here and perform
-      // the merge manually.
+      // multidimensional, associative array containing some keys that represent
+      // module names (which are strings) and other keys that represent update
+      // function numbers (which are integers). We cannot use
+      // drupal_array_merge_recursive(), because we want the largest value for
+      // each set of keys, and we do not want numeric keys to be renumbered.
+      // Therefore, we explicitly traverse the structure.
       if (isset($result) && is_array($result)) {
         foreach ($result as $module => $module_data) {
           foreach ($module_data as $update => $update_data) {
Index: modules/file/file.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/file.module,v
retrieving revision 1.27
diff -u -p -r1.27 file.module
--- modules/file/file.module	1 May 2010 08:12:23 -0000	1.27
+++ modules/file/file.module	7 May 2010 18:05:34 -0000
@@ -257,7 +257,7 @@ function file_ajax_upload() {
 
   $output = theme('status_messages') . drupal_render($form);
   $js = drupal_add_js();
-  $settings = call_user_func_array('array_merge_recursive', $js['settings']['data']);
+  $settings = call_user_func_array('drupal_array_merge_recursive', $js['settings']['data']);
 
   $commands = array();
   $commands[] = ajax_command_replace(NULL, $output, $settings);
Index: modules/node/node.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.pages.inc,v
retrieving revision 1.125
diff -u -p -r1.125 node.pages.inc
--- modules/node/node.pages.inc	6 May 2010 05:59:31 -0000	1.125
+++ modules/node/node.pages.inc	7 May 2010 18:05:35 -0000
@@ -135,7 +135,7 @@ function node_form($form, &$form_state, 
   );
   // Get the node-specific bits.
   if ($extra = node_invoke($node, 'form', $form_state)) {
-    $form = array_merge_recursive($form, $extra);
+    $form = drupal_array_merge_recursive($form, $extra);
   }
   if (!isset($form['title']['#weight'])) {
     $form['title']['#weight'] = -5;
Index: modules/rdf/rdf.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/rdf/rdf.module,v
retrieving revision 1.40
diff -u -p -r1.40 rdf.module
--- modules/rdf/rdf.module	5 May 2010 15:49:04 -0000	1.40
+++ modules/rdf/rdf.module	7 May 2010 18:05:36 -0000
@@ -95,22 +95,22 @@ function rdf_rdf_namespaces() {
  * Returns an array of RDF namespaces defined via hook_rdf_namespaces().
  */
 function rdf_get_namespaces() {
-  $rdf_namespaces = module_invoke_all('rdf_namespaces');
-  // module_invoke_all() uses array_merge_recursive() which might return nested
-  // arrays if several modules redefine the same prefix multiple times.
-  // We need to ensure the array of namespaces is flat and only contains
-  // strings as URIs.
-  foreach ($rdf_namespaces as $prefix => $uri) {
-    if (is_array($uri)) {
-      if (count(array_unique($uri)) == 1) {
-        // All namespaces declared for this prefix are the same, merge them all
-        // into a single namespace.
-        $rdf_namespaces[$prefix] = $uri[0];
-      }
-      else {
-        // There are conflicting namespaces for this prefix, do not include it
-        // in order to avoid asserting any inaccurate RDF statement.
-        unset($rdf_namespaces[$prefix]);
+  // Do the equivalent of module_invoke_all('rdf_namespaces'), but exclude
+  // prefixes for which different modules define conflicting URIs.
+  $rdf_namespaces = array();
+  $conflicts = array();
+  foreach (module_implements('rdf_namespaces') as $module) {
+    $function = $module . '_rdf_namespaces';
+    $result = $function();
+    if (is_array($result)) {
+      foreach ($result as $prefix => $uri) {
+        if (isset($rdf_namespaces[$prefix]) && $rdf_namespaces[$prefix] !== $uri) {
+          $conflicts[$prefix] = TRUE;
+          unset($rdf_namespaces[$prefix]);
+        }
+        if (!isset($conflicts[$prefix])) {
+          $rdf_namespaces[$prefix] = $uri;
+        }
       }
     }
   }
@@ -648,7 +648,7 @@ function rdf_preprocess_username(&$varia
   // (http://www.w3.org/TR/rdfa-syntax/#rdfa-attributes).
   // Therefore, merge rather than override so as not to clobber values set by
   // earlier preprocess functions.
-  $variables['attributes_array'] = array_merge_recursive($variables['attributes_array'], $attributes);
+  $variables['attributes_array'] = drupal_array_merge_recursive($variables['attributes_array'], $attributes);
 }
 
 /**
Index: modules/simpletest/tests/ajax.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/ajax.test,v
retrieving revision 1.11
diff -u -p -r1.11 ajax.test
--- modules/simpletest/tests/ajax.test	30 Apr 2010 08:07:55 -0000	1.11
+++ modules/simpletest/tests/ajax.test	7 May 2010 18:05:36 -0000
@@ -286,7 +286,7 @@ class AJAXMultiFormTestCase extends AJAX
         $button = $this->xpath($field_xpath . $button_xpath_suffix);
         $button_id = (string) $button[0]['id'];
         $commands = $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_id, $settings['ajax'][$button_id]);
-        $settings = array_merge_recursive($settings, $commands[0]['settings']);
+        $settings = drupal_array_merge_recursive($settings, $commands[0]['settings']);
         $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.'));
         $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.'));
         $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other');
Index: modules/simpletest/tests/bootstrap.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/bootstrap.test,v
retrieving revision 1.29
diff -u -p -r1.29 bootstrap.test
--- modules/simpletest/tests/bootstrap.test	17 Mar 2010 13:58:45 -0000	1.29
+++ modules/simpletest/tests/bootstrap.test	7 May 2010 18:05:37 -0000
@@ -414,3 +414,28 @@ class BootstrapResettableStaticTestCase 
     $this->assertEqual($var, 'foo', t('Variable was reset after second invocation of global reset.'));
   }
 }
+
+/**
+ * Test miscellaneous functions in bootstrap.inc.
+ */
+class BootstrapMiscTestCase extends DrupalUnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Miscellaneous bootstrap unit tests',
+      'description' => 'Test miscellaneous functions in bootstrap.inc.',
+      'group' => 'Bootstrap',
+    );
+  }
+
+  /**
+   * Test miscellaneous functions in bootstrap.inc.
+   */
+  function testMisc() {
+    // Test drupal_array_merge_recursive().
+    $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => 'X', 'class' => array('a', 'b')), 'language' => 'en');
+    $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('c', 'd')), 'html' => TRUE);
+    $expected = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('a', 'b', 'c', 'd')), 'language' => 'en', 'html' => TRUE);
+    $this->assertIdentical(drupal_array_merge_recursive($link_options_1, $link_options_2), $expected, t('drupal_array_merge_recursive() returned a properly merged array.'));
+  }
+}
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.927
diff -u -p -r1.927 system.module
--- modules/system/system.module	6 May 2010 15:21:25 -0000	1.927
+++ modules/system/system.module	7 May 2010 18:05:38 -0000
@@ -3489,7 +3489,7 @@ function _system_date_formats_build() {
 
     // If another module already set this format, merge in the new settings.
     if (isset($date_formats[$module_format['type']][$module_format['format']])) {
-      $date_formats[$module_format['type']][$module_format['format']] = array_merge_recursive($date_formats[$module_format['type']][$module_format['format']], $format);
+      $date_formats[$module_format['type']][$module_format['format']] = drupal_array_merge_recursive($date_formats[$module_format['type']][$module_format['format']], $format);
     }
     else {
       // This setting will be overridden later if it already exists in the db.
