summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDries Buytaert2008-11-11 22:39:59 (GMT)
committerDries Buytaert2008-11-11 22:39:59 (GMT)
commit59ece2e39d28ba6052ea67d7770297b93a29d243 (patch)
tree212d76aad3f27766f0fc60540d8fa0e828b820b1
parent9d631d22f920796287041408dd8cf090370bac72 (diff)
- Patch #325665 by chx, Damien Tournoud, justinrandell et al: improved the cache registry lookups.
-rw-r--r--includes/bootstrap.inc164
-rw-r--r--includes/common.inc2
-rw-r--r--includes/menu.inc1
-rw-r--r--includes/module.inc5
-rw-r--r--includes/registry.inc22
-rw-r--r--modules/block/block.module2
-rw-r--r--modules/system/system.admin.inc2
-rw-r--r--modules/system/system.module4
-rw-r--r--modules/user/user.admin.inc2
-rw-r--r--modules/user/user.module2
10 files changed, 117 insertions, 89 deletions
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index 3a661ac..495dd0b 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -207,6 +207,16 @@ define('CHECK_PLAIN', 0);
define('PASS_THROUGH', -1);
/**
+ * Signals that the registry lookup cache should be reset.
+ */
+define('REGISTRY_RESET_LOOKUP_CACHE', 1);
+
+/**
+ * Signals that the registry lookup cache should be written to storage.
+ */
+define('REGISTRY_WRITE_LOOKUP_CACHE', 2);
+
+/**
* @} End of "Title text filtering flags".
*/
@@ -655,7 +665,7 @@ function page_get_cache() {
* The name of the bootstrap hook we wish to invoke.
*/
function bootstrap_invoke_all($hook) {
- foreach (module_list(TRUE, TRUE) as $module) {
+ foreach (module_implements($hook) as $module) {
module_invoke($module, $hook);
}
}
@@ -1412,7 +1422,6 @@ function drupal_function_exists($function) {
$checked[$function] = FALSE;
if (function_exists($function)) {
- registry_mark_code('function', $function);
$checked[$function] = TRUE;
return TRUE;
}
@@ -1456,8 +1465,52 @@ function drupal_autoload_class($class) {
/**
* Helper to check for a resource in the registry.
+ *
+ * @param $type
+ * The type of resource we are looking up, or one of the constants
+ * REGISTRY_RESET_LOOKUP_CACHE or REGISTRY_WRITE_LOOKUP_CACHE, which
+ * signal that we should reset or write the cache, respectively.
+ * @param $name
+ * The name of the resource, or NULL if either of the REGISTRY_* constants
+ * is passed in.
+ * @return
+ * TRUE if the resource was found, FALSE if not.
+ * NULL if either of the REGISTRY_* constants is passed in as $type.
*/
-function _registry_check_code($type, $name) {
+function _registry_check_code($type, $name = NULL) {
+ static $lookup_cache, $cache_update_needed;
+
+ if (!isset($lookup_cache)) {
+ $lookup_cache = _registry_get_lookup_cache();
+ }
+
+ // When we rebuild the registry, we need to reset this cache so
+ // we don't keep lookups for resources that changed during the rebuild.
+ if ($type == REGISTRY_RESET_LOOKUP_CACHE) {
+ $cache_update_needed = TRUE;
+ $lookup_cache = NULL;
+ return;
+ }
+
+ // Called from drupal_page_footer, we write to permanent storage if there
+ // changes to the lookup cache for this request.
+ if ($type == REGISTRY_WRITE_LOOKUP_CACHE) {
+ if ($cache_update_needed) {
+ _registry_set_lookup_cache($lookup_cache);
+ }
+ return;
+ }
+
+ // $type can be one of 'function', 'interface' or 'class', so we only need the
+ // first letter to keep the cache key unique.
+ $cache_key = $type[0] . $name;
+ if (isset($lookup_cache[$cache_key])) {
+ if ($lookup_cache[$cache_key]) {
+ require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
+ }
+ return $lookup_cache[$cache_key];
+ }
+
// This function may get called when the default database is not active, but
// there is no reason we'd ever want to not use the default database for
// this query.
@@ -1466,38 +1519,20 @@ function _registry_check_code($type, $name) {
':type' => $type,
))
->fetchField();
+
+ // Flag that we've run a lookup query and need to update the cache.
+ $cache_update_needed = TRUE;
+
+ // Misses are valuable information worth caching, so cache even if
+ // $file is FALSE.
+ $lookup_cache[$cache_key] = $file;
+
if ($file) {
require_once DRUPAL_ROOT . '/' . $file;
- registry_mark_code($type, $name);
return TRUE;
}
- return FALSE;
-}
-
-/**
- * Collect the resources used for this request.
- *
- * @param $type
- * The type of resource.
- * @param $name
- * The name of the resource.
- * @param $return
- * Boolean flag to indicate whether to return the resources.
- */
-function registry_mark_code($type, $name, $return = FALSE) {
- static $resources = array();
-
- if ($type && $name) {
- if (!isset($resources[$type])) {
- $resources[$type] = array();
- }
- if (!in_array($name, $resources[$type])) {
- $resources[$type][] = $name;
- }
- }
-
- if ($return) {
- return $resources;
+ else {
+ return FALSE;
}
}
@@ -1513,60 +1548,35 @@ function registry_rebuild() {
}
/**
- * Save the files required by the registry for this path.
+ * Wrapper function to perform array to string conversion of lookup cache.
*/
-function registry_cache_path_files() {
- if ($used_code = registry_mark_code(NULL, NULL, TRUE)) {
- $files = array();
- $type_sql = array();
- $params = array();
-
- // This function may get called when the default database is not active, but
- // there is no reason we'd ever want to not use the default database for
- // this query.
- $select = Database::getConnection('default')->select('registry')->distinct();
- $select->addField('registry', 'filename');
-
- // This creates a series of 2-clause AND conditions that are then ORed together.
- $ors = db_or();
- foreach ($used_code as $type => $names) {
- $and = db_and()
- ->condition('name', $names, 'IN')
- ->condition('type', $type);
- $ors->condition($and);
- }
- $select->condition($ors);
- $files = $select->execute()->fetchCol();
-
- if ($files) {
- sort($files);
- // Only write this to cache if the file list we are going to cache
- // is different to what we loaded earlier in the request.
- if ($files != registry_load_path_files(TRUE)) {
- $menu = menu_get_item();
- cache_set('registry:' . $menu['path'], implode(';', $files), 'cache_registry');
- }
- }
+function _registry_set_lookup_cache(array $lookup_cache) {
+ // Cache a string, not an array, so we can avoid the memory usage hit
+ // from serialize() in the cache system.
+ $key_value_pairs = array();
+ foreach ($lookup_cache as $key => $value) {
+ $key_value_pairs[] = "$key|" . ($value ? $value : '');
}
+ return cache_set('lookup_cache', implode(';', $key_value_pairs), 'cache_registry');
}
/**
- * registry_load_path_files
+ * Wrapper function to perform string to array conversion of lookup cache.
*/
-function registry_load_path_files($return = FALSE) {
- static $file_cache_data = array();
- if ($return) {
- sort($file_cache_data);
- return $file_cache_data;
- }
- $menu = menu_get_item();
- $cache = cache_get('registry:' . $menu['path'], 'cache_registry');
- if (!empty($cache->data)) {
- foreach(explode(';', $cache->data) as $file) {
- require_once DRUPAL_ROOT . '/' . $file;
- $file_cache_data[] = $file;
+function _registry_get_lookup_cache() {
+ // In _registry_set_lookup_cache, we cache a string, not an array, to avoid
+ // serialize() in the cache system. serialize() makes a copy, and thus uses
+ // extra memory, which we are trying to avoid.
+ $lookup_cache = array();
+ if ($cache = cache_get('lookup_cache', 'cache_registry')) {
+ // Each item is separated by ';'.
+ foreach (explode(';', $cache->data) as $lookup) {
+ // Key value pairs are separated by '|'.
+ list($resource, $result) = explode('|', $lookup);
+ $lookup_cache[$resource] = $result;
}
}
+ return $lookup_cache;
}
/**
diff --git a/includes/common.inc b/includes/common.inc
index 5c9cb05..8e78606 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -1641,7 +1641,7 @@ function drupal_page_footer() {
module_invoke_all('exit');
module_implements(MODULE_IMPLEMENTS_WRITE_CACHE);
- registry_cache_path_files();
+ _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
}
/**
diff --git a/includes/menu.inc b/includes/menu.inc
index f4f8ecd..fdcc1bf 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -393,7 +393,6 @@ function menu_execute_active_handler($path = NULL) {
menu_rebuild();
}
if ($router_item = menu_get_item($path)) {
- registry_load_path_files();
if ($router_item['access']) {
if (drupal_function_exists($router_item['page_callback'])) {
return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
diff --git a/includes/module.inc b/includes/module.inc
index c5c3686..9eb2eee 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -90,7 +90,7 @@ function module_list($refresh = FALSE, $bootstrap = TRUE, $sort = FALSE, $fixed_
* The array of filesystem objects used to rebuild the cache.
*/
function module_rebuild_cache() {
- // Get current list of modules
+ // Get current list of modules, including uninstalled modules.
$files = drupal_system_listing('/\.module$/', 'modules', 'name', 0);
// Extract current files from database.
@@ -128,7 +128,8 @@ function module_rebuild_cache() {
// Log the critical hooks implemented by this module.
$bootstrap = 0;
foreach (bootstrap_hooks() as $hook) {
- if (module_hook($file->name, $hook)) {
+ // Only look for hooks in installed modules.
+ if (!empty($file->status) && in_array($file->name, module_implements($hook))) {
$bootstrap = 1;
break;
}
diff --git a/includes/registry.inc b/includes/registry.inc
index c028111..ec968dd 100644
--- a/includes/registry.inc
+++ b/includes/registry.inc
@@ -69,10 +69,26 @@ function _registry_rebuild() {
->execute();
}
}
- _registry_parse_files($files);
+ $parsed_files = _registry_parse_files($files);
+
+ $unchanged_resources = array();
+ foreach (_registry_get_lookup_cache() as $key => $file) {
+ // If the file for this cached resource is carried over unchanged from
+ // the last registry build, then we can safely re-cache it.
+ if ($file && in_array($file, array_keys($files)) && !in_array($file, $parsed_files)) {
+ $unchanged_resources[$key] = $file;
+ }
+ }
+ _registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
module_implements(MODULE_IMPLEMENTS_CLEAR_CACHE);
cache_clear_all('*', 'cache_registry', TRUE);
+
+ // We have some unchanged resources, warm up the cache - no need to pay
+ // for looking them up again.
+ if (count($unchanged_resources) > 0) {
+ _registry_set_lookup_cache($unchanged_resources);
+ }
}
/**
@@ -92,12 +108,13 @@ function registry_get_parsed_files() {
* The list of files to check and parse.
*/
function _registry_parse_files($files) {
- $changed_files = array();
+ $parsed_files = array();
foreach ($files as $filename => $file) {
$contents = file_get_contents($filename);
$md5 = md5($contents);
$new_file = !isset($file['md5']);
if ($new_file || $md5 != $file['md5']) {
+ $parsed_files[] = $filename;
// We update the md5 after we've saved the files resources rather than here, so if we
// don't make it through this rebuild, the next run will reparse the file.
_registry_parse_file($filename, $contents, $file['module'], $file['weight']);
@@ -108,6 +125,7 @@ function _registry_parse_files($files) {
->execute();
}
}
+ return $parsed_files;
}
/**
diff --git a/modules/block/block.module b/modules/block/block.module
index 9c482e1..95756a9 100644
--- a/modules/block/block.module
+++ b/modules/block/block.module
@@ -239,7 +239,7 @@ function _block_rehash() {
// Valid region names for the theme.
$regions = system_region_list($theme_key);
- foreach (module_list() as $module) {
+ foreach (module_implements('block') as $module) {
$module_blocks = module_invoke($module, 'block', 'list');
if ($module_blocks) {
foreach ($module_blocks as $delta => $block) {
diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index 03db7b6..fe94ba1 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -657,7 +657,7 @@ function system_modules($form_state = array()) {
}
}
// Generate link for module's help page, if there is one.
- if ($help_arg && module_hook($filename, 'help')) {
+ if ($help_arg && $module->status && in_array($filename, module_implements('help'))) {
if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) {
// Module has a help page.
$extra['help'] = theme('more_help_link', url("admin/help/$filename"));
diff --git a/modules/system/system.module b/modules/system/system.module
index 48ac89a..b4f7969 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -1398,12 +1398,12 @@ function system_get_module_admin_tasks($module) {
$admin_tasks = array();
$admin_task_count = 0;
// Check for permissions.
- if (module_hook($module, 'perm') && $admin_access) {
+ if (in_array($module, module_implements('perm')) && $admin_access) {
$admin_tasks[-1] = l(t('Configure permissions'), 'admin/user/permissions', array('fragment' => 'module-' . $module));
}
// Check for menu items that are admin links.
- if ($menu = module_invoke($module, 'menu')) {
+ if (in_array($module, module_implements('menu')) && $menu = module_invoke($module, 'menu')) {
foreach (array_keys($menu) as $path) {
if (isset($items[$path])) {
$admin_tasks[$items[$path]['title'] . $admin_task_count ++] = l($items[$path]['title'], $path);
diff --git a/modules/user/user.admin.inc b/modules/user/user.admin.inc
index c58cbb6..370b7e0 100644
--- a/modules/user/user.admin.inc
+++ b/modules/user/user.admin.inc
@@ -510,7 +510,7 @@ function user_admin_perm($form_state, $rid = NULL) {
// Render role/permission overview:
$options = array();
$hide_descriptions = !system_admin_compact_mode();
- foreach (module_list(FALSE, FALSE, TRUE) as $module) {
+ foreach (module_implements('perm') as $module) {
if ($permissions = module_invoke($module, 'perm')) {
$form['permission'][] = array(
'#markup' => $module,
diff --git a/modules/user/user.module b/modules/user/user.module
index 84ba504..224d44d 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -1917,7 +1917,7 @@ function user_help($path, $arg) {
function _user_categories($account) {
$categories = array();
- foreach (module_list() as $module) {
+ foreach (module_implements('user_categories') as $module) {
if ($data = module_invoke($module, 'user_categories', NULL, $account, '')) {
$categories = array_merge($data, $categories);
}