=== modified file 'includes/bootstrap.inc' --- includes/bootstrap.inc 2008-09-08 21:24:30 +0000 +++ includes/bootstrap.inc 2008-09-11 02:37:21 +0000 @@ -820,7 +820,7 @@ function watchdog($type, $message, $vari ); // Call the logging hooks to log/process the message - foreach (module_implements('watchdog', TRUE) as $module) { + foreach (module_implements('watchdog') as $module) { module_invoke($module, 'watchdog', $log_message); } } @@ -1430,60 +1430,6 @@ function registry_rebuild() { } /** - * Save hook implementations cache. - * - * @param $hook - * Array with the hook name and list of modules that implement it. - * @param $write_to_persistent_cache - * Whether to write to the persistent cache. - */ -function registry_cache_hook_implementations($hook, $write_to_persistent_cache = FALSE) { - static $implementations; - - if ($hook) { - // Newer is always better, so overwrite anything that's come before. - $implementations[$hook['hook']] = $hook['modules']; - } - - if ($write_to_persistent_cache === TRUE) { - // Only write this to cache if the implementations data we are going to cache - // is different to what we loaded earlier in the request. - if ($implementations != module_implements()) { - cache_set('hooks', $implementations, 'cache_registry'); - } - } -} - -/** - * Save the files required by the registry for this path. - */ -function registry_cache_path_files() { - if ($used_code = registry_mark_code(NULL, NULL, TRUE)) { - $files = array(); - $type_sql = array(); - $params = array(); - foreach ($used_code as $type => $names) { - $type_sql[] = "(name IN (" . db_placeholders($names, 'varchar') . ") AND type = '%s')"; - $params = array_merge($params, $names); - $params[] = $type; - } - $res = db_query("SELECT DISTINCT filename FROM {registry} WHERE " . implode(' OR ', $type_sql), $params); - while ($row = db_fetch_object($res)) { - $files[] = $row->filename; - } - 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'); - } - } - } -} - -/** * registry_load_path_files */ function registry_load_path_files($return = FALSE) { === modified file 'includes/common.inc' --- includes/common.inc 2008-09-10 04:13:01 +0000 +++ includes/common.inc 2008-09-11 03:03:41 +0000 @@ -640,7 +640,7 @@ function _drupal_get_last_caller($backtr // The first trace is the call itself. // It gives us the line and the file of the last call. $call = $backtrace[0]; - + // The second call give us the function where the call originated. if (isset($backtrace[1])) { if (isset($backtrace[1]['class'])) { @@ -1526,8 +1526,7 @@ function drupal_page_footer() { module_invoke_all('exit'); - registry_cache_hook_implementations(FALSE, TRUE); - registry_cache_path_files(); + module_implements(); } /** === modified file 'includes/menu.inc' --- includes/menu.inc 2008-09-05 08:24:08 +0000 +++ includes/menu.inc 2008-09-11 02:37:52 +0000 @@ -1726,7 +1726,7 @@ function menu_router_build($reset = FALS // We need to manually call each module so that we can know which module // a given item came from. $callbacks = array(); - foreach (module_implements('menu', NULL, TRUE) as $module) { + foreach (module_implements('menu') as $module) { $router_items = call_user_func($module . '_menu'); if (isset($router_items) && is_array($router_items)) { foreach (array_keys($router_items) as $path) { @@ -1884,7 +1884,7 @@ function _menu_delete_item($item, $force * - plid The mlid of the parent. * - router_path The path of the relevant router item. * @return - * The mlid of the saved menu link, or FALSE if the menu link could not be + * The mlid of the saved menu link, or FALSE if the menu link could not be * saved. */ function menu_link_save(&$item) { === modified file 'includes/module.inc' --- includes/module.inc 2008-08-21 19:36:35 +0000 +++ includes/module.inc 2008-09-11 02:37:21 +0000 @@ -384,9 +384,6 @@ function module_hook($module, $hook) { * * @param $hook * The name of the hook (e.g. "help" or "menu"). - * @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 * For internal use only: Whether to force the stored list of hook * implementations to be regenerated (such as after enabling a new module, @@ -396,32 +393,63 @@ function module_hook($module, $hook) { * An array with the names of the modules which are implementing this hook. * If $hook is NULL then it will return the implementation cache. */ -function module_implements($hook = NULL, $sort = FALSE, $refresh = FALSE) { - static $implementations = array(); +function module_implements($hook = NULL, $refresh = FALSE) { + static $implementations = array(), $loaded = array(), $cache; - if (!isset($hook)) { + if (defined('MAINTENANCE_MODE')) { + $implementations = array(); + foreach (module_list() as $module) { + if (module_hook($module, $hook)) { + $implementations[] = $module; + } + } return $implementations; } if ($refresh) { $implementations = array(); + $loaded = array(); + cache_clear_all('hooks', 'cache_registry'); } - if (!defined('MAINTENANCE_MODE') && empty($implementations) && ($cache = cache_get('hooks', 'cache_registry'))) { + if (!$hook) { + // Only write this to cache if the implementations data we are going to cache + // is different to what we loaded earlier in the request. + if (!$refresh && $cache && $implementations != $cache->data) { + cache_set('hooks', $implementations, 'cache_registry'); + } + return; + } + + if (empty($implementations) && ($cache = cache_get('hooks', 'cache_registry'))) { $implementations = $cache->data; } - if ($hook) { + if (empty($loaded[$hook])) { + if (isset($implementations[$hook])) { + $cached = TRUE; + // The implementations may not be in memory yet. + foreach ($implementations[$hook] as $module) { + $function = $module .'_'. $hook; + // If the implementations are already cached but there are functions + // to be looked up, then we discard the cache. The per router path + // caching will make this rare. + if (!function_exists($function)) { + unset($implementations[$hook]); + break; + } + } + } if (!isset($implementations[$hook])) { $implementations[$hook] = array(); - foreach (module_list() as $module) { - if (module_hook($module, $hook)) { - $implementations[$hook][] = $module; - } + $result = db_query("SELECT name, filename, module FROM {registry} WHERE type = 'function' AND hook = '%s'", $hook); + while ($function = db_fetch_object($result)) { + $implementations[$hook][] = $function->module; + drupal_function_exists($function->name); } } - registry_cache_hook_implementations(array('hook' => $hook, 'modules' => $implementations[$hook])); - - return $implementations[$hook]; + $loaded[$hook] = TRUE; } + + return $implementations[$hook]; } /** === modified file 'includes/registry.inc' --- includes/registry.inc 2008-08-21 19:36:35 +0000 +++ includes/registry.inc 2008-09-11 02:37:21 +0000 @@ -45,7 +45,7 @@ function _registry_rebuild() { if ($module->status) { $dir = dirname($module->filename); foreach ($module->info['files'] as $file) { - $files["./$dir/$file"] = array(); + $files["./$dir/$file"] = array('module' => $module); } } } @@ -67,6 +67,7 @@ function _registry_rebuild() { } _registry_parse_files($files); + module_implements('', FALSE, TRUE); cache_clear_all('*', 'cache_registry', TRUE); } @@ -84,7 +85,7 @@ function registry_get_parsed_files() { * Parse all files that have changed since the registry was last built, and save their function and class listings. * * @param $files - * The list of files to check and parse. + * The list of files to check and parse. */ function _registry_parse_files($files) { $changed_files = array(); @@ -95,7 +96,7 @@ function _registry_parse_files($files) { if ($new_file || $md5 != $file['md5']) { // 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); + _registry_parse_file($filename, $file, $contents); $file['md5'] = $md5; db_merge('registry_file') ->key(array('filename' => $filename)) @@ -109,11 +110,13 @@ function _registry_parse_files($files) { * Parse a file and save its function and class listings. * * @param $filename - * Name of the file we are going to parse. + * Name of the file we are going to parse. + * @param $file + * An associative array about the file. Only the module key is used. * @param $contents - * Contents of the file we are going to parse as a string. + * Contents of the file we are going to parse as a string. */ -function _registry_parse_file($filename, $contents) { +function _registry_parse_file($filename, $file, $contents) { static $map = array(T_FUNCTION => 'function', T_CLASS => 'class', T_INTERFACE => 'interface'); // Delete registry entries for this file, so we can insert the new resources. db_delete('registry')->condition('filename', $filename)->execute(); @@ -123,6 +126,20 @@ function _registry_parse_file($filename, if (is_array($token) && isset($map[$token[0]])) { $type = $map[$token[0]]; if ($resource_name = _registry_get_resource_name($tokens, $type)) { + $hook = ''; + $module = ''; + if ($type == 'function' && isset($file['module'])) { + $module = $file['module']->name; + $n = strlen($module); + if (substr($resource_name, 0, $n) == $module) { + $hook = substr($resource_name, $n + 1); + } + } + $fields = array( + 'filename' => $filename, + 'module' => $module, + 'hook' => $hook, + ); // Because some systems, such as cache, currently use duplicate function // names in separate files an insert query cannot be used here as it // would cause a key constraint violation. Instead we use a merge query. @@ -132,7 +149,7 @@ function _registry_parse_file($filename, // filename instead of another. // TODO: Convert this back to an insert query after all duplicate // function names have been purged from Drupal. - db_merge('registry')->key(array('name' => $resource_name, 'type' => $type))->fields(array('filename' => $filename))->execute(); + db_merge('registry')->key(array('name' => $resource_name, 'type' => $type))->fields($fields)->execute(); // We skip the body because classes may contain functions. _registry_skip_body($tokens); === modified file 'index.php' --- index.php 2008-08-21 19:36:35 +0000 +++ index.php 2008-09-11 02:37:21 +0000 @@ -14,6 +14,7 @@ require_once './includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + $return = menu_execute_active_handler(); // Menu status constants are integers; page content is a string. === modified file 'modules/simpletest/tests/registry.test' --- modules/simpletest/tests/registry.test 2008-08-14 09:18:28 +0000 +++ modules/simpletest/tests/registry.test 2008-09-11 03:05:55 +0000 @@ -29,7 +29,7 @@ class RegistryParseFileTestCase extends * testRegistryParseFile */ function testRegistryParseFile() { - _registry_parse_file($this->fileName, $this->getFileContents()); + _registry_parse_file($this->fileName, array(), $this->getFileContents()); foreach (array('functionName', 'className', 'interfaceName') as $resource) { $foundName = db_result(db_query("SELECT name FROM {registry} WHERE name = '%s'", $this->$resource)); $this->assertTrue($this->$resource == $foundName, t('Resource "@resource" found.', array('@resource' => $this->$resource))); === modified file 'modules/system/system.install' --- modules/system/system.install 2008-09-06 08:36:19 +0000 +++ modules/system/system.install 2008-09-11 02:37:21 +0000 @@ -1091,6 +1091,18 @@ function system_schema() { 'length' => 255, 'not null' => TRUE, ), + 'module' => array( + 'description' => t('Name of the module the file belongs to.'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'hook' => array( + 'description' => t('Name of the hook this function implements, if any.'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), ), 'primary key' => array('name', 'type'), );