summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/common.inc1
-rw-r--r--includes/module.inc71
-rw-r--r--update.php4
3 files changed, 69 insertions, 7 deletions
diff --git a/includes/common.inc b/includes/common.inc
index 06b42e0f9..0c09f7087 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -2405,6 +2405,7 @@ function drupal_page_footer() {
_registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
drupal_cache_system_paths();
+ module_implements_write_cache();
}
/**
diff --git a/includes/module.inc b/includes/module.inc
index 3a5609a00..1b6d04cf7 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -14,7 +14,6 @@ function module_load_all($bootstrap = FALSE) {
foreach (module_list(TRUE, $bootstrap) as $module) {
drupal_load('module', $module);
}
- module_implements('', FALSE, TRUE);
}
/**
@@ -336,27 +335,67 @@ 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;
-
- if ($refresh) {
+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 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();
+ cache_set('module_implements', array());
return;
}
+ // Fetch implementations from cache.
+ 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 are done.
+ $implementations['#write_cache'] = TRUE;
+ }
+ }
+ }
+ else {
+ foreach ($implementations[$hook] as $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][$module]);
+ $implementations['#write_cache'] = TRUE;
}
}
}
@@ -371,6 +410,24 @@ function module_implements($hook, $sort = FALSE, $refresh = FALSE) {
}
/**
+ * Writes the hook implementation cache.
+ *
+ * @see module_implements()
+ */
+function module_implements_write_cache() {
+ $implementations = &drupal_static('module_implements');
+ // Check whether we need to write the cache. We do not want to cache hooks,
+ // which are only invoked on HTTP POST requests. Maybe the page is not
+ // cacheable for other reasons than the HTTP request type as well, but this
+ // does not matter for the hook implementation cache, because nothing breaks
+ // if hook implementations are not cached.
+ 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
diff --git a/update.php b/update.php
index 2c8fbb14b..20a9caf45 100644
--- a/update.php
+++ b/update.php
@@ -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();