Index: update.php =================================================================== RCS file: /cvs/drupal/drupal/update.php,v retrieving revision 1.303 diff -u -p -r1.303 update.php --- update.php 14 Sep 2009 07:33:55 -0000 1.303 +++ update.php 26 Sep 2009 18:41:01 -0000 @@ -292,6 +292,10 @@ if (empty($op) && $update_access_allowed drupal_load('module', 'system'); drupal_load('module', 'filter'); + // Reset the module_implements() cache so that any new hook implementations + // in updated code are picked up. + module_implements('', FALSE, TRUE); + // Set up $language, since the installer components require it. drupal_language_initialize(); Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.995 diff -u -p -r1.995 common.inc --- includes/common.inc 25 Sep 2009 15:20:12 -0000 1.995 +++ includes/common.inc 26 Sep 2009 18:41:03 -0000 @@ -2405,6 +2405,7 @@ function drupal_page_footer() { _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE); drupal_cache_system_paths(); + module_implements_write_cache(); } /** Index: includes/module.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/module.inc,v retrieving revision 1.157 diff -u -p -r1.157 module.inc --- includes/module.inc 24 Aug 2009 00:14:18 -0000 1.157 +++ includes/module.inc 26 Sep 2009 18:41:03 -0000 @@ -14,7 +14,6 @@ function module_load_all($bootstrap = FA foreach (module_list(TRUE, $bootstrap) as $module) { drupal_load('module', $module); } - module_implements('', FALSE, TRUE); } /** @@ -336,27 +335,65 @@ 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). * @return * An array with the names of the modules which are implementing this hook. + * @see module_implements_write_cache(). */ -function module_implements($hook, $sort = FALSE, $refresh = FALSE) { - static $implementations; +function module_implements($hook, $sort = FALSE, $reset = FALSE) { + $implementations = &drupal_static(__FUNCTION__, array()); + + // We maintain a persistent cache of hook implementations in addition to the + // static cache to avoid looping through every module * 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 enabled. The cost of the + // cache_get() is more or less constant, and reduced further when 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 otherwise. - if ($refresh) { + if ($reset) { $implementations = array(); + cache_set('module_implements', array()); return; } + if (empty($implementations)) { + $implementations = cache_get('module_implements'); + if ($implementations === FALSE) { + $implementations = array(); + } + else { + $implementations = $implementations->data; + } + } + if (!isset($implementations[$hook])) { $implementations[$hook] = array(); $list = module_list(FALSE, FALSE, $sort); foreach ($list as $module) { if (module_hook($module, $hook)) { - $implementations[$hook][] = $module; + $implementations[$hook][$module] = $module; + // We added something to the cache, so write it when we're done. + $implementations['#write_cache'] = TRUE; + } + } + } + else { + foreach ($implementations[$hook] as $module) { + // It's possible that a module removed a hook implementation without the + // implementatations 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 pick up any newly added hook implementations. + unset($implementations[$hook][$module]); + $implementations['#write_cache'] = TRUE; } } } @@ -371,6 +408,22 @@ function module_implements($hook, $sort } /** + * Write to the module_implements() cache. + * @see module_implements() + */ +function module_implements_write_cache() { + $implementations = &drupal_static('module_implements'); + // Check whether we need to write the cache. + // We don't want to cache hooks which are only fired on HTTP POST requests. + // Maybe the page is not cacheable for other reasons than the HTTP request + // type as well, but this doesn't matter for us, because nothing breaks if we don't. + if (isset($implementations['#write_cache']) && drupal_page_is_cacheable()) { + unset($implementations['#write_cache']); + cache_set('module_implements', $implementations); + } +} + +/** * Invoke a hook in a particular module. * * @param $module