=== added file 'includes/module_admin.inc'
--- includes/module_admin.inc	1970-01-01 00:00:00 +0000
+++ includes/module_admin.inc	2007-04-08 03:15:12 +0000
@@ -0,0 +1,312 @@
+<?php
+
+function _module_rebuild_cache() {
+  // Get current list of modules
+  $files = drupal_system_listing('\.module$', 'modules', 'name', 0);
+
+  // Extract current files from database.
+  system_get_files_database($files, 'module');
+
+  ksort($files);
+
+  foreach ($files as $filename => $file) {
+    $file->info = _module_parse_info_file(dirname($file->filename) .'/'. $file->name .'.info');
+    // Skip modules that don't provide info.
+    if (empty($file->info)) {
+      unset($files[$filename]);
+      continue;
+    }
+    $files[$filename]->info = $file->info;
+
+    // log the critical hooks implemented by this module
+    $bootstrap = 0;
+    foreach (bootstrap_hooks() as $hook) {
+      if (module_hook($file->name, $hook)) {
+        $bootstrap = 1;
+        break;
+      }
+    }
+
+    // Update the contents of the system table:
+    // TODO: We shouldn't actually need this description field anymore. Remove me next release.
+    if (isset($file->status) || (isset($file->old_filename) && $file->old_filename != $file->filename)) {
+      db_query("UPDATE {system} SET description = '%s', name = '%s', filename = '%s', bootstrap = %d WHERE filename = '%s'", $file->info['description'], $file->name, $file->filename, $bootstrap, $file->old_filename);
+    }
+    else {
+      // This is a new module.
+      $files[$filename]->status = 0;
+      $files[$filename]->throttle = 0;
+      db_query("INSERT INTO {system} (name, description, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, $file->info['description'], 'module', $file->filename, 0, 0, $bootstrap);
+    }
+  }
+  $files = _module_build_dependents($files);
+  return $files;
+}
+
+/**
+ * Find dependents; modules that are required by other modules.
+ * Adds an array of dependents to the $file->info array.
+ *
+ * @return
+ *   The list of files array with dependents added where applicable.
+ */
+function _module_build_dependents($files) {
+  foreach ($files as $filename => $file) {
+    if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
+      foreach ($file->info['dependencies'] as $dependency) {
+        if (!empty($files[$dependency]) && is_array($files[$dependency]->info)) {
+          if (!isset($files[$dependency]->info['dependents'])) {
+            $files[$dependency]->info['dependents'] = array();
+          }
+          $files[$dependency]->info['dependents'][] = $filename;
+        }
+      }
+    }
+  }
+  return $files;
+}
+
+/**
+ * Parse Drupal info file format.
+ * Uses ini parser provided by php's parse_ini_file().
+ *
+ * Files should use the ini format to specify values.
+ * e.g.
+ * key = "value"
+ * key2 = value2
+ *
+ * Some things to be aware of:
+ * - This function is NOT for placing arbitrary module-specific settings. Use variable_get()
+ *   and variable_set() for that.
+ * - You may not use double-quotes in a value.
+ *
+ * Information stored in the module.info file:
+ * name - The real name of the module for display purposes.
+ * description - A brief description of the module.
+ * dependencies - A space delimited list of the short names (shortname) of other modules this module depends on.
+ * package - The name of the package of modules this module belongs to.
+ *
+ * Example of .info file:
+ *   name = Forum
+ *   description = Enables threaded discussions about general topics.
+ *   dependencies = taxonomy comment
+ *   package = Core - optional
+ *
+ * @param $filename
+ *   The file we are parsing. Accepts file with relative or absolute path.
+ * @return
+ *   The info array.
+ */
+function _module_parse_info_file($filename) {
+  $info = array();
+
+  if (file_exists($filename)) {
+    $info = parse_ini_file($filename);
+
+    if (isset($info['dependencies'])) {
+      $info['dependencies'] = explode(" ", $info['dependencies']);
+    }
+    else {
+      $info['dependencies'] = NULL;
+    }
+  }
+  return $info;
+}
+
+function _module_enable($module_list) {
+  $invoke_modules = array();
+  foreach ($module_list as $module) {
+    $existing = db_fetch_object(db_query("SELECT name, status FROM {system} WHERE type = 'module' AND name = '%s'", $module));
+    if ($existing->status === '0') {
+      module_load_install($module);
+      db_query("UPDATE {system} SET status = 1, throttle = 0 WHERE type = 'module' AND name = '%s'", $module);
+      drupal_load('module', $module);
+      $invoke_modules[] = $module;
+    }
+  }
+
+  if (!empty($invoke_modules)) {
+    // Refresh the module list to include the new enabled module.
+    module_list(TRUE, FALSE);
+    // Force to regenerate the stored list of hook implementations.
+    module_build_registry_cache();
+  }
+
+  foreach ($invoke_modules as $module) {
+    module_invoke($module, 'enable');
+  }
+}
+
+function _module_disable($module_list) {
+  $invoke_modules = array();
+  foreach ($module_list as $module) {
+    if (module_exists($module)) {
+      module_load_install($module);
+      module_invoke($module, 'disable');
+      db_query("UPDATE {system} SET status = 0, throttle = 0 WHERE type = 'module' AND name = '%s'", $module);
+      $invoke_modules[] = $module;
+    }
+  }
+
+  if (!empty($invoke_modules)) {
+    // Refresh the module list to exclude the disabled modules.
+    module_list(TRUE, FALSE);
+    // Force to regenerate the stored list of hook implementations.
+    module_build_registry_cache();
+  }
+}
+
+/**
+ * @defgroup hooks Hooks
+ * @{
+ * Allow modules to interact with the Drupal core.
+ *
+ * Drupal's module system is based on the concept of "hooks". A hook is a PHP
+ * function that is named foo_bar(), where "foo" is the name of the module (whose
+ * filename is thus foo.module) and "bar" is the name of the hook. Each hook has
+ * a defined set of parameters and a specified result type.
+ *
+ * To extend Drupal, a module need simply implement a hook. When Drupal wishes to
+ * allow intervention from modules, it determines which modules implement a hook
+ * and call that hook in all enabled modules that implement it.
+ *
+ * The available hooks to implement are explained here in the Hooks section of
+ * the developer documentation. The string "hook" is used as a placeholder for
+ * the module name is the hook definitions. For example, if the module file is
+ * called example.module, then hook_help() as implemented by that module would be
+ * defined as example_help().
+ */
+
+/**
+ * Write the registry cache into the database.
+ */
+function _module_save_registry_cache() {
+  // TODO: use a separate cache for this.
+  cache_set('module_registry', 'cache', serialize(_module_get_registry_cache()));
+}
+
+
+/**
+ * Clear the module registry cache.
+ */
+function _module_clear_registry_cache() {
+  _module_set_registry_cache('@reset');
+  module_get_hooks(TRUE);
+  
+}
+
+/**
+ * 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.
+ *
+ * @param $hook
+ *   an array in the following format:
+ *
+ *     '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_set_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($reset = FALSE) {
+  static $cache = array();
+  if ($reset) {
+    $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(TRUE, FALSE) as $module) {
+      $hooks = module_invoke_hook($module, 'hooks');
+      if (is_array($hooks)) {
+        $cache = array_merge($cache, $hooks);
+      }
+    }
+  }
+  return $cache;
+}

=== modified file 'includes/install.inc'
--- includes/install.inc	2007-03-27 05:13:53 +0000
+++ includes/install.inc	2007-04-08 03:15:12 +0000
@@ -103,10 +103,7 @@ function drupal_install_profile_name() {
 
   if (!isset($name)) {
     // Load profile details.
-    $function = $profile .'_profile_details';
-    if (function_exists($function)) {
-      $details = $function();
-    }
+    $details = module_invoke_hook($profile, 'profile_details');
     $name = isset($details['name']) ? $details['name'] : 'Drupal';
   }
 
@@ -147,8 +144,7 @@ function drupal_detect_database_types() 
   foreach (array('mysql', 'mysqli', 'pgsql') as $type) {
     if (file_exists('./includes/install.'. $type .'.inc')) {
       include_once './includes/install.'. $type .'.inc';
-      $function = $type .'_is_available';
-      if ($function()) {
+      if (module_invoke_hook($type, 'is_available')) {
         $databases[$type] = $type;
       }
     }
@@ -271,8 +267,7 @@ function drupal_verify_profile($profile,
   require_once($profile_file);
 
   // Get a list of modules required by this profile.
-  $function = $profile .'_profile_modules';
-  $module_list = array_merge(drupal_required_modules(), $function(), ($locale ? array('locale') : array()));
+  $module_list = array_merge(drupal_required_modules(), module_invoke_hook($profile, 'profile_modules'), ($locale ? array('locale') : array()));
 
   // Get a list of modules that exist in Drupal's assorted subdirectories.
   $present_modules = array();
@@ -308,7 +303,7 @@ function drupal_install_profile($profile
 
   $system_path = dirname(drupal_get_filename('module', 'system', NULL));
   require_once './' . $system_path . '/system.install';
-  module_invoke('system', 'install');
+  module_invoke_hook('system', 'install');
   $system_versions = drupal_get_schema_versions('system');
   $system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED;
   db_query("INSERT INTO {system} (filename, name, type, description, status, throttle, bootstrap, schema_version) VALUES('%s', '%s', 'module', '', 1, 0, 0, %d)", $system_path . '/system.module', 'system', $system_version);
@@ -319,6 +314,8 @@ function drupal_install_profile($profile
   // Install schemas for profile and all its modules.
   module_rebuild_cache();
   drupal_install_modules($module_list);
+  // Force to regenerate the stored list of hook implementations.
+  module_build_registry_cache();
 }
 
 
@@ -335,7 +332,7 @@ function drupal_install_modules($module_
   foreach ($module_list as $module) {
     if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
       module_load_install($module);
-      module_invoke($module, 'install');
+      module_invoke_hook($module, 'install');
       $versions = drupal_get_schema_versions($module);
       drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
       $enable_modules[] = $module;
@@ -353,7 +350,7 @@ function drupal_install_modules($module_
  */
 function drupal_uninstall_module($module) {
   module_load_install($module);
-  module_invoke($module, 'uninstall');
+  module_invoke_hook($module, 'uninstall');
   drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
 }
 
@@ -680,8 +677,7 @@ function drupal_check_profile($profile) 
   require_once($profile_file);
 
   // Get a list of modules required by this profile.
-  $function = $profile .'_profile_modules';
-  $module_list = array_unique(array_merge(drupal_required_modules(), $function()));
+  $module_list = array_unique(array_merge(drupal_required_modules(), module_invoke_hook($profile, 'profile_modules')));
 
   // Get a list of all .install files.
   $installs = drupal_get_install_files($module_list);
@@ -690,9 +686,7 @@ function drupal_check_profile($profile) 
   $requirements = array();
   foreach ($installs as $install) {
     require_once $install->filename;
-    if (module_hook($install->name, 'requirements')) {
-      $requirements = array_merge($requirements, module_invoke($install->name, 'requirements', 'install'));
-    }
+    $requirements = array_merge($requirements, (array)module_invoke_hook($install->name, 'requirements', 'install'));
   }
   return $requirements;
 }
@@ -720,7 +714,7 @@ function drupal_check_module($module) {
     require_once $install[$module]->filename;
 
     // Check requirements
-    $requirements = module_invoke($module, 'requirements', 'install');
+    $requirements = module_invoke_hook($module, 'requirements', 'install');
     if (is_array($requirements) && drupal_requirements_severity($requirements) == REQUIREMENT_ERROR) {
       // Print any error messages
       foreach ($requirements as $requirement) {

=== modified file 'includes/module.inc'
--- includes/module.inc	2007-02-04 21:20:50 +0000
+++ includes/module.inc	2007-04-08 03:31:07 +0000
@@ -37,48 +37,37 @@ function module_iterate($function, $argu
  * @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 $fixed_list
- *   (Optional) Override the module list with the given modules. Stays until the
- *   next call with $refresh = TRUE.
  * @return
  *   An associative array whose keys and values are the names of all loaded
  *   modules.
  */
-function module_list($refresh = FALSE, $bootstrap = TRUE, $sort = FALSE, $fixed_list = NULL) {
+function module_list($refresh = FALSE, $bootstrap = TRUE, $sort = FALSE) {
   static $list, $sorted_list;
 
-  if ($refresh || $fixed_list) {
-    unset($sorted_list);
+  if ($refresh) {
+    $sorted_list = array();
     $list = array();
-    if ($fixed_list) {
-      foreach ($fixed_list as $name => $module) {
-        drupal_get_filename('module', $name, $module['filename']);
-        $list[$name] = $name;
-      }
+    if ($bootstrap) {
+      $result = db_query("SELECT name, filename, throttle, bootstrap FROM {system} WHERE type = 'module' AND status = 1 AND bootstrap = 1 ORDER BY weight ASC, filename ASC");
     }
     else {
-      if ($bootstrap) {
-        $result = db_query("SELECT name, filename, throttle, bootstrap FROM {system} WHERE type = 'module' AND status = 1 AND bootstrap = 1 ORDER BY weight ASC, filename ASC");
-      }
-      else {
-        $result = db_query("SELECT name, filename, throttle, bootstrap FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC");
-      }
-      while ($module = db_fetch_object($result)) {
-        if (file_exists($module->filename)) {
-          // Determine the current throttle status and see if the module should be
-          // loaded based on server load. We have to directly access the throttle
-          // variables, since throttle.module may not be loaded yet.
-          $throttle = ($module->throttle && variable_get('throttle_level', 0) > 0);
-          if (!$throttle) {
-            drupal_get_filename('module', $module->name, $module->filename);
-            $list[$module->name] = $module->name;
-          }
+      $result = db_query("SELECT name, filename, throttle, bootstrap FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC");
+    }
+    while ($module = db_fetch_object($result)) {
+      if (file_exists($module->filename)) {
+        // Determine the current throttle status and see if the module should be
+        // loaded based on server load. We have to directly access the throttle
+        // variables, since throttle.module may not be loaded yet.
+        $throttle = ($module->throttle && variable_get('throttle_level', 0) > 0);
+        if (!$throttle) {
+          drupal_get_filename('module', $module->name, $module->filename);
+          $list[$module->name] = $module->name;
         }
       }
     }
   }
   if ($sort) {
-    if (!isset($sorted_list)) {
+    if (empty($sorted_list)) {
       $sorted_list = $list;
       ksort($sorted_list);
     }
@@ -88,125 +77,6 @@ function module_list($refresh = FALSE, $
 }
 
 /**
- * Rebuild the database cache of module files.
- *
- * @return
- *   The array of filesystem objects used to rebuild the cache.
- */
-function module_rebuild_cache() {
-  // Get current list of modules
-  $files = drupal_system_listing('\.module$', 'modules', 'name', 0);
-
-  // Extract current files from database.
-  system_get_files_database($files, 'module');
-
-  ksort($files);
-
-  foreach ($files as $filename => $file) {
-    $file->info = _module_parse_info_file(dirname($file->filename) .'/'. $file->name .'.info');
-    // Skip modules that don't provide info.
-    if (empty($file->info)) {
-      unset($files[$filename]);
-      continue;
-    }
-    $files[$filename]->info = $file->info;
-
-    // log the critical hooks implemented by this module
-    $bootstrap = 0;
-    foreach (bootstrap_hooks() as $hook) {
-      if (module_hook($file->name, $hook)) {
-        $bootstrap = 1;
-        break;
-      }
-    }
-
-    // Update the contents of the system table:
-    // TODO: We shouldn't actually need this description field anymore. Remove me next release.
-    if (isset($file->status) || (isset($file->old_filename) && $file->old_filename != $file->filename)) {
-      db_query("UPDATE {system} SET description = '%s', name = '%s', filename = '%s', bootstrap = %d WHERE filename = '%s'", $file->info['description'], $file->name, $file->filename, $bootstrap, $file->old_filename);
-    }
-    else {
-      // This is a new module.
-      $files[$filename]->status = 0;
-      $files[$filename]->throttle = 0;
-      db_query("INSERT INTO {system} (name, description, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, $file->info['description'], 'module', $file->filename, 0, 0, $bootstrap);
-    }
-  }
-  $files = _module_build_dependents($files);
-  return $files;
-}
-
-/**
- * Find dependents; modules that are required by other modules.
- * Adds an array of dependents to the $file->info array.
- *
- * @return
- *   The list of files array with dependents added where applicable.
- */
-function _module_build_dependents($files) {
-  foreach ($files as $filename => $file) {
-    if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
-      foreach ($file->info['dependencies'] as $dependency) {
-        if (!empty($files[$dependency]) && is_array($files[$dependency]->info)) {
-          if (!isset($files[$dependency]->info['dependents'])) {
-            $files[$dependency]->info['dependents'] = array();
-          }
-          $files[$dependency]->info['dependents'][] = $filename;
-        }
-      }
-    }
-  }
-  return $files;
-}
-
-/**
- * Parse Drupal info file format.
- * Uses ini parser provided by php's parse_ini_file().
- *
- * Files should use the ini format to specify values.
- * e.g.
- * key = "value"
- * key2 = value2
- *
- * Some things to be aware of:
- * - This function is NOT for placing arbitrary module-specific settings. Use variable_get()
- *   and variable_set() for that.
- * - You may not use double-quotes in a value.
- *
- * Information stored in the module.info file:
- * name - The real name of the module for display purposes.
- * description - A brief description of the module.
- * dependencies - A space delimited list of the short names (shortname) of other modules this module depends on.
- * package - The name of the package of modules this module belongs to.
- *
- * Example of .info file:
- *   name = Forum
- *   description = Enables threaded discussions about general topics.
- *   dependencies = taxonomy comment
- *   package = Core - optional
- *
- * @param $filename
- *   The file we are parsing. Accepts file with relative or absolute path.
- * @return
- *   The info array.
- */
-function _module_parse_info_file($filename) {
-  $info = array();
-
-  if (file_exists($filename)) {
-    $info = parse_ini_file($filename);
-
-    if (isset($info['dependencies'])) {
-      $info['dependencies'] = explode(" ", $info['dependencies']);
-    }
-    else {
-      $info['dependencies'] = NULL;
-    }
-  }
-  return $info;
-}
-
-/**
  * Determine whether a given module exists.
  *
  * @param $module
@@ -220,153 +90,121 @@ function module_exists($module) {
 }
 
 /**
- * Load a module's installation hooks.
+ * Determine whether a module implements a hook.
+ *
+ * @param $module
+ *   The name of the module (without the .module extension).
+ * @param $hook
+ *   The name of the hook (e.g. "help" or "menu").
+ * @return
+ *   TRUE if the module is both installed and enabled, and the hook is
+ *   implemented in that module.
  */
-function module_load_install($module) {
-  // Make sure the installation API is available
-  include_once './includes/install.inc';
-
-  $install_file = './'. drupal_get_path('module', $module) .'/'. $module .'.install';
-  if (is_file($install_file)) {
-    include_once $install_file;
-  }
+function module_hook($module, $hook) {
+  $registry = _module_get_registry_cache();
+  return isset($registry[$hook][$module]) && is_array($registry[$hook][$module]);
 }
 
 /**
- * Enable a given list of modules.
+ * 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_list
- *   An array of module names.
+ * @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_enable($module_list) {
-  $invoke_modules = array();
-  foreach ($module_list as $module) {
-    $existing = db_fetch_object(db_query("SELECT name, status FROM {system} WHERE type = 'module' AND name = '%s'", $module));
-    if ($existing->status === '0') {
-      module_load_install($module);
-      db_query("UPDATE {system} SET status = 1, throttle = 0 WHERE type = 'module' AND name = '%s'", $module);
-      drupal_load('module', $module);
-      $invoke_modules[] = $module;
-    }
-  }
-
-  if (!empty($invoke_modules)) {
-    // 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);
-  }
+function module_get_function($module, $hook) {
+  $registry = _module_get_registry_cache();
 
-  foreach ($invoke_modules as $module) {
-    module_invoke($module, 'enable');
+  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'];
   }
 }
 
 /**
- * Disable a given set of modules.
+ * Determine which modules are implementing a hook.
  *
- * @param $module_list
- *   An array of module names.
+ * @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.
+ * @return
+ *   An array with the names of the modules which are implementing this hook.
  */
-function module_disable($module_list) {
-  $invoke_modules = array();
-  foreach ($module_list as $module) {
-    if (module_exists($module)) {
-      module_load_install($module);
-      module_invoke($module, 'disable');
-      db_query("UPDATE {system} SET status = 0, throttle = 0 WHERE type = 'module' AND name = '%s'", $module);
-      $invoke_modules[] = $module;
-    }
-  }
+function module_implements($hook, $sort = FALSE) {
+  $registry = _module_get_registry_cache();
 
-  if (!empty($invoke_modules)) {
-    // 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);
+  if (isset($registry[$hook])) {
+    return array_keys($registry[$hook]);
   }
+  return array();
 }
 
 /**
- * @defgroup hooks Hooks
- * @{
- * Allow modules to interact with the Drupal core.
- *
- * Drupal's module system is based on the concept of "hooks". A hook is a PHP
- * function that is named foo_bar(), where "foo" is the name of the module (whose
- * filename is thus foo.module) and "bar" is the name of the hook. Each hook has
- * a defined set of parameters and a specified result type.
- *
- * To extend Drupal, a module need simply implement a hook. When Drupal wishes to
- * allow intervention from modules, it determines which modules implement a hook
- * and call that hook in all enabled modules that implement it.
- *
- * The available hooks to implement are explained here in the Hooks section of
- * the developer documentation. The string "hook" is used as a placeholder for
- * the module name is the hook definitions. For example, if the module file is
- * called example.module, then hook_help() as implemented by that module would be
- * defined as example_help().
- */
-
-/**
- * Determine whether a module implements a hook.
+ * 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 (e.g. "help" or "menu").
+ *   The name of the hook to invoke.
+ * @param ...
+ *   Arguments to pass to the hook implementation.
  * @return
- *   TRUE if the module is both installed and enabled, and the hook is
- *   implemented in that module.
+ *   The return value of the hook implementation.
  */
-function module_hook($module, $hook) {
-  return function_exists($module .'_'. $hook);
+function module_invoke() {
+  $args = func_get_args();
+  $module = array_shift($args);
+  $hook = array_shift($args);
+
+  if ($function = module_get_function($module, $hook)) {
+    return call_user_func_array($function, $args);
+  }
 }
 
 /**
- * Determine which modules are implementing a hook.
+ * Invoke a hook in a particular module. This function passes the first
+ * parameter directly so it may be a reference.
  *
+ * @param $module
+ *   The name of the module (without the .module extension).
  * @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,
- *   before processing hook_enable).
+ *   The name of the hook to invoke.
+ * @param $ref1
+ *   The first parameter that will be passed as a reference; it is
+ *   required.
+ * @param ...
+ *   Arguments to pass to the hook implementation.
  * @return
- *   An array with the names of the modules which are implementing this hook.
+ *   The return value of the hook implementation.
  */
-function module_implements($hook, $sort = FALSE, $refresh = FALSE) {
-  static $implementations;
-
-  if ($refresh) {
-    unset($implementations);
-    return;
-  }
+function module_invoke_ref1($module, $hook, &$ref1) {
+  // Take the full function args, and remove the first 2 items.
+  $args = array_splice(func_get_args(), 0, 2);
+  
+  // Put our reference back into the array as a reference.
+  $args[0] = &$ref1;
 
-  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;
-      }
-    }
+  if ($function = module_get_function($module, $hook)) {
+    return call_user_func_array($function, $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];
 }
 
 /**
- * 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,17 +215,36 @@ 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);
   }
 }
+
+function _module_invoke_all($hook, &$args) {
+  $return = array();
+  foreach (module_implements($hook) as $module) {
+    $function = module_get_function($module, $hook);
+    $result = call_user_func_array($function, $args);
+
+    if (isset($result) && is_array($result)) {
+      $return = array_merge($return, $result);
+    }
+    else if (isset($result)) {
+      $return[] = $result;
+    }
+  }
+
+  return $return;
+}
 /**
- * Invoke a hook in all enabled modules that implement it.
+ * Invoke a hook in all enabled modules that implement it. Because references
+ * don't survive func_get_args without help, this function passes all values
+ * by value, not reference.
  *
  * @param $hook
  *   The name of the hook to invoke.
@@ -400,19 +257,107 @@ function module_invoke() {
 function module_invoke_all() {
   $args = func_get_args();
   $hook = array_shift($args);
-  $return = array();
-  foreach (module_implements($hook) as $module) {
-    $function = $module .'_'. $hook;
-    $result = call_user_func_array($function, $args);
-    if (isset($result) && is_array($result)) {
-      $return = array_merge($return, $result);
-    }
-    else if (isset($result)) {
-      $return[] = $result;
+
+  return _module_invoke_all($hook, $args);
+}
+
+/**
+ * Invoke a hook in all enabled modules that implement it. The first
+ * argument is required and will be passed as a reference.
+ *
+ * @param $hook
+ *   The name of the hook to invoke.
+ * @param &$ref1
+ *   An argument that will be passed as a reference.
+ * @param ...
+ *   Arguments to pass to the hook.
+ * @return
+ *   An array of return values of the hook implementations. If modules return
+ *   arrays from their implementations, those are merged into one array.
+ */
+function module_invoke_all_ref1($hook, &$ref1) {
+  // Take the full function args, and remove the first item.
+  $args = func_get_args();
+  array_shift($args);
+  // Put our reference back into the array -- as a reference --
+  // and overwriting $hook.
+  $args[0] = &$ref1;
+
+  return _module_invoke_all($hook, $args);
+}
+
+/**
+ * Invoke a hook in all enabled modules that implement it. The first
+ * two arguments are required and will be passed as references.
+ *
+ * @param $hook
+ *   The name of the hook to invoke.
+ * @param &$ref1
+ *   An argument that will be passed as a reference.
+ * @param &$ref2
+ *   A second argument that will be passed as a reference.
+ * @param ...
+ *   Arguments to pass to the hook.
+ * @return
+ *   An array of return values of the hook implementations. If modules return
+ *   arrays from their implementations, those are merged into one array.
+ */
+function module_invoke_all_ref2($hook, &$ref1, &$ref2) {
+  // Take the full function args, and remove the first item.
+  $args = array_splice(func_get_args(), 0, 1);
+  // Put our reference back into the array -- as a reference --
+  // and overwriting $hook.
+  $args[0] = &$ref1;
+  $args[1] = &$ref2;
+
+  return _module_invoke_all($hook, $args);
+}
+
+/**
+ * Add a module's combined information into the registry cache.
+ */
+function _module_set_registry_cache($registry, $module = NULL, $info = NULL) {
+  return _module_get_registry_cache($registry, $module, $info);
+}
+ 
+function _module_get_registry_cache($registry = NULL, $module = NULL, $info = NULL) {
+  static $registry_storage = NULL;
+  if (!isset($registry)) {
+    if (!isset($registry_storage)) {
+      _module_load_registry_cache();
     }
+    return $registry_storage;
   }
 
-  return $return;
+  if ($registry == '@reset') {
+    $registry_storage = array();
+    return;
+  }
+
+  if (is_array($registry)) {
+    $registry_storage = $registry;
+    return;
+  }
+
+  $registry_storage[$registry][$module] = $info;
+
+  return $registry_storage;
+}
+
+/**
+ * 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_set_registry_cache(unserialize($registry->data));
+  }
+  else {
+    include_once './includes/module_admin.inc';
+    module_build_registry_cache();
+  }
 }
 
 /**
@@ -425,3 +370,50 @@ function module_invoke_all() {
 function drupal_required_modules() {
   return array('block', 'filter', 'node', 'system', 'user', 'watchdog');
 }
+
+/**
+ * Rebuild the database cache of module files.
+ *
+ * @return
+ *   The array of filesystem objects used to rebuild the cache.
+ */
+function module_rebuild_cache() {
+  include_once './includes/module_admin.inc';
+  return _module_rebuild_cache();
+}
+
+/**
+ * Enable a given list of modules.
+ *
+ * @param $module_list
+ *   An array of module names.
+ */
+function module_enable($module_list) {
+  include_once './includes/module_admin.inc';
+  _module_enable($module_list);
+}
+
+/**
+ * Disable a given set of modules.
+ *
+ * @param $module_list
+ *   An array of module names.
+ */
+function module_disable($module_list) {
+  include_once './includes/module_admin.inc';
+  _module_disable($module_list);
+}
+
+/**
+ * Load a module's installation hooks.
+ */
+function module_load_install($module) {
+  // Make sure the installation API is available
+  include_once './includes/install.inc';
+
+  $install_file = './'. drupal_get_path('module', $module) .'/'. $module .'.install';
+  if (is_file($install_file)) {
+    include_once $install_file;
+  }
+}
+

=== modified file 'includes/theme.inc'
--- includes/theme.inc	2007-04-06 14:31:51 +0000
+++ includes/theme.inc	2007-04-08 03:15:12 +0000
@@ -177,6 +177,7 @@ function _theme_process_registry(&$cache
  */
 function _theme_build_registry($theme, $theme_engine) {
   $cache = array();
+
   foreach (module_implements('theme') as $module) {
     _theme_process_registry($cache, $module, 'module');
   }

=== modified file 'install.php'
--- install.php	2007-03-25 23:34:17 +0000
+++ install.php	2007-04-08 03:33:48 +0000
@@ -2,6 +2,7 @@
 // $Id: install.php,v 1.40 2007/03/25 23:34:17 dries Exp $
 
 require_once './includes/install.inc';
+require_once './includes/module.inc';
 
 /**
  * The Drupal installation happens in a series of steps. We begin by verifying
@@ -39,10 +40,13 @@ function install_main() {
   }
 
   // Load module basics (needed for hook invokes).
-  include_once './includes/module.inc';
-  $module_list['system']['filename'] = 'modules/system/system.module';
-  $module_list['filter']['filename'] = 'modules/filter/filter.module';
-  module_list(TRUE, FALSE, FALSE, $module_list);
+  $registry = array(
+    'elements' => array('system' => array('function' => 'system_elements', 'file' => NULL)),
+  );
+  _module_set_registry_cache($registry);
+  // Do not even try to consult the database about these modules.
+  drupal_get_filename('module', 'system', 'modules/system/system.module');
+  drupal_get_filename('module', 'filter', 'modules/filter/filter.module');
   drupal_load('module', 'system');
   drupal_load('module', 'filter');
 

=== modified file 'modules/block/block.module'
--- modules/block/block.module	2007-04-06 13:27:20 +0000
+++ modules/block/block.module	2007-04-08 03:15:12 +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-04-06 13:27:20 +0000
+++ modules/comment/comment.module	2007-04-08 03:15:12 +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) {
@@ -344,9 +350,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.
 
@@ -2062,4 +2066,3 @@ function int2vancode($i = 0) {
 function vancode2int($c = '00') {
   return base_convert(substr($c, 1), 36, 10);
 }
-

=== modified file 'modules/filter/filter.module'
--- modules/filter/filter.module	2007-04-06 13:27:20 +0000
+++ modules/filter/filter.module	2007-04-08 03:15:12 +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) {
@@ -1508,4 +1514,3 @@ function filter_xss_bad_protocol($string
 /**
  * @} End of "Standard filters".
  */
-

=== modified file 'modules/node/node.module'
--- modules/node/node.module	2007-04-06 13:27:20 +0000
+++ modules/node/node.module	2007-04-08 03:15:12 +0000
@@ -10,6 +10,36 @@
 define('NODE_NEW_LIMIT', time() - 30 * 24 * 60 * 60);
 
 /**
+ * Implementation of hook_hooks
+ */
+function node_hooks() {
+  return array('access', 'delete', 'insert', 'load',
+               'node_access_records', 'node_info', 'node_grants', 'node_operations',
+               'node_type', 'prepare', 'nodeapi', 'submit', 'validate', 'view');
+}
+
+/**
+ * node pollutes its own namespace.
+ */
+function node_register_hooks() {
+  module_register_hook('access', 'node_content_access');
+  module_register_hook('form', 'node_content_form');
+  module_register_hook('help');
+  module_register_hook('perm');
+  module_register_hook('cron');
+  module_register_hook('search');
+  module_register_hook('user');
+  module_register_hook('menu');
+  module_register_hook('block');
+  module_register_hook('update_index');
+  module_register_hook('form_alter');
+  module_register_hook('node_operations');
+  module_register_hook('db_rewrite_sql');
+  module_register_hook('forms');
+  module_register_hook('link');
+  module_register_hook('init');
+}
+/**
  * Implementation of hook_help().
  */
 function node_help($section) {
@@ -455,9 +485,6 @@ 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.
-  }
   return module_hook($module, $hook);
 }
 
@@ -476,10 +503,7 @@ 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;
+    $function = module_get_function($module, $hook);
     return ($function($node, $a2, $a3, $a4));
   }
 }
@@ -497,18 +521,7 @@ function node_invoke(&$node, $hook, $a2 
  *   The returned value of the invoked hooks.
  */
 function node_invoke_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
-  $return = array();
-  foreach (module_implements('nodeapi') as $name) {
-    $function = $name .'_nodeapi';
-    $result = $function($node, $op, $a3, $a4);
-    if (isset($result) && is_array($result)) {
-      $return = array_merge($return, $result);
-    }
-    else if (isset($result)) {
-      $return[] = $result;
-    }
-  }
-  return $return;
+  return module_invoke_all_ref1('nodeapi', $node, $op, $a3, $a4);
 }
 
 /**
@@ -2676,9 +2689,6 @@ 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.
-  }
   $access = module_invoke($module, 'access', $op, $node);
   if (!is_null($access)) {
     return $access;

=== modified file 'modules/search/search.module'
--- modules/search/search.module	2007-04-06 13:27:20 +0000
+++ modules/search/search.module	2007-04-08 03:15:12 +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-04-06 14:31:51 +0000
+++ modules/system/system.module	2007-04-08 03:15:12 +0000
@@ -87,6 +87,19 @@ 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',
+               'menu', 'ping', 'requirements', 'theme', 'throttle', 'uninstall',
+               'xmlrpc');
+}
+/**
  * Implementation of hook_elements().
  */
 function system_elements() {

=== modified file 'modules/taxonomy/taxonomy.module'
--- modules/taxonomy/taxonomy.module	2007-04-06 13:27:20 +0000
+++ modules/taxonomy/taxonomy.module	2007-04-08 03:15:12 +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-04-06 14:27:22 +0000
+++ modules/user/user.module	2007-04-08 03:15:12 +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
@@ -2804,4 +2810,3 @@ function user_forms() {
   $forms['user_admin_new_role']['callback'] = 'user_admin_role';
   return $forms;
 }
-

=== modified file 'update.php'
--- update.php	2007-04-02 15:15:50 +0000
+++ update.php	2007-04-08 03:15:12 +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'])) {

