From e0887f6958f48f4828d3f945d577d55d128b252a Mon Sep 17 00:00:00 2001 From: Nathaniel Date: Tue, 13 Dec 2011 15:16:23 +0900 Subject: [PATCH] #557542-175 adding module_implements() cache. --- includes/common.inc | 3 ++ includes/module.inc | 70 +++++++++++++++++++++++++++++++++++++++------ install.php | 36 +++++++++++------------ update.php | 4 ++- 4 files changed, 84 insertions(+), 29 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index 43f05d4c6df..7893f24218a 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1621,6 +1621,9 @@ function drupal_page_footer() { } module_invoke_all('exit'); + + // Update the hook implementation cache. + module_implements('', FALSE, FALSE, TRUE); } /** diff --git a/includes/module.inc b/includes/module.inc index bc4fbeaa0b4..dc23f678ed7 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -242,15 +242,15 @@ function module_load_install($module) { /** * Load a module include file. - * + * * Examples: * @code * // Load node.admin.inc from the node module. * module_load_include('inc', 'node', 'node.admin'); * // Load content_types.inc from the node module. - * module_load_include('inc', 'node', 'content_types'); + * module_load_include('inc', 'node', 'content_types'); * @endcode - * + * * Do not use this function to load an install file. Use module_load_install() * instead. * @@ -259,7 +259,7 @@ function module_load_install($module) { * @param $module * The module to which the include file belongs. * @param $name - * Optionally, specify the base file name (without the $type extension). + * Optionally, specify the base file name (without the $type extension). * If not set, $module is used. */ function module_load_include($type, $module, $name = NULL) { @@ -405,22 +405,62 @@ function module_hook($module, $hook) { * @param $sort * By default, modules are ordered by weight and filename, settings this option * to TRUE, module list will be ordered by module name. - * @param $refresh + * @param $reset * For internal use only: Whether to force the stored list of hook * implementations to be regenerated (such as after enabling a new module, * before processing hook_enable). + * @param $write_cache + * For internal use only: Update the persistent cache of hook implementations. * @return * An array with the names of the modules which are implementing this hook. */ -function module_implements($hook, $sort = FALSE, $refresh = FALSE) { +function module_implements($hook, $sort = FALSE, $reset = FALSE, $write_cache = FALSE) { static $implementations; - - if ($refresh) { + static $verified; + + // We maintain a persistent cache of hook implementations in addition to the + // static cache to avoid looping through every module and every hook on each + // request. Benchmarks show that the benefit of this caching outweighs the + // additional database hit even when using the default database caching + // backend and only a small number of modules are enabled. The cost of the + // cache_get() is more or less constant and reduced further when non-database + // caching backends are used, so there will be more significant gains when a + // large number of modules are installed or hooks invoked, since this can + // quickly lead to module_hook() being called several thousand times + // per request. + if ($reset) { $implementations = array(); + $verified = array(); + cache_set('module_implements', array()); + return; + } + + if ($write_cache) { + // Check whether we should write the cache. We do not want to cache hooks + // which are only invoked on HTTP POST requests since these do not need to + // be optimized as tightly, and not doing so keeps the cache entry smaller. + if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) { + unset($implementations['#write_cache']); + cache_set('module_implements', $implementations); + } return; } + // Fetch implementations from cache. + if (empty($implementations)) { + $cache = cache_get('module_implements'); + if (!$cache) { + $implementations = array(); + } + else { + $implementations = $cache->data; + } + } + if (!isset($implementations[$hook])) { + // The hook is not cached, so ensure that whether or not it has + // implementations, that the cache is updated at the end of the request. + $implementations['#write_cache'] = TRUE; $implementations[$hook] = array(); $list = module_list(FALSE, TRUE, $sort); foreach ($list as $module) { @@ -429,6 +469,20 @@ function module_implements($hook, $sort = FALSE, $refresh = FALSE) { } } } + elseif (empty($verified[$hook])) { + foreach ($implementations[$hook] as $key => $module) { + // It is possible that a module removed a hook implementation without the + // implementations cache being rebuilt yet, so we check module_hook() on + // each request to avoid undefined function errors. + if (!module_hook($module, $hook)) { + // Clear out the stale implementation from the cache and force a cache + // refresh to forget about no longer existing hook implementations. + unset($implementations[$hook][$key]); + $implementations['#write_cache'] = TRUE; + } + } + $verified[$hook] = TRUE; + } // The explicit cast forces a copy to be made. This is needed because // $implementations[$hook] is only a reference to an element of diff --git a/install.php b/install.php index 68b35406d89..86d58b2e63c 100644 --- a/install.php +++ b/install.php @@ -47,6 +47,18 @@ function install_main() { drupal_load('module', 'system'); drupal_load('module', 'filter'); + // Load the cache infrastructure using a "fake" cache implementation that + // does not attempt to write to the database. We need this during the initial + // part of the installer because the database is not available yet. We + // continue to use it even when the database does become available, in order + // to preserve consistency between interactive and command-line installations + // (the latter complete in one page request and therefore are forced to + // continue using the cache implementation they started with) and also + // because any data put in the cache during the installer is inherently + // suspect, due to the fact that Drupal is not fully set up yet. + require_once './includes/cache-install.inc'; + $conf['cache_inc'] = './includes/cache-install.inc'; + // Install profile chosen, set the global immediately. // This needs to be done before the theme cache gets // initialized in drupal_maintenance_theme(). @@ -61,12 +73,6 @@ function install_main() { $verify = install_verify_settings(); if ($verify) { - // Since we have a database connection, we use the normal cache system. - // This is important, as the installer calls into the Drupal system for - // the clean URL checks, so we should maintain the cache properly. - require_once './includes/cache.inc'; - $conf['cache_inc'] = './includes/cache.inc'; - // Establish a connection to the database. require_once './includes/database.inc'; db_set_active(); @@ -78,13 +84,6 @@ function install_main() { } } else { - // Since no persistent storage is available yet, and functions that check - // for cached data will fail, we temporarily replace the normal cache - // system with a stubbed-out version that short-circuits the actual - // caching process and avoids any errors. - require_once './includes/cache-install.inc'; - $conf['cache_inc'] = './includes/cache-install.inc'; - $task = NULL; } @@ -815,17 +814,14 @@ function install_tasks($profile, $task) { // The end of the install process. Remember profile used. if ($task == 'done') { - // Rebuild menu to get content type links registered by the profile, - // and possibly any other menu items created through the tasks. - menu_rebuild(); + // 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(); // Register actions declared by any modules. actions_synchronize(); - // Randomize query-strings on css/js files, to hide the fact that - // this is a new install, not upgraded yet. - _drupal_flush_css_js(); - variable_set('install_profile', $profile); } diff --git a/update.php b/update.php index 2f2c40a8a51..6ad6ed9ed55 100644 --- a/update.php +++ b/update.php @@ -594,7 +594,7 @@ function update_check_requirements() { $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; if (empty($op)) { // Minimum load of components. - drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); require_once './includes/install.inc'; require_once './includes/file.inc'; @@ -605,6 +605,8 @@ function update_check_requirements() { $module_list['system']['filename'] = 'modules/system/system.module'; $module_list['filter']['filename'] = 'modules/filter/filter.module'; module_list(TRUE, FALSE, FALSE, $module_list); + module_implements('', FALSE, TRUE); + drupal_load('module', 'system'); drupal_load('module', 'filter');