=== modified file 'includes/module.inc'
--- includes/module.inc	2007-02-04 21:20:50 +0000
+++ includes/module.inc	2007-02-09 06:41:29 +0000
@@ -254,7 +254,7 @@ function module_enable($module_list) {
     // Refresh the module list to include the new enabled module.
     module_list(TRUE, FALSE);
     // Force to regenerate the stored list of hook implementations.
-    module_implements('', FALSE, TRUE);
+    _module_build_registry_cache();
   }
 
   foreach ($invoke_modules as $module) {
@@ -283,7 +283,7 @@ function module_disable($module_list) {
     // Refresh the module list to exclude the disabled modules.
     module_list(TRUE, FALSE);
     // Force to regenerate the stored list of hook implementations.
-    module_implements('', FALSE, TRUE);
+    module_build_registry_cache();
   }
 }
 
@@ -320,7 +320,31 @@ function module_disable($module_list) {
  *   implemented in that module.
  */
 function module_hook($module, $hook) {
-  return function_exists($module .'_'. $hook);
+  $registry = _module_get_registry_cache();
+  return isset($registry[$hook][$module]) && is_array($registry[$hook][$module]);
+}
+
+/**
+ * Get the actual function to be used in for a module's hook. If this
+ * hook requires an external file, include that file.
+ *
+ * @param $module
+ *   The name of the module (without the .module extension).
+ * @param $hook
+ *   The name of the hook (e.g. "help" or "menu").
+ * @return
+ *   The name of the function to call to implement that hook.
+ */
+function module_get_function($module, $hook) {
+  $registry = _module_get_registry_cache();
+
+  if (isset($registry[$hook][$module]) && is_array($registry[$hook][$module])) {
+    $info = $registry[$hook][$module];
+    if (!empty($info['file'])) {
+      include_once(drupal_get_path('module', $module) .'/'. $info[$file]);
+    }
+    return $info['function'];
+  }
 }
 
 /**
@@ -331,42 +355,47 @@ 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
- *   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.
  */
-function module_implements($hook, $sort = FALSE, $refresh = FALSE) {
-  static $implementations;
+function module_implements($hook, $sort = FALSE) {
+  $registry = _module_get_registry_cache();
 
-  if ($refresh) {
-    unset($implementations);
-    return;
+  if (isset($registry[$hook])) {
+    return array_keys($registry[$hook]);
   }
+  return array();
+}
 
-  if (!isset($implementations[$hook])) {
-    $implementations[$hook] = array();
-    $list = module_list(FALSE, TRUE, $sort);
-    foreach ($list as $module) {
-      if (module_hook($module, $hook)) {
-        $implementations[$hook][] = $module;
-      }
-    }
-  }
+/**
+ * Invoke a hook in a particular module. This function is unable to pass
+ * references, however, so it is sometimes necessary to implement it
+ * yourself.
+ *
+ * @param $module
+ *   The name of the module (without the .module extension).
+ * @param $hook
+ *   The name of the hook to invoke.
+ * @param ...
+ *   Arguments to pass to the hook implementation.
+ * @return
+ *   The return value of the hook implementation.
+ */
+function module_invoke() {
+  $args = func_get_args();
+  $module = array_shift($args);
+  $hook = array_shift($args);
 
-  // The explicit cast forces a copy to be made. This is needed because
-  // $implementations[$hook] is only a reference to an element of
-  // $implementations and if there are nested foreaches (due to nested node
-  // API calls, for example), they would both manipulate the same array's
-  // references, which causes some modules' hooks not to be called.
-  // See also http://www.zend.com/zend/art/ref-count.php.
-  return (array)$implementations[$hook];
+  if ($function = module_get_function($module, $hook)) {
+    return call_user_func_array($function, $args);
+  }
 }
 
 /**
- * Invoke a hook in a particular module.
+ * Invokes a predefined hook. This is similar to module_invoke, but it does
+ * not check the hook registry; instead it directly checks the hook, as there
+ * are a few hooks that are not registered, such as 'register', 'hooks' and
+ * 'update'.
  *
  * @param $module
  *   The name of the module (without the .module extension).
@@ -377,12 +406,12 @@ function module_implements($hook, $sort 
  * @return
  *   The return value of the hook implementation.
  */
-function module_invoke() {
+function module_invoke_hook() {
   $args = func_get_args();
   $module = array_shift($args);
   $hook = array_shift($args);
   $function = $module .'_'. $hook;
-  if (module_hook($module, $hook)) {
+  if (function_exists($function)) {
     return call_user_func_array($function, $args);
   }
 }
@@ -402,8 +431,9 @@ function module_invoke_all() {
   $hook = array_shift($args);
   $return = array();
   foreach (module_implements($hook) as $module) {
-    $function = $module .'_'. $hook;
+    $function = module_get_function($module, $hook);
     $result = call_user_func_array($function, $args);
+
     if (isset($result) && is_array($result)) {
       $return = array_merge($return, $result);
     }
@@ -416,6 +446,190 @@ function module_invoke_all() {
 }
 
 /**
+ * Clear the module registry cache.
+ */
+function _module_clear_registry_cache() {
+  // pass through for static.
+  _module_add_registry_cache('@reset');
+}
+
+/**
+ * Get the current registry cache
+ */
+function _module_get_registry_cache($load = TRUE) {
+  // pass through for static.
+  $arg = $load ? '@load' : NULL;
+  return _module_add_registry_cache($arg);
+}
+
+/**
+ * Add a module's combined information into the registry cache.
+ */
+function _module_add_registry_cache($hook = NULL, $module = NULL, $info = NULL) {
+  static $registry = NULL;
+  if ($hook == '@load') {
+    if ($registry === NULL) {
+      _module_load_registry_cache();
+    }
+    return $registry;
+  }
+
+  if ($hook === NULL) {
+    return $registry;
+  }
+
+  if ($hook == '@reset') {
+    $registry = array();
+    return;
+  }
+
+  if (is_array($hook)) {
+    $registry = $hook;
+    return;
+  }
+
+  $registry[$hook][$module] = $info;
+
+  return $registry;
+}
+
+/**
+ * Get the registry cache from the database; if it doesn't exist, build
+ * it.
+ */
+function _module_load_registry_cache() {
+  // TODO: use a separate cache for this.
+  $registry = cache_get('module_registry', 'cache');
+  if (isset($registry->data)) {
+    _module_add_registry_cache(unserialize($registry->data));
+  }
+  else {
+    module_build_registry_cache();
+  }
+}
+
+/**
+ * Write the registry cache into the database.
+ */
+function _module_save_registry_cache() {
+  $registry = _module_get_registry_cache(FALSE);
+  // TODO: use a separate cache for this.
+  cache_set('module_registry', 'cache', serialize($registry));
+}
+
+
+/**
+ * Rebuild the hook registry cache.
+ */
+function module_build_registry_cache() {
+  _module_clear_registry_cache();
+  $files = drupal_system_listing('\.module$', 'modules', 'name', 0);
+  // Extract current files from database.
+  system_get_files_database($files, 'module');
+
+  foreach ($files as $filename => $file) {
+    if (isset($file->status)) {
+      _module_build_registry($file);
+    }
+  }
+
+  _module_save_registry_cache();
+}
+
+/**
+ * Collect information about what hooks and hooks a module provides.
+ *
+ * @param $module
+ * The module information as read by drupal_listing.
+ */
+function _module_build_registry($module) {
+  // This should become if module_implements_hook and module_hook
+  _module_register_current_module($module);
+  // This skips the typical module_implements routine because at this point
+  // that data no longer exists.
+  $function = $module->name .'_register_hooks';
+  if (function_exists($function)) {
+    $function();
+  }
+  else {
+    // If the module doesn't implement the register hook, assume the
+    // default hooks.
+    module_register_default_hooks();
+  }
+}
+
+/**
+ * Set or get which module is currently being registered.
+ */
+function _module_register_current_module($module = NULL) {
+  static $cache = NULL;
+  if ($module !== NULL) {
+    $cache = $module;
+  }
+  return $cache;
+}
+
+/**
+ * Register a hook; should only be called from within a module's
+ * registry hook.
+ *
+ * 'hook_name' => array(
+ *   'module_name' => array(
+ *     'function' => function name
+ *     'file' => (optional) filename to be loaded
+ *   )
+ * )
+ *
+*/
+function module_register_hook($hook, $function = NULL, $file = NULL) {
+  $module = _module_register_current_module();
+  if ($function === NULL) {
+    $function = $module->name . '_' . $hook;
+  }
+
+  // files registered by modules must be in the module's filepath.
+  if ($file) {
+    $file = dirname($module->filename) . '/' . $file;
+  }
+  _module_add_registry_cache($hook, $module->name, array('function' => $function, 'file' => $file));
+}
+
+/**
+ * Scan through all known hooks and register them for the current
+ * module.
+ */
+function module_register_default_hooks() {
+  $module = _module_register_current_module();
+
+  foreach (module_get_hooks() as $hook) {
+    $function = $module->name .'_'. $hook;
+    if (function_exists($function)) {
+      module_register_hook($hook, $function);
+    }
+  }
+}
+
+/**
+ * Get a list of all hooks defined in the system.
+ */
+function module_get_hooks() {
+  static $cache = array();
+
+  // This is a special hook, which operates as the hold hook mechanism did,
+  // which is that it's a well-known named function to tell Drupal what hooks
+  // a given module utilizes.
+  if (!$cache) {
+    foreach (module_list(FALSE, FALSE) as $module) {
+      $hooks = module_invoke_hook($module, 'hooks');
+      if (is_array($hooks)) {
+        $cache = array_merge($cache, $hooks);
+      }
+    }
+  }
+  return $cache;
+}
+
+/**
  * @} End of "defgroup hooks".
  */
 

=== modified file 'modules/block/block.module'
--- modules/block/block.module	2007-01-31 15:49:22 +0000
+++ modules/block/block.module	2007-02-09 06:41:29 +0000
@@ -13,6 +13,12 @@
 define('BLOCK_REGION_NONE', -1);
 
 /**
+ * Implementation of hook_hooks
+ */
+function block_hooks() {
+  return array('block');
+}
+/**
  * Implementation of hook_help().
  */
 function block_help($section) {

=== modified file 'modules/comment/comment.module'
--- modules/comment/comment.module	2007-01-31 15:49:22 +0000
+++ modules/comment/comment.module	2007-02-09 06:41:29 +0000
@@ -121,6 +121,12 @@ define('COMMENT_PREVIEW_OPTIONAL', 0);
 define('COMMENT_PREVIEW_REQUIRED', 1);
 
 /**
+ * Implementation of hook_hooks
+ */
+function comment_hooks() {
+  return array('comment');
+}
+/**
  * Implementation of hook_help().
  */
 function comment_help($section) {
@@ -297,9 +303,7 @@ function theme_comment_block() {
  */
 function comment_link($type, $node = NULL, $teaser = FALSE) {
   $links = array();
-
   if ($type == 'node' && $node->comment) {
-
     if ($teaser) {
       // Main page: display the number of comments that have been posted.
 
@@ -1994,4 +1998,3 @@ function comment_invoke_comment(&$commen
 function vancode2int($c = '00') {
   return base_convert(substr($c, 1), 36, 10);
 }
-

=== modified file 'modules/filter/filter.module'
--- modules/filter/filter.module	2007-01-31 15:49:22 +0000
+++ modules/filter/filter.module	2007-02-09 06:41:29 +0000
@@ -15,6 +15,12 @@ define('FILTER_HTML_STRIP', 1);
 define('FILTER_HTML_ESCAPE', 2);
 
 /**
+ * Implementation of hook_hooks
+ */
+function filter_hooks() {
+  return array('filter', 'filter_tips');
+}
+/**
  * Implementation of hook_help().
  */
 function filter_help($section) {
@@ -1484,4 +1490,3 @@ function filter_xss_bad_protocol($string
 /**
  * @} End of "Standard filters".
  */
-

=== modified file 'modules/menu/menu.module'
--- modules/menu/menu.module	2007-01-31 16:38:34 +0000
+++ modules/menu/menu.module	2007-02-09 06:41:29 +0000
@@ -7,6 +7,12 @@
  */
 
 /**
+ * Implementation of hook_hooks
+ */
+function menu_hooks() {
+  return array('menu');
+}
+/**
  * Implementation of hook_help().
  */
 function menu_help($section) {

=== modified file 'modules/node/node.module'
--- modules/node/node.module	2007-02-06 08:16:27 +0000
+++ modules/node/node.module	2007-02-09 06:57:13 +0000
@@ -10,6 +10,13 @@
 define('NODE_NEW_LIMIT', time() - 30 * 24 * 60 * 60);
 
 /**
+ * Implementation of hook_hooks
+ */
+function node_hooks() {
+  return array('node_access_records', 'node_info', 'node_grants', 'node_operations', 'node_type', 'nodeapi');
+}
+
+/**
  * Implementation of hook_help().
  */
 function node_help($section) {
@@ -414,9 +421,9 @@ function _node_type_set_defaults($info) 
  */
 function node_hook(&$node, $hook) {
   $module = node_get_types('module', $node);
-  if ($module == 'node') {
-    $module = 'node_content'; // Avoid function name collisions.
-  }
+//  if ($module == 'node') {
+//    $module = 'node_content'; // Avoid function name collisions.
+//  }
   return module_hook($module, $hook);
 }
 
@@ -435,10 +442,10 @@ function node_hook(&$node, $hook) {
 function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
   if (node_hook($node, $hook)) {
     $module = node_get_types('module', $node);
-    if ($module == 'node') {
-      $module = 'node_content'; // Avoid function name collisions.
-    }
-    $function = $module .'_'. $hook;
+    //if ($module == 'node') {
+    //  $module = 'node_content'; // Avoid function name collisions.
+    //}
+    $function = module_get_function($module, $hook);
     return ($function($node, $a2, $a3, $a4));
   }
 }
@@ -458,7 +465,7 @@ function node_invoke(&$node, $hook, $a2 
 function node_invoke_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
   $return = array();
   foreach (module_implements('nodeapi') as $name) {
-    $function = $name .'_nodeapi';
+    $function = module_get_function($name, 'nodeapi');
     $result = $function($node, $op, $a3, $a4);
     if (isset($result) && is_array($result)) {
       $return = array_merge($return, $result);
@@ -673,7 +680,6 @@ function node_view($node, $teaser = FALS
 
   if ($links) {
     $node->links = module_invoke_all('link', 'node', $node, !$page);
-
     foreach (module_implements('link_alter') AS $module) {
       $function = $module .'_link_alter';
       $function($node, $node->links);
@@ -2641,9 +2647,9 @@ function node_access($op, $node) {
   // Can't use node_invoke(), because the access hook takes the $op parameter
   // before the $node parameter.
   $module = node_get_types('module', $node);
-  if ($module == 'node') {
-    $module = 'node_content'; // Avoid function name collisions.
-  }
+  //if ($module == 'node') {
+  //  $module = 'node_content'; // Avoid function name collisions.
+  //}
   $access = module_invoke($module, 'access', $op, $node);
   if (!is_null($access)) {
     return $access;

=== modified file 'modules/search/search.module'
--- modules/search/search.module	2007-01-31 21:26:55 +0000
+++ modules/search/search.module	2007-02-09 06:41:29 +0000
@@ -91,6 +91,13 @@ define('PREG_CLASS_CJK', '\x{3041}-\x{30
 '\x{4e00}-\x{9fbb}\x{f900}-\x{fad9}');
 
 /**
+ * Implementation of hook_hooks
+ */
+function search_hooks() {
+  // search: search, search_item, update_index
+  return array('search', 'search_item', 'update_index');
+}
+/**
  * Implementation of hook_help().
  */
 function search_help($section) {

=== modified file 'modules/system/system.module'
--- modules/system/system.module	2007-02-04 21:20:50 +0000
+++ modules/system/system.module	2007-02-09 06:41:29 +0000
@@ -51,6 +51,18 @@ function system_perm() {
 }
 
 /**
+ * Implementation of hook_hooks
+ */
+function system_hooks() {
+  // filter: filter, filter_tips,
+  // comment: comment
+  // search: search, search_item, update_index
+  return array('cron', 'db_rewrite_sql', 'disable', 'elements', 'enable',
+               'exit', 'file_download', 'form', 'form_alter', 'forms',
+               'help', 'init', 'install', 'link', 'link_alter', 'mail_alter',
+               'ping', 'requirements', 'throttle', 'uninstall', 'xmlrpc');
+}
+/**
  * Implementation of hook_elements().
  */
 function system_elements() {

=== modified file 'modules/taxonomy/taxonomy.module'
--- modules/taxonomy/taxonomy.module	2007-01-31 21:26:55 +0000
+++ modules/taxonomy/taxonomy.module	2007-02-09 06:41:29 +0000
@@ -7,6 +7,12 @@
  */
 
 /**
+ * Implementation of hook_hooks
+ */
+function taxonomy_hooks() {
+  return array('taxonomy');
+}
+/**
  * Implementation of hook_perm().
  */
 function taxonomy_perm() {

=== modified file 'modules/user/user.module'
--- modules/user/user.module	2007-02-02 15:25:25 +0000
+++ modules/user/user.module	2007-02-09 06:41:29 +0000
@@ -10,6 +10,12 @@ define('USERNAME_MAX_LENGTH', 60);
 define('EMAIL_MAX_LENGTH', 64);
 
 /**
+ * Implementation of hook_hooks
+ */
+function user_hooks() {
+  return array('auth', 'perm', 'profile_alter', 'user', 'user_operations');
+}
+/**
  * Invokes hook_user() in every module.
  *
  * We cannot use module_invoke() for this, because the arguments need to
@@ -2746,4 +2752,3 @@ function user_forms() {
   $forms['user_admin_new_role']['callback'] = 'user_admin_role';
   return $forms;
 }
-

=== modified file 'update.php'
--- update.php	2006-12-25 21:22:03 +0000
+++ update.php	2007-02-09 06:41:29 +0000
@@ -288,7 +288,7 @@ function update_fix_watchdog() {
  *   TRUE if the update was finished. Otherwise, FALSE.
  */
 function update_data($module, $number) {
-  $ret = module_invoke($module, 'update_'. $number);
+  $ret = module_invoke_hook($module, 'update_'. $number);
   // Assume the update finished unless the update results indicate otherwise.
   $finished = 1;
   if (isset($ret['#finished'])) {

