From f72f814f7b077e12bfb436f8a092c19e81763dca Mon Sep 17 00:00:00 2001 From: Dries <dries@buytaert.net> Date: Thu, 3 May 2012 11:09:39 -0400 Subject: [PATCH] - Patch #1404198 by sun, beejeebus, fago, chx: Separate database cache clearing from static cache clearing and data structure rebuilding. --- core/includes/common.inc | 132 +++++++++++++----- core/includes/gettext.inc | 2 +- core/includes/install.core.inc | 24 ++-- core/includes/lock.inc | 7 +- core/includes/menu.inc | 12 +- core/includes/module.inc | 21 ++- core/includes/theme.inc | 4 +- core/modules/aggregator/aggregator.test | 2 +- core/modules/block/block.module | 13 +- core/modules/block/block.test | 4 +- core/modules/comment/comment.test | 1 - core/modules/entity/tests/entity.test | 2 +- core/modules/field/field.module | 15 +- core/modules/field_ui/field_ui.module | 2 +- core/modules/filter/filter.module | 7 + core/modules/menu/menu.install | 2 +- core/modules/menu/menu.module | 2 +- core/modules/node/content_types.inc | 4 +- core/modules/node/node.module | 10 +- core/modules/search/search.test | 4 +- core/modules/shortcut/shortcut.install | 2 +- .../simpletest/drupal_web_test_case.php | 20 +-- core/modules/system/system.admin.inc | 13 +- core/modules/system/system.api.php | 55 +++++++- core/modules/system/system.module | 19 ++- core/modules/system/system.test | 10 +- core/modules/system/tests/cache.test | 5 +- core/modules/system/tests/menu.test | 2 +- .../update_script_test.module | 6 +- core/modules/update/update.module | 4 +- profiles/standard/standard.install | 2 +- 31 files changed, 275 insertions(+), 133 deletions(-) diff --git a/core/includes/common.inc b/core/includes/common.inc index 673ac9cfdcc1..f3cdd6982319 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -7267,50 +7267,116 @@ function drupal_implode_tags($tags) { } /** - * Flushes all cached data on the site. - * - * Empties cache tables, rebuilds the menu cache and theme registries, and - * invokes a hook so that other modules' cache data can be cleared as well. + * Flushes all persistent caches, resets all variables, and rebuilds all data structures. + * + * At times, it is necessary to re-initialize the entire system to account for + * changed or new code. This function: + * - Clears all persistent caches (invoking hook_cache_flush()), which always + * includes: + * - The bootstrap cache bin containing base system, module system, and theme + * system information. + * - The common 'cache' cache bin containing arbitrary caches. + * - The page cache. + * - The URL alias path cache. + * - Resets all static variables that have been defined via drupal_static(). + * - Clears asset (JS/CSS) file caches. + * - Updates the system with latest information about extensions (modules and + * themes). + * - Updates the bootstrap flag for modules implementing bootstrap_hooks(). + * - Rebuilds the full database schema information (invoking hook_schema()). + * - Rebuilds data structures of all modules (invoking hook_rebuild()). In + * core this means + * - blocks, node types, date formats and actions are synchronized with the + * database + * - The 'active' status of fields is refreshed. + * - Rebuilds the menu router. + * + * This means the entire system is reset so all caches and static variables are + * effectively empty. After that is guaranteed, information about the currently + * active code is updated, and rebuild operations are successively called in + * order to synchronize the active system according to the current information + * defined in code. + * + * All modules need to ensure that all of their caches are flushed when + * hook_cache_flush() is invoked; any previously known information must no + * longer exist. All following hook_rebuild() operations must be based on fresh + * and current system data. All modules must be able to rely on this contract. + * + * @see hook_cache_flush() + * @see hook_rebuild() + * + * This function also resets the theme, which means it is not initialized + * anymore and all previously added JavaScript and CSS is gone. Normally, this + * function is called as an end-of-POST-request operation that is followed by a + * redirect, so this effect is not visible. Since the full reset is the whole + * point of this function, callers need to take care for backing up all needed + * variables and properly restoring or re-initializing them on their own. For + * convenience, this function automatically re-initializes the maintenance theme + * if it was initialized before. + * + * @todo Try to clear page/JS/CSS caches last, so cached pages can still be + * served during this possibly long-running operation. (Conflict on bootstrap + * cache though.) + * @todo Add a global lock to ensure that caches are not primed in concurrent + * requests. */ function drupal_flush_all_caches() { - // Change query-strings on css/js files to enforce reload for all users. - _drupal_flush_css_js(); + // Flush all persistent caches. + // This is executed based on old/previously known information, which is + // sufficient, since new extensions cannot have any primed caches yet. + foreach (module_invoke_all('cache_flush') as $bin) { + cache($bin)->flush(); + } - registry_rebuild(); + // Flush asset file caches. drupal_clear_css_cache(); drupal_clear_js_cache(); + _drupal_flush_css_js(); - // Rebuild the theme data. Note that the module data is rebuilt above, as - // part of registry_rebuild(). - system_rebuild_theme_data(); - drupal_theme_rebuild(); - - entity_info_cache_clear(); + // Reset all static caches. + drupal_static_reset(); - // @todo D8: Split cache flushing from rebuilding. - // @see http://drupal.org/node/996236 - if (module_exists('node')) { - node_types_rebuild(); - } - // node_menu() defines menu items based on node types so it needs to come - // after node types are rebuilt. - menu_rebuild(); + // Clear all non-drupal_static() static caches. + // None currently; kept if any static caches need to be reset in the future. - // Synchronize to catch any actions that were added or removed. - actions_synchronize(); + // Update and synchronize the class registry and extension information based + // on current/actual code. + // Module data is rebuilt as part of registry_rebuild(). + registry_rebuild(); + system_rebuild_theme_data(); - // Don't clear cache_form - in-progress form submissions may break. - // Ordered so clearing the page cache will always be the last action. - $core = array('cache', 'path', 'filter', 'bootstrap', 'page'); - $cache_bins = array_merge(module_invoke_all('flush_caches'), $core); - foreach ($cache_bins as $bin) { - cache($bin)->flush(); - } + // Ensure that all modules that are currently supposed to be enabled are + // actually loaded. + module_load_all(); - // Rebuild the bootstrap module list. We do this here so that developers - // can get new hook_boot() implementations registered without having to - // write a hook_update_N() function. + // Update the list of bootstrap modules. + // Allows developers to get new hook_boot() implementations registered without + // having to write a hook_update_N() function. _system_update_bootstrap_status(); + + // Rebuild the schema and cache a fully-built schema based on new module data. + // This is necessary for any invocation of index.php, because setting cache + // table entries requires schema information and that occurs during bootstrap + // before any modules are loaded, so if there is no cached schema, + // drupal_get_schema() will try to generate one, but with no loaded modules, + // it will return nothing. + drupal_get_schema(NULL, TRUE); + + // Rebuild all information based on new module data. + module_invoke_all('rebuild'); + + // Rebuild the menu router based on all rebuilt data. + // Important: This rebuild must happen last, so the menu router is guaranteed + // to be based on up to date information. + menu_router_rebuild(); + + // Re-initialize the maintenance theme, if the current request attempted to + // use it. Unlike regular usages of this function, the installer and update + // scripts need to flush all caches during GET requests/page building. + if (function_exists('_drupal_maintenance_theme')) { + unset($GLOBALS['theme']); + drupal_maintenance_theme(); + } } /** diff --git a/core/includes/gettext.inc b/core/includes/gettext.inc index a8498dc22bc8..2355157cb889 100644 --- a/core/includes/gettext.inc +++ b/core/includes/gettext.inc @@ -61,7 +61,7 @@ function _locale_import_po($file, $langcode, $overwrite_options, $customized = L cache()->deletePrefix('locale:'); // Rebuild the menu, strings may have changed. - menu_rebuild(); + menu_router_rebuild(); drupal_set_message(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => $additions, '%update' => $updates, '%delete' => $deletes))); watchdog('locale', 'Imported %file into %locale: %number new strings added, %update updated and %delete removed.', array('%file' => $file->filename, '%locale' => $langcode, '%number' => $additions, '%update' => $updates, '%delete' => $deletes)); diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 5cb3399a094a..76ce3e679680 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1567,28 +1567,26 @@ function install_import_translations_remaining(&$install_state) { * A message informing the user that the installation is complete. */ function install_finished(&$install_state) { - drupal_set_title(st('@drupal installation complete', array('@drupal' => drupal_install_profile_distribution_name())), PASS_THROUGH); - $messages = drupal_set_message(); - $output = '<p>' . st('Congratulations, you installed @drupal!', array('@drupal' => drupal_install_profile_distribution_name())) . '</p>'; - $output .= '<p>' . (isset($messages['error']) ? st('Review the messages above before visiting <a href="@url">your new site</a>.', array('@url' => url(''))) : st('<a href="@url">Visit your new site</a>.', array('@url' => url('')))) . '</p>'; - - // Flush all caches to ensure that any full bootstraps during the installer - // do not leave stale cached data, and that any content types or other items - // registered by the install profile are registered correctly. - drupal_flush_all_caches(); - // Remember the profile which was used. variable_set('install_profile', drupal_get_profile()); - // Install profiles are always loaded last + // Install profiles are always loaded last. db_update('system') ->fields(array('weight' => 1000)) ->condition('type', 'module') ->condition('name', drupal_get_profile()) ->execute(); - // Cache a fully-built schema. - drupal_get_schema(NULL, TRUE); + // Flush all caches to ensure that any full bootstraps during the installer + // do not leave stale cached data, and that any content types or other items + // registered by the install profile are registered correctly. + drupal_flush_all_caches(); + + drupal_set_title(st('@drupal installation complete', array('@drupal' => drupal_install_profile_distribution_name())), PASS_THROUGH); + + $messages = drupal_set_message(); + $output = '<p>' . st('Congratulations, you installed @drupal!', array('@drupal' => drupal_install_profile_distribution_name())) . '</p>'; + $output .= '<p>' . (isset($messages['error']) ? st('Review the messages above before visiting <a href="@url">your new site</a>.', array('@url' => url(''))) : st('<a href="@url">Visit your new site</a>.', array('@url' => url('')))) . '</p>'; // Run cron to populate update status tables (if available) so that users // will be warned if they've installed an out of date Drupal version. diff --git a/core/includes/lock.inc b/core/includes/lock.inc index 7ef199e1d0ce..fd794aee8d1d 100644 --- a/core/includes/lock.inc +++ b/core/includes/lock.inc @@ -13,9 +13,10 @@ * In most environments, multiple Drupal page requests (a.k.a. threads or * processes) will execute in parallel. This leads to potential conflicts or * race conditions when two requests execute the same code at the same time. A - * common example of this is a rebuild like menu_rebuild() where we invoke many - * hook implementations to get and process data from all active modules, and - * then delete the current data in the database to insert the new afterwards. + * common example of this is a rebuild like menu_router_rebuild() where we + * invoke many hook implementations to get and process data from all active + * modules, and then delete the current data in the database to insert the new + * afterwards. * * This is a cooperative, advisory lock system. Any long-running operation * that could potentially be attempted in parallel by multiple requests should diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 2bfc39e6e89a..dc70185f4b91 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -301,7 +301,7 @@ * then the 1011 bitstring represents node/%/edit/foo where % means that * any argument matches that part. We limit ourselves to using binary * numbers that correspond the patterns of wildcards of router items that - * actually exists. This list of 'masks' is built in menu_rebuild(). + * actually exists. This list of 'masks' is built in menu_router_rebuild(). * * @param $parts * An array of path parts, for the above example @@ -450,7 +450,7 @@ function menu_get_item($path = NULL, $router_item = NULL) { // Rebuild if we know it's needed, or if the menu masks are missing which // occurs rarely, likely due to a race condition of multiple rebuilds. if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) { - menu_rebuild(); + menu_router_rebuild(); } $original_map = arg(NULL, $path); @@ -2658,12 +2658,12 @@ function menu_reset_static_cache() { * TRUE if the menu was rebuilt, FALSE if another thread was rebuilding * in parallel and the current thread just waited for completion. */ -function menu_rebuild() { - if (!lock_acquire('menu_rebuild')) { +function menu_router_rebuild() { + if (!lock_acquire(__FUNCTION__)) { // Wait for another request that is already doing this work. // We choose to block here since otherwise the router item may not // be available in menu_execute_active_handler() resulting in a 404. - lock_wait('menu_rebuild'); + lock_wait(__FUNCTION__); return FALSE; } @@ -2684,7 +2684,7 @@ function menu_rebuild() { watchdog_exception('menu', $e); } - lock_release('menu_rebuild'); + lock_release(__FUNCTION__); return TRUE; } diff --git a/core/includes/module.inc b/core/includes/module.inc index 6192a38d1ae9..6b4604a7b21d 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -19,7 +19,15 @@ * have been loaded. */ function module_load_all($bootstrap = FALSE) { - static $has_run = FALSE; + // Already loaded code cannot be unloaded, but new modules may be added within + // a request, which should be loaded as well. + // Use the advanced drupal_static() pattern, since this is called very often. + // @see theme() + static $drupal_static_fast; + if (!isset($drupal_static_fast)) { + $drupal_static_fast['has_run'] = &drupal_static(__FUNCTION__, FALSE); + } + $has_run = &$drupal_static_fast['has_run']; if (isset($bootstrap)) { foreach (module_list(TRUE, $bootstrap) as $module) { @@ -66,7 +74,16 @@ function module_load_all($bootstrap = FALSE) { * the list. */ function module_list($refresh = FALSE, $bootstrap_refresh = FALSE, $sort = FALSE, $fixed_list = NULL) { - static $list = array(), $sorted_list; + // system_list() may be reset within a request, so the module list needs to be + // reset, too. + // Use the advanced drupal_static() pattern, since this is called very often. + static $drupal_static_fast; + if (!isset($drupal_static_fast)) { + $drupal_static_fast['list'] = &drupal_static(__FUNCTION__ . ':list', array()); + $drupal_static_fast['sorted_list'] = &drupal_static(__FUNCTION__ . ':sorted_list'); + } + $list = &$drupal_static_fast['list']; + $sorted_list = &$drupal_static_fast['sorted_list']; if (empty($list) || $refresh || $fixed_list) { $list = array(); diff --git a/core/includes/theme.inc b/core/includes/theme.inc index b145979875ee..2a88969f8c80 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1489,7 +1489,7 @@ function theme_enable($theme_list) { } list_themes(TRUE); - menu_rebuild(); + menu_router_rebuild(); drupal_theme_rebuild(); // Invoke hook_themes_enabled() after the themes have been enabled. @@ -1522,7 +1522,7 @@ function theme_disable($theme_list) { } list_themes(TRUE); - menu_rebuild(); + menu_router_rebuild(); drupal_theme_rebuild(); // Invoke hook_themes_disabled after the themes have been disabled. diff --git a/core/modules/aggregator/aggregator.test b/core/modules/aggregator/aggregator.test index 61ad16b2d817..ddfcc0775b5c 100644 --- a/core/modules/aggregator/aggregator.test +++ b/core/modules/aggregator/aggregator.test @@ -893,7 +893,7 @@ class AggregatorRenderingTestCase extends AggregatorTestCase { $feed->block = 0; aggregator_save_feed((array) $feed); // It is nescessary to flush the cache after saving the number of items. - drupal_flush_all_caches(); + $this->resetAll(); // Check that the block is no longer displayed. $this->drupalGet('node'); $this->assertNoText(t($block['title']), 'Feed block is not displayed on the page when number of items is set to 0.'); diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 2bfd7653b9c1..f7d8dc0c5b44 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -935,9 +935,16 @@ function _block_get_renderable_block($element) { } /** - * Implements hook_flush_caches(). + * Implements hook_cache_flush(). */ -function block_flush_caches() { +function block_cache_flush() { + return array('block'); +} + +/** + * Implements hook_rebuild(). + */ +function block_rebuild() { // Rehash blocks for active themes. We don't use list_themes() here, // because if MAINTENANCE_MODE is defined it skips reading the database, // and we can't tell which themes are active. @@ -945,8 +952,6 @@ function block_flush_caches() { foreach ($themes as $theme) { _block_rehash($theme->name); } - - return array('block'); } /** diff --git a/core/modules/block/block.test b/core/modules/block/block.test index a5bde420ebcb..192e264531b3 100644 --- a/core/modules/block/block.test +++ b/core/modules/block/block.test @@ -391,7 +391,7 @@ class BlockTestCase extends DrupalWebTestCase { // Disable caching for this block. variable_set('block_test_caching', DRUPAL_NO_CACHE); // Flushing all caches should call _block_rehash(). - drupal_flush_all_caches(); + $this->resetAll(); // Verify that the database is updated with the new caching mode. $current_caching = db_query("SELECT cache FROM {block} WHERE module = 'block_test' AND delta = 'test_cache'")->fetchField(); $this->assertEqual($current_caching, DRUPAL_NO_CACHE, t("Test block's database entry updated to DRUPAL_NO_CACHE.")); @@ -833,7 +833,7 @@ class BlockHiddenRegionTestCase extends DrupalWebTestCase { $theme = 'block_test_theme'; theme_enable(array($theme)); variable_set('theme_default', $theme); - menu_rebuild(); + menu_router_rebuild(); // Ensure that "block_test_theme" is set as the default theme. $this->drupalGet('admin/structure/block'); diff --git a/core/modules/comment/comment.test b/core/modules/comment/comment.test index c41f9af7b8ed..3f5b0409f58f 100644 --- a/core/modules/comment/comment.test +++ b/core/modules/comment/comment.test @@ -2090,7 +2090,6 @@ class CommentFieldsTest extends CommentHelperCase { $edit['modules[Core][book][enable]'] = 'book'; $edit['modules[Core][poll][enable]'] = 'poll'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); - $this->resetAll(); // Now enable the comment module. $edit = array(); diff --git a/core/modules/entity/tests/entity.test b/core/modules/entity/tests/entity.test index cd9b879f7693..abc02ef27026 100644 --- a/core/modules/entity/tests/entity.test +++ b/core/modules/entity/tests/entity.test @@ -222,7 +222,7 @@ class EntityAPIInfoTestCase extends DrupalWebTestCase { // Change the label of the test entity type and make sure changes appear // after flushing caches. variable_set('entity_cache_test_label', 'New label.'); - drupal_flush_all_caches(); + $this->resetAll(); $info = entity_get_info('entity_cache_test'); $this->assertEqual($info['label'], 'New label.', 'New label appears in entity info.'); diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 1e8b7bbf15e3..0220a6ec9547 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -380,14 +380,19 @@ function field_system_info_alter(&$info, $file, $type) { } /** - * Implements hook_flush_caches(). + * Implements hook_cache_flush(). */ -function field_flush_caches() { +function field_cache_flush() { + // Request a flush of our cache table. + return array('field'); +} + +/** + * Implements hook_rebuild(). + */ +function field_rebuild() { // Refresh the 'active' status of fields. field_sync_field_status(); - - // Request a flush of our cache table. - return array('field'); } /** diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index c75005a2624b..b65cd3c5cf73 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -52,7 +52,7 @@ function field_ui_help($path, $arg) { function field_ui_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { // The Field UI relies on entity_get_info() to build menu items for entity // field administration pages. Ensure that the menu is rebuilt. - menu_rebuild(); + menu_router_rebuild(); } /** diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index 45f5170ce744..9f492fe3f16b 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -5,6 +5,13 @@ * Framework for handling filtering of content. */ +/** + * Implements hook_cache_flush(). + */ +function filter_cache_flush() { + return array('filter'); +} + /** * Implements hook_help(). */ diff --git a/core/modules/menu/menu.install b/core/modules/menu/menu.install index 05aed283fae4..d876017af653 100644 --- a/core/modules/menu/menu.install +++ b/core/modules/menu/menu.install @@ -66,6 +66,6 @@ function menu_install() { * Implements hook_uninstall(). */ function menu_uninstall() { - menu_rebuild(); + menu_router_rebuild(); } diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module index 5d55d066e8c8..81094d07c534 100644 --- a/core/modules/menu/menu.module +++ b/core/modules/menu/menu.module @@ -180,7 +180,7 @@ function menu_theme() { * Add a link for each custom menu. */ function menu_enable() { - menu_rebuild(); + menu_router_rebuild(); $base_link = db_query("SELECT mlid AS plid, menu_name FROM {menu_links} WHERE link_path = 'admin/structure/menu' AND module = 'system'")->fetchAssoc(); $base_link['router_path'] = 'admin/structure/menu/manage/%'; $base_link['module'] = 'menu'; diff --git a/core/modules/node/content_types.inc b/core/modules/node/content_types.inc index 2e45c9a7c754..9ca1e15a7ad7 100644 --- a/core/modules/node/content_types.inc +++ b/core/modules/node/content_types.inc @@ -355,7 +355,7 @@ function node_type_form_submit($form, &$form_state) { $status = node_type_save($type); node_types_rebuild(); - menu_rebuild(); + menu_router_rebuild(); $t_args = array('%name' => $type->name); if ($status == SAVED_UPDATED) { @@ -458,7 +458,7 @@ function node_type_delete_confirm_submit($form, &$form_state) { watchdog('menu', 'Deleted content type %name.', $t_args, WATCHDOG_NOTICE); node_types_rebuild(); - menu_rebuild(); + menu_router_rebuild(); $form_state['redirect'] = 'admin/structure/types'; return; diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 5e1c3b31c91f..9e21bfaa0e76 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -76,6 +76,13 @@ */ const NODE_ACCESS_IGNORE = NULL; +/** + * Implements hook_rebuild(). + */ +function node_rebuild() { + node_types_rebuild(); +} + /** * Implements hook_help(). */ @@ -1890,9 +1897,6 @@ function node_menu() { 'type' => MENU_CALLBACK, ); // @todo Remove this loop when we have a 'description callback' property. - // Resets the internal static cache of _node_types_build() and forces a - // rebuild of the node type information. - node_type_cache_reset(); foreach (node_type_get_types() as $type) { $type_url_str = str_replace('_', '-', $type->type); $items['node/add/' . $type_url_str] = array( diff --git a/core/modules/search/search.test b/core/modules/search/search.test index dd63ec488ca5..60854655c219 100644 --- a/core/modules/search/search.test +++ b/core/modules/search/search.test @@ -1225,7 +1225,7 @@ class SearchKeywordsConditions extends SearchWebTestCase { $this->drupalLogin($this->searching_user); // Test with all search modules enabled. variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); - menu_rebuild(); + menu_router_rebuild(); } /** @@ -1936,7 +1936,7 @@ class SearchPageOverride extends SearchWebTestCase { // Enable the extra type module for searching. variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); - menu_rebuild(); + menu_router_rebuild(); } function testSearchPageHook() { diff --git a/core/modules/shortcut/shortcut.install b/core/modules/shortcut/shortcut.install index 60c113aea38a..9ee9fd365056 100644 --- a/core/modules/shortcut/shortcut.install +++ b/core/modules/shortcut/shortcut.install @@ -36,7 +36,7 @@ function shortcut_install() { // Drupal is already installed (i.e., we are not in the installer). // @see http://drupal.org/node/1376150 if (variable_get('install_task', '') != 'done') { - menu_rebuild(); + menu_router_rebuild(); } shortcut_set_save($shortcut_set); } diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php index 15c165630216..6a0073dde932 100644 --- a/core/modules/simpletest/drupal_web_test_case.php +++ b/core/modules/simpletest/drupal_web_test_case.php @@ -1014,7 +1014,7 @@ protected function drupalCreateContentType($settings = array()) { $saved_type = node_type_save($type); node_types_rebuild(); - menu_rebuild(); + menu_router_rebuild(); node_add_body_field($type); $this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type))); @@ -1415,6 +1415,9 @@ protected function setUp() { // Create the database prefix for this test. $this->prepareDatabasePrefix(); + // Prepare the environment for running tests. + $this->prepareEnvironment(); + // Reset all statics and variables to perform tests in a clean environment. $conf = array(); drupal_static_reset(); @@ -1425,9 +1428,6 @@ protected function setUp() { // write back to persistent caches when they are destructed. $this->changeDatabasePrefix(); - // Prepare the environment for running tests. - $this->prepareEnvironment(); - // Preset the 'install_profile' system variable, so the first call into // system_rebuild_module_data() (in drupal_install_system()) will register // the test's profile as a module. Without this, the installation profile of @@ -1553,17 +1553,7 @@ protected function preloadRegistry() { * are enabled later. */ protected function resetAll() { - // Reset all static variables. - drupal_static_reset(); - // Reset the list of enabled modules. - module_list(TRUE); - - // Reset cached schema for new database prefix. This must be done before - // drupal_flush_all_caches() so rebuilds can make use of the schema of - // modules enabled on the cURL side. - drupal_get_schema(NULL, TRUE); - - // Perform rebuilds and flush remaining caches. + // Clear all database and static caches and rebuild data structures. drupal_flush_all_caches(); // Reload global $conf array and permissions. diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 9619461d3033..dcc289ac2877 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -327,12 +327,13 @@ function system_theme_default() { // Set the default theme. variable_set('theme_default', $theme); - // Rebuild the menu. This duplicates the menu_rebuild() in theme_enable(). - // However, modules must know the current default theme in order to use - // this information in hook_menu() or hook_menu_alter() implementations, - // and doing the variable_set() before the theme_enable() could result - // in a race condition where the theme is default but not enabled. - menu_rebuild(); + // Rebuild the menu. This duplicates the menu_router_rebuild() in + // theme_enable(). However, modules must know the current default theme in + // order to use this information in hook_menu() or hook_menu_alter() + // implementations, and doing the variable_set() before the theme_enable() + // could result in a race condition where the theme is default but not + // enabled. + menu_router_rebuild(); // The status message depends on whether an admin theme is currently in use: // a value of 0 means the admin theme is set to be the default theme. diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 15ffa2175695..74240154476f 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -2087,21 +2087,62 @@ function hook_mail($key, &$message, $params) { } /** - * Add a list of cache tables to be cleared. + * Flush all persistent and static caches. * - * This hook allows your module to add cache bins to the list of cache bins - * that will be cleared by the Clear button on the Performance page or - * whenever drupal_flush_all_caches is invoked. + * This hook asks your module to clear all of its persistent (database) and + * static caches, in order to ensure a clean environment for subsequently + * invoked data rebuilds. * - * @return - * An array of cache bins. + * Do NOT use this hook for rebuilding information. Only use it to flush custom + * caches and return the names of additional cache bins to flush. + * + * Static caches using drupal_static() do not need to be reset manually. + * However, all other static variables that do not use drupal_static() must be + * manually reset. + * + * This hook is invoked by drupal_flush_all_caches(). It runs before module data + * is updated and before hook_rebuild(). + * + * @return array + * An array of cache bins to be flushed. * * @see drupal_flush_all_caches() + * @see hook_rebuild() */ -function hook_flush_caches() { +function hook_cache_flush() { return array('example'); } +/** + * Rebuild data based upon refreshed caches. + * + * This hook allows your module to rebuild its data based on the latest/current + * module data. It runs after hook_cache_flush() and after all module data has + * been updated. + * + * This hook is only invoked after the system has been completely cleared; + * i.e., all previously cached data is known to be gone and every API in the + * system is known to return current information, so your module can safely rely + * on all available data to rebuild its own. + * + * The menu router is the only exception regarding rebuilt data; it is only + * rebuilt after all hook_rebuild() implementations have been invoked. That + * ensures that hook_menu() implementations and the final router rebuild can + * rely on all data being returned by all modules. + * + * @see hook_cache_flush() + * @see drupal_flush_all_caches() + */ +function hook_rebuild() { + // Rehash blocks for active themes. We don't use list_themes() here, + // because if MAINTENANCE_MODE is defined it skips reading the database, + // and we can't tell which themes are active. + $themes = db_query("SELECT name FROM {system} WHERE type = 'theme' AND status = 1"); + foreach ($themes as $theme) { + _block_rehash($theme->name); + } +} + /** * Perform necessary actions before modules are installed. * diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 80a84033fb9a..8af6cc3938f3 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -3054,8 +3054,7 @@ function system_cron() { } } - $core = array('cache', 'path', 'filter', 'page', 'form', 'menu'); - $cache_bins = array_merge(module_invoke_all('flush_caches'), $core); + $cache_bins = array_merge(module_invoke_all('cache_flush'), array('form', 'menu')); foreach ($cache_bins as $bin) { cache($bin)->expire(); } @@ -3081,13 +3080,21 @@ function system_cron() { } /** - * Implements hook_flush_caches(). + * Implements hook_cache_flush(). */ -function system_flush_caches() { +function system_cache_flush() { + // Do NOT flush the 'form' cache bin to retain in-progress form submissions. + return array('bootstrap', 'cache', 'page', 'path'); +} + +/** + * Implements hook_rebuild(). + */ +function system_rebuild() { // Rebuild list of date formats. system_date_formats_rebuild(); - // Reset the menu static caches. - menu_reset_static_cache(); + // Synchronize any actions that were added or removed. + actions_synchronize(); } /** diff --git a/core/modules/system/system.test b/core/modules/system/system.test index 4fdad7172e1d..1458ca742dfb 100644 --- a/core/modules/system/system.test +++ b/core/modules/system/system.test @@ -2184,7 +2184,7 @@ class SystemInfoAlterTestCase extends DrupalWebTestCase { // Enable our test module. Flush all caches, which we assert is the only // thing necessary to use the rebuilt {system}.info. module_enable(array('module_test'), FALSE); - drupal_flush_all_caches(); + $this->resetAll(); $this->assertTrue(module_exists('module_test'), t('Test module is enabled.')); $info = $this->getSystemInfo('seven', 'theme'); @@ -2199,7 +2199,7 @@ class SystemInfoAlterTestCase extends DrupalWebTestCase { // Disable the module and verify that {system}.info is rebuilt without it. module_disable(array('module_test'), FALSE); - drupal_flush_all_caches(); + $this->resetAll(); $this->assertFalse(module_exists('module_test'), t('Test module is disabled.')); $info = $this->getSystemInfo('seven', 'theme'); @@ -2294,7 +2294,7 @@ class UpdateScriptFunctionalTest extends DrupalWebTestCase { $this->drupalPost(NULL, array(), t('Continue')); $this->assertText(t('No pending updates.'), t('End of update process was reached.')); // Confirm that all caches were cleared. - $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared when there were no requirements warnings or errors.'); + $this->assertText(t('hook_cache_flush() invoked for update_script_test.module.'), 'Caches were cleared when there were no requirements warnings or errors.'); // If there is a requirements warning, we expect it to be initially // displayed, but clicking the link to proceed should allow us to go @@ -2312,7 +2312,7 @@ class UpdateScriptFunctionalTest extends DrupalWebTestCase { $this->drupalPost(NULL, array(), t('Apply pending updates')); $this->assertText(t('The update_script_test_update_8000() update was executed successfully.'), t('End of update process was reached.')); // Confirm that all caches were cleared. - $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared after resolving a requirements warning and applying updates.'); + $this->assertText(t('hook_cache_flush() invoked for update_script_test.module.'), 'Caches were cleared after resolving a requirements warning and applying updates.'); // Now try again without pending updates to make sure that works too. $this->drupalGet($this->update_url, array('external' => TRUE)); @@ -2322,7 +2322,7 @@ class UpdateScriptFunctionalTest extends DrupalWebTestCase { $this->drupalPost(NULL, array(), t('Continue')); $this->assertText(t('No pending updates.'), t('End of update process was reached.')); // Confirm that all caches were cleared. - $this->assertText(t('hook_flush_caches() invoked for update_script_test.module.'), 'Caches were cleared after applying updates and re-running the script.'); + $this->assertText(t('hook_cache_flush() invoked for update_script_test.module.'), 'Caches were cleared after applying updates and re-running the script.'); // If there is a requirements error, it should be displayed even after // clicking the link to proceed (since the problem that triggered the error diff --git a/core/modules/system/tests/cache.test b/core/modules/system/tests/cache.test index e5d4435b98e0..66778ae0112c 100644 --- a/core/modules/system/tests/cache.test +++ b/core/modules/system/tests/cache.test @@ -321,8 +321,9 @@ class CacheClearCase extends CacheTestCase { */ function testFlushAllCaches() { // Create cache entries for each flushed cache bin. - $bins = array('cache', 'filter', 'page', 'bootstrap', 'path'); - $bins = array_merge(module_invoke_all('flush_caches'), $bins); + $bins = module_invoke_all('cache_flush'); + $this->assertTrue($bins, 'hook_cache_flush() returned bins to flush.'); + $bins = array_merge($bins, array('menu')); foreach ($bins as $id => $bin) { $cid = 'test_cid_clear' . $id; cache($bin)->set($cid, $this->default_value); diff --git a/core/modules/system/tests/menu.test b/core/modules/system/tests/menu.test index b38e9a170f16..791cd5c8b550 100644 --- a/core/modules/system/tests/menu.test +++ b/core/modules/system/tests/menu.test @@ -394,7 +394,7 @@ class MenuRouterTestCase extends DrupalWebTestCase { // Change the menu_name parameter in menu_test.module, then force a menu // rebuild. menu_test_menu_name('changed'); - menu_rebuild(); + menu_router_rebuild(); $sql = "SELECT menu_name FROM {menu_links} WHERE router_path = 'menu_name_test'"; $name = db_query($sql)->fetchField(); diff --git a/core/modules/system/tests/modules/update_script_test/update_script_test.module b/core/modules/system/tests/modules/update_script_test/update_script_test.module index beb5a71ece55..986dc31ab3f1 100644 --- a/core/modules/system/tests/modules/update_script_test/update_script_test.module +++ b/core/modules/system/tests/modules/update_script_test/update_script_test.module @@ -6,13 +6,13 @@ */ /** - * Implements hook_flush_caches(). + * Implements hook_cache_flush(). * * This sets a message to confirm that all caches are cleared whenever * update.php completes. * * @see UpdateScriptFunctionalTest::testRequirements() */ -function update_script_test_flush_caches() { - drupal_set_message(t('hook_flush_caches() invoked for update_script_test.module.')); +function update_script_test_cache_flush() { + drupal_set_message(t('hook_cache_flush() invoked for update_script_test.module.')); } diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 44d86d6af619..4b3eab29c82f 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -836,7 +836,7 @@ function _update_cache_clear($cid = NULL, $wildcard = FALSE) { } /** - * Implements hook_flush_caches(). + * Implements hook_cache_flush(). * * Called from update.php (among others) to flush the caches. * Since we're running update.php, we are likely to install a new version of @@ -851,7 +851,7 @@ function _update_cache_clear($cid = NULL, $wildcard = FALSE) { * check if the site is in MAINTENANCE_MODE == 'update' (which indicates * update.php is running, not update module... alas for overloaded names). */ -function update_flush_caches() { +function update_cache_flush() { if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') { _update_cache_clear(); } diff --git a/profiles/standard/standard.install b/profiles/standard/standard.install index 30a943bacc7d..ec1da2c61caf 100644 --- a/profiles/standard/standard.install +++ b/profiles/standard/standard.install @@ -430,7 +430,7 @@ function standard_install() { menu_link_save($item); // Update the menu router information. - menu_rebuild(); + menu_router_rebuild(); // Enable the admin theme. db_update('system') -- GitLab