diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index 2ccbf94..33ba99e 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -370,16 +370,33 @@ protected function createExtensionChangelist() { return $module_data[$module]->sort; }, $module_list); - // Work out what modules to install and uninstall. + // Determine which modules to uninstall. $uninstall = array_diff(array_keys($current_extensions['module']), array_keys($new_extensions['module'])); - $install = array_diff(array_keys($new_extensions['module']), array_keys($current_extensions['module'])); - // Sort the module list by their weights. So that dependencies - // are uninstalled last. - asort($module_list); + // Sort the list of newly uninstalled extensions by their weights, so that + // dependencies are uninstalled last. Extensions of the same weight are + // sorted in reverse alphabetical order, to ensure the order is exactly + // opposite from installation. For example, this module list: + // array( + // 'actions' => 0, + // 'ban' => 0, + // 'options' => -2, + // 'text' => -1, + // ); + // will result in the following sort order: + // 0 options + // 1 text + // 2 0 ban + // 2 1 actions + // @todo Move this sorting functionality to the extension system. + array_multisort(array_values($module_list), SORT_ASC, array_keys($module_list), SORT_DESC, $module_list); $uninstall = array_intersect(array_keys($module_list), $uninstall); - // Sort the module list by their weights (reverse). So that dependencies - // are installed first. - arsort($module_list); + + // Determine which modules to install. + $install = array_diff(array_keys($new_extensions['module']), array_keys($current_extensions['module'])); + // Ensure that installed modules are sorted in exactly the reverse order + // (with dependencies installed first, and modules of the same name sorted + // in alphabetical order). + $module_list = array_reverse($module_list); $install = array_intersect(array_keys($module_list), $install); // Work out what themes to enable and to disable. diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php index 53954c7..92b7656 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php @@ -152,8 +152,8 @@ function testImport() { // Ensure installations and uninstallation occur as expected. $installed = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_installed', array()); $uninstalled = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_uninstalled', array()); - $expected = array('ban', 'action', 'text', 'options'); - $this->assertIdentical($expected, $installed, 'Ban, Action, Text and Options modules installed in the correct order.'); + $expected = array('action', 'ban', 'text', 'options'); + $this->assertIdentical($expected, $installed, 'Action, Ban, Text and Options modules installed in the correct order.'); $this->assertTrue(empty($uninstalled), 'No modules uninstalled during import'); // Verify that the action.settings configuration object was only written @@ -207,7 +207,7 @@ function testImport() { $installed = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_installed', array()); $uninstalled = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_uninstalled', array()); $expected = array('options', 'text', 'ban', 'action'); - $this->assertIdentical($expected, $uninstalled, 'Options, Text, Action and Ban modules uninstalled in the correct order.'); + $this->assertIdentical($expected, $uninstalled, 'Options, Text, Ban and Action modules uninstalled in the correct order.'); $this->assertTrue(empty($installed), 'No modules installed during import'); $theme_info = \Drupal::service('theme_handler')->listInfo();