cms/drupal/includes/module.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * API for loading and interacting with Drupal modules.
       
     6  */
       
     7 
       
     8 /**
       
     9  * Loads all the modules that have been enabled in the system table.
       
    10  *
       
    11  * @param $bootstrap
       
    12  *   Whether to load only the reduced set of modules loaded in "bootstrap mode"
       
    13  *   for cached pages. See bootstrap.inc.
       
    14  *
       
    15  * @return
       
    16  *   If $bootstrap is NULL, return a boolean indicating whether all modules
       
    17  *   have been loaded.
       
    18  */
       
    19 function module_load_all($bootstrap = FALSE) {
       
    20   static $has_run = FALSE;
       
    21 
       
    22   if (isset($bootstrap)) {
       
    23     foreach (module_list(TRUE, $bootstrap) as $module) {
       
    24       drupal_load('module', $module);
       
    25     }
       
    26     // $has_run will be TRUE if $bootstrap is FALSE.
       
    27     $has_run = !$bootstrap;
       
    28   }
       
    29   return $has_run;
       
    30 }
       
    31 
       
    32 
       
    33 /**
       
    34  * Returns a list of currently active modules.
       
    35  *
       
    36  * Usually, this returns a list of all enabled modules. When called early on in
       
    37  * the bootstrap, it will return a list of vital modules only (those needed to
       
    38  * generate cached pages).
       
    39  *
       
    40  * All parameters to this function are optional and should generally not be
       
    41  * changed from their defaults.
       
    42  *
       
    43  * @param $refresh
       
    44  *   (optional) Whether to force the module list to be regenerated (such as
       
    45  *   after the administrator has changed the system settings). Defaults to
       
    46  *   FALSE.
       
    47  * @param $bootstrap_refresh
       
    48  *   (optional) When $refresh is TRUE, setting $bootstrap_refresh to TRUE forces
       
    49  *   the module list to be regenerated using the reduced set of modules loaded
       
    50  *   in "bootstrap mode" for cached pages. Otherwise, setting $refresh to TRUE
       
    51  *   generates the complete list of enabled modules.
       
    52  * @param $sort
       
    53  *   (optional) By default, modules are ordered by weight and module name. Set
       
    54  *   this option to TRUE to return a module list ordered only by module name.
       
    55  * @param $fixed_list
       
    56  *   (optional) If an array of module names is provided, this will override the
       
    57  *   module list with the given set of modules. This will persist until the next
       
    58  *   call with $refresh set to TRUE or with a new $fixed_list passed in. This
       
    59  *   parameter is primarily intended for internal use (e.g., in install.php and
       
    60  *   update.php).
       
    61  *
       
    62  * @return
       
    63  *   An associative array whose keys and values are the names of the modules in
       
    64  *   the list.
       
    65  */
       
    66 function module_list($refresh = FALSE, $bootstrap_refresh = FALSE, $sort = FALSE, $fixed_list = NULL) {
       
    67   static $list = array(), $sorted_list;
       
    68 
       
    69   if (empty($list) || $refresh || $fixed_list) {
       
    70     $list = array();
       
    71     $sorted_list = NULL;
       
    72     if ($fixed_list) {
       
    73       foreach ($fixed_list as $name => $module) {
       
    74         drupal_get_filename('module', $name, $module['filename']);
       
    75         $list[$name] = $name;
       
    76       }
       
    77     }
       
    78     else {
       
    79       if ($refresh) {
       
    80         // For the $refresh case, make sure that system_list() returns fresh
       
    81         // data.
       
    82         drupal_static_reset('system_list');
       
    83       }
       
    84       if ($bootstrap_refresh) {
       
    85         $list = system_list('bootstrap');
       
    86       }
       
    87       else {
       
    88         // Not using drupal_map_assoc() here as that requires common.inc.
       
    89         $list = array_keys(system_list('module_enabled'));
       
    90         $list = (!empty($list) ? array_combine($list, $list) : array());
       
    91       }
       
    92     }
       
    93   }
       
    94   if ($sort) {
       
    95     if (!isset($sorted_list)) {
       
    96       $sorted_list = $list;
       
    97       ksort($sorted_list);
       
    98     }
       
    99     return $sorted_list;
       
   100   }
       
   101   return $list;
       
   102 }
       
   103 
       
   104 /**
       
   105  * Builds a list of bootstrap modules and enabled modules and themes.
       
   106  *
       
   107  * @param $type
       
   108  *   The type of list to return:
       
   109  *   - module_enabled: All enabled modules.
       
   110  *   - bootstrap: All enabled modules required for bootstrap.
       
   111  *   - theme: All themes.
       
   112  *
       
   113  * @return
       
   114  *   An associative array of modules or themes, keyed by name. For $type
       
   115  *   'bootstrap', the array values equal the keys. For $type 'module_enabled'
       
   116  *   or 'theme', the array values are objects representing the respective
       
   117  *   database row, with the 'info' property already unserialized.
       
   118  *
       
   119  * @see module_list()
       
   120  * @see list_themes()
       
   121  */
       
   122 function system_list($type) {
       
   123   $lists = &drupal_static(__FUNCTION__);
       
   124 
       
   125   // For bootstrap modules, attempt to fetch the list from cache if possible.
       
   126   // if not fetch only the required information to fire bootstrap hooks
       
   127   // in case we are going to serve the page from cache.
       
   128   if ($type == 'bootstrap') {
       
   129     if (isset($lists['bootstrap'])) {
       
   130       return $lists['bootstrap'];
       
   131     }
       
   132     if ($cached = cache_get('bootstrap_modules', 'cache_bootstrap')) {
       
   133       $bootstrap_list = $cached->data;
       
   134     }
       
   135     else {
       
   136       $bootstrap_list = db_query("SELECT name, filename FROM {system} WHERE status = 1 AND bootstrap = 1 AND type = 'module' ORDER BY weight ASC, name ASC")->fetchAllAssoc('name');
       
   137       cache_set('bootstrap_modules', $bootstrap_list, 'cache_bootstrap');
       
   138     }
       
   139     // To avoid a separate database lookup for the filepath, prime the
       
   140     // drupal_get_filename() static cache for bootstrap modules only.
       
   141     // The rest is stored separately to keep the bootstrap module cache small.
       
   142     foreach ($bootstrap_list as $module) {
       
   143       drupal_get_filename('module', $module->name, $module->filename);
       
   144     }
       
   145     // We only return the module names here since module_list() doesn't need
       
   146     // the filename itself.
       
   147     $lists['bootstrap'] = array_keys($bootstrap_list);
       
   148   }
       
   149   // Otherwise build the list for enabled modules and themes.
       
   150   elseif (!isset($lists['module_enabled'])) {
       
   151     if ($cached = cache_get('system_list', 'cache_bootstrap')) {
       
   152       $lists = $cached->data;
       
   153     }
       
   154     else {
       
   155       $lists = array(
       
   156         'module_enabled' => array(),
       
   157         'theme' => array(),
       
   158         'filepaths' => array(),
       
   159       );
       
   160       // The module name (rather than the filename) is used as the fallback
       
   161       // weighting in order to guarantee consistent behavior across different
       
   162       // Drupal installations, which might have modules installed in different
       
   163       // locations in the file system. The ordering here must also be
       
   164       // consistent with the one used in module_implements().
       
   165       $result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC");
       
   166       foreach ($result as $record) {
       
   167         $record->info = unserialize($record->info);
       
   168         // Build a list of all enabled modules.
       
   169         if ($record->type == 'module') {
       
   170           $lists['module_enabled'][$record->name] = $record;
       
   171         }
       
   172         // Build a list of themes.
       
   173         if ($record->type == 'theme') {
       
   174           $lists['theme'][$record->name] = $record;
       
   175         }
       
   176         // Build a list of filenames so drupal_get_filename can use it.
       
   177         if ($record->status) {
       
   178           $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename);
       
   179         }
       
   180       }
       
   181       foreach ($lists['theme'] as $key => $theme) {
       
   182         if (!empty($theme->info['base theme'])) {
       
   183           // Make a list of the theme's base themes.
       
   184           require_once DRUPAL_ROOT . '/includes/theme.inc';
       
   185           $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key);
       
   186           // Don't proceed if there was a problem with the root base theme.
       
   187           if (!current($lists['theme'][$key]->base_themes)) {
       
   188             continue;
       
   189           }
       
   190           // Determine the root base theme.
       
   191           $base_key = key($lists['theme'][$key]->base_themes);
       
   192           // Add to the list of sub-themes for each of the theme's base themes.
       
   193           foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) {
       
   194             $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name'];
       
   195           }
       
   196           // Add the base theme's theme engine info.
       
   197           $lists['theme'][$key]->info['engine'] = isset($lists['theme'][$base_key]->info['engine']) ? $lists['theme'][$base_key]->info['engine'] : 'theme';
       
   198         }
       
   199         else {
       
   200           // A plain theme is its own engine.
       
   201           $base_key = $key;
       
   202           if (!isset($lists['theme'][$key]->info['engine'])) {
       
   203             $lists['theme'][$key]->info['engine'] = 'theme';
       
   204           }
       
   205         }
       
   206         // Set the theme engine prefix.
       
   207         $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];
       
   208       }
       
   209       cache_set('system_list', $lists, 'cache_bootstrap');
       
   210     }
       
   211     // To avoid a separate database lookup for the filepath, prime the
       
   212     // drupal_get_filename() static cache with all enabled modules and themes.
       
   213     foreach ($lists['filepaths'] as $item) {
       
   214       drupal_get_filename($item['type'], $item['name'], $item['filepath']);
       
   215     }
       
   216   }
       
   217 
       
   218   return $lists[$type];
       
   219 }
       
   220 
       
   221 /**
       
   222  * Resets all system_list() caches.
       
   223  */
       
   224 function system_list_reset() {
       
   225   drupal_static_reset('system_list');
       
   226   drupal_static_reset('system_rebuild_module_data');
       
   227   drupal_static_reset('list_themes');
       
   228   cache_clear_all('bootstrap_modules', 'cache_bootstrap');
       
   229   cache_clear_all('system_list', 'cache_bootstrap');
       
   230 
       
   231   // Clean up the bootstrap file scan cache.
       
   232   drupal_static_reset('_drupal_file_scan_cache');
       
   233   cache_clear_all('_drupal_file_scan_cache', 'cache_bootstrap');
       
   234 }
       
   235 
       
   236 /**
       
   237  * Determines which modules require and are required by each module.
       
   238  *
       
   239  * @param $files
       
   240  *   The array of filesystem objects used to rebuild the cache.
       
   241  *
       
   242  * @return
       
   243  *   The same array with the new keys for each module:
       
   244  *   - requires: An array with the keys being the modules that this module
       
   245  *     requires.
       
   246  *   - required_by: An array with the keys being the modules that will not work
       
   247  *     without this module.
       
   248  */
       
   249 function _module_build_dependencies($files) {
       
   250   require_once DRUPAL_ROOT . '/includes/graph.inc';
       
   251   foreach ($files as $filename => $file) {
       
   252     $graph[$file->name]['edges'] = array();
       
   253     if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
       
   254       foreach ($file->info['dependencies'] as $dependency) {
       
   255         $dependency_data = drupal_parse_dependency($dependency);
       
   256         $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data;
       
   257       }
       
   258     }
       
   259   }
       
   260   drupal_depth_first_search($graph);
       
   261   foreach ($graph as $module => $data) {
       
   262     $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array();
       
   263     $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array();
       
   264     $files[$module]->sort = $data['weight'];
       
   265   }
       
   266   return $files;
       
   267 }
       
   268 
       
   269 /**
       
   270  * Determines whether a given module exists.
       
   271  *
       
   272  * @param string $module
       
   273  *   The name of the module (without the .module extension).
       
   274  *
       
   275  * @return bool
       
   276  *   TRUE if the module is both installed and enabled, FALSE otherwise.
       
   277  */
       
   278 function module_exists($module) {
       
   279   $list = module_list();
       
   280   return isset($list[$module]);
       
   281 }
       
   282 
       
   283 /**
       
   284  * Loads a module's installation hooks.
       
   285  *
       
   286  * @param $module
       
   287  *   The name of the module (without the .module extension).
       
   288  *
       
   289  * @return
       
   290  *   The name of the module's install file, if successful; FALSE otherwise.
       
   291  */
       
   292 function module_load_install($module) {
       
   293   // Make sure the installation API is available
       
   294   include_once DRUPAL_ROOT . '/includes/install.inc';
       
   295 
       
   296   return module_load_include('install', $module);
       
   297 }
       
   298 
       
   299 /**
       
   300  * Loads a module include file.
       
   301  *
       
   302  * Examples:
       
   303  * @code
       
   304  *   // Load node.admin.inc from the node module.
       
   305  *   module_load_include('inc', 'node', 'node.admin');
       
   306  *   // Load content_types.inc from the node module.
       
   307  *   module_load_include('inc', 'node', 'content_types');
       
   308  * @endcode
       
   309  *
       
   310  * Do not use this function to load an install file, use module_load_install()
       
   311  * instead. Do not use this function in a global context since it requires
       
   312  * Drupal to be fully bootstrapped, use require_once DRUPAL_ROOT . '/path/file'
       
   313  * instead.
       
   314  *
       
   315  * @param $type
       
   316  *   The include file's type (file extension).
       
   317  * @param $module
       
   318  *   The module to which the include file belongs.
       
   319  * @param $name
       
   320  *   (optional) The base file name (without the $type extension). If omitted,
       
   321  *   $module is used; i.e., resulting in "$module.$type" by default.
       
   322  *
       
   323  * @return
       
   324  *   The name of the included file, if successful; FALSE otherwise.
       
   325  */
       
   326 function module_load_include($type, $module, $name = NULL) {
       
   327   static $files = array();
       
   328 
       
   329   if (!isset($name)) {
       
   330     $name = $module;
       
   331   }
       
   332 
       
   333   $key = $type . ':' . $module . ':' . $name;
       
   334   if (isset($files[$key])) {
       
   335     return $files[$key];
       
   336   }
       
   337 
       
   338   if (function_exists('drupal_get_path')) {
       
   339     $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
       
   340     if (is_file($file)) {
       
   341       require_once $file;
       
   342       $files[$key] = $file;
       
   343       return $file;
       
   344     }
       
   345     else {
       
   346       $files[$key] = FALSE;
       
   347     }
       
   348   }
       
   349   return FALSE;
       
   350 }
       
   351 
       
   352 /**
       
   353  * Loads an include file for each module enabled in the {system} table.
       
   354  */
       
   355 function module_load_all_includes($type, $name = NULL) {
       
   356   $modules = module_list();
       
   357   foreach ($modules as $module) {
       
   358     module_load_include($type, $module, $name);
       
   359   }
       
   360 }
       
   361 
       
   362 /**
       
   363  * Enables or installs a given list of modules.
       
   364  *
       
   365  * Definitions:
       
   366  * - "Enabling" is the process of activating a module for use by Drupal.
       
   367  * - "Disabling" is the process of deactivating a module.
       
   368  * - "Installing" is the process of enabling it for the first time or after it
       
   369  *   has been uninstalled.
       
   370  * - "Uninstalling" is the process of removing all traces of a module.
       
   371  *
       
   372  * Order of events:
       
   373  * - Gather and add module dependencies to $module_list (if applicable).
       
   374  * - For each module that is being enabled:
       
   375  *   - Install module schema and update system registries and caches.
       
   376  *   - If the module is being enabled for the first time or had been
       
   377  *     uninstalled, invoke hook_install() and add it to the list of installed
       
   378  *     modules.
       
   379  *   - Invoke hook_enable().
       
   380  * - Invoke hook_modules_installed().
       
   381  * - Invoke hook_modules_enabled().
       
   382  *
       
   383  * @param string[] $module_list
       
   384  *   An array of module names.
       
   385  * @param bool $enable_dependencies
       
   386  *   If TRUE, dependencies will automatically be added and enabled in the
       
   387  *   correct order. This incurs a significant performance cost, so use FALSE
       
   388  *   if you know $module_list is already complete and in the correct order.
       
   389  *
       
   390  * @return bool
       
   391  *   FALSE if one or more dependencies are missing, TRUE otherwise.
       
   392  *
       
   393  * @see hook_install()
       
   394  * @see hook_enable()
       
   395  * @see hook_modules_installed()
       
   396  * @see hook_modules_enabled()
       
   397  * @see module_disable()
       
   398  * @see drupal_uninstall_modules()
       
   399  */
       
   400 function module_enable($module_list, $enable_dependencies = TRUE) {
       
   401   if ($enable_dependencies) {
       
   402     // Get all module data so we can find dependencies and sort.
       
   403     $module_data = system_rebuild_module_data();
       
   404     // Create an associative array with weights as values.
       
   405     $module_list = array_flip(array_values($module_list));
       
   406 
       
   407     while (list($module) = each($module_list)) {
       
   408       if (!isset($module_data[$module])) {
       
   409         // This module is not found in the filesystem, abort.
       
   410         return FALSE;
       
   411       }
       
   412       if ($module_data[$module]->status) {
       
   413         // Skip already enabled modules.
       
   414         unset($module_list[$module]);
       
   415         continue;
       
   416       }
       
   417       $module_list[$module] = $module_data[$module]->sort;
       
   418 
       
   419       // Add dependencies to the list, with a placeholder weight.
       
   420       // The new modules will be processed as the while loop continues.
       
   421       foreach (array_keys($module_data[$module]->requires) as $dependency) {
       
   422         if (!isset($module_list[$dependency])) {
       
   423           $module_list[$dependency] = 0;
       
   424         }
       
   425       }
       
   426     }
       
   427 
       
   428     if (!$module_list) {
       
   429       // Nothing to do. All modules already enabled.
       
   430       return TRUE;
       
   431     }
       
   432 
       
   433     // Sort the module list by pre-calculated weights.
       
   434     arsort($module_list);
       
   435     $module_list = array_keys($module_list);
       
   436   }
       
   437 
       
   438   // Required for module installation checks.
       
   439   include_once DRUPAL_ROOT . '/includes/install.inc';
       
   440 
       
   441   $modules_installed = array();
       
   442   $modules_enabled = array();
       
   443   foreach ($module_list as $module) {
       
   444     // Only process modules that are not already enabled.
       
   445     $existing = db_query("SELECT status FROM {system} WHERE type = :type AND name = :name", array(
       
   446       ':type' => 'module',
       
   447       ':name' => $module))
       
   448       ->fetchObject();
       
   449     if ($existing->status == 0) {
       
   450       // Load the module's code.
       
   451       drupal_load('module', $module);
       
   452       module_load_install($module);
       
   453 
       
   454       // Update the database and module list to reflect the new module. This
       
   455       // needs to be done first so that the module's hook implementations,
       
   456       // hook_schema() in particular, can be called while it is being
       
   457       // installed.
       
   458       db_update('system')
       
   459         ->fields(array('status' => 1))
       
   460         ->condition('type', 'module')
       
   461         ->condition('name', $module)
       
   462         ->execute();
       
   463       // Refresh the module list to include it.
       
   464       system_list_reset();
       
   465       module_list(TRUE);
       
   466       module_implements('', FALSE, TRUE);
       
   467       _system_update_bootstrap_status();
       
   468       // Update the registry to include it.
       
   469       registry_update();
       
   470       // Refresh the schema to include it.
       
   471       drupal_get_schema(NULL, TRUE);
       
   472       // Update the theme registry to include it.
       
   473       drupal_theme_rebuild();
       
   474       // Clear entity cache.
       
   475       entity_info_cache_clear();
       
   476 
       
   477       // Now install the module if necessary.
       
   478       if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
       
   479         drupal_install_schema($module);
       
   480 
       
   481         // Set the schema version to the number of the last update provided
       
   482         // by the module.
       
   483         $versions = drupal_get_schema_versions($module);
       
   484         $version = $versions ? max($versions) : SCHEMA_INSTALLED;
       
   485 
       
   486         // If the module has no current updates, but has some that were
       
   487         // previously removed, set the version to the value of
       
   488         // hook_update_last_removed().
       
   489         if ($last_removed = module_invoke($module, 'update_last_removed')) {
       
   490           $version = max($version, $last_removed);
       
   491         }
       
   492         drupal_set_installed_schema_version($module, $version);
       
   493         // Allow the module to perform install tasks.
       
   494         module_invoke($module, 'install');
       
   495         // Record the fact that it was installed.
       
   496         $modules_installed[] = $module;
       
   497         watchdog('system', '%module module installed.', array('%module' => $module), WATCHDOG_INFO);
       
   498       }
       
   499 
       
   500       // Enable the module.
       
   501       module_invoke($module, 'enable');
       
   502 
       
   503       // Record the fact that it was enabled.
       
   504       $modules_enabled[] = $module;
       
   505       watchdog('system', '%module module enabled.', array('%module' => $module), WATCHDOG_INFO);
       
   506     }
       
   507   }
       
   508 
       
   509   // If any modules were newly installed, invoke hook_modules_installed().
       
   510   if (!empty($modules_installed)) {
       
   511     module_invoke_all('modules_installed', $modules_installed);
       
   512   }
       
   513 
       
   514   // If any modules were newly enabled, invoke hook_modules_enabled().
       
   515   if (!empty($modules_enabled)) {
       
   516     module_invoke_all('modules_enabled', $modules_enabled);
       
   517   }
       
   518 
       
   519   return TRUE;
       
   520 }
       
   521 
       
   522 /**
       
   523  * Disables a given set of modules.
       
   524  *
       
   525  * @param string[] $module_list
       
   526  *   An array of module names.
       
   527  * @param bool $disable_dependents
       
   528  *   If TRUE, dependent modules will automatically be added and disabled in the
       
   529  *   correct order. This incurs a significant performance cost, so use FALSE
       
   530  *   if you know $module_list is already complete and in the correct order.
       
   531  *
       
   532  * @see drupal_uninstall_modules()
       
   533  * @see module_enable()
       
   534  */
       
   535 function module_disable($module_list, $disable_dependents = TRUE) {
       
   536   if ($disable_dependents) {
       
   537     // Get all module data so we can find dependents and sort.
       
   538     $module_data = system_rebuild_module_data();
       
   539     // Create an associative array with weights as values.
       
   540     $module_list = array_flip(array_values($module_list));
       
   541 
       
   542     $profile = drupal_get_profile();
       
   543     while (list($module) = each($module_list)) {
       
   544       if (!isset($module_data[$module]) || !$module_data[$module]->status) {
       
   545         // This module doesn't exist or is already disabled, skip it.
       
   546         unset($module_list[$module]);
       
   547         continue;
       
   548       }
       
   549       $module_list[$module] = $module_data[$module]->sort;
       
   550 
       
   551       // Add dependent modules to the list, with a placeholder weight.
       
   552       // The new modules will be processed as the while loop continues.
       
   553       foreach ($module_data[$module]->required_by as $dependent => $dependent_data) {
       
   554         if (!isset($module_list[$dependent]) && $dependent != $profile) {
       
   555           $module_list[$dependent] = 0;
       
   556         }
       
   557       }
       
   558     }
       
   559 
       
   560     // Sort the module list by pre-calculated weights.
       
   561     asort($module_list);
       
   562     $module_list = array_keys($module_list);
       
   563   }
       
   564 
       
   565   $invoke_modules = array();
       
   566 
       
   567   foreach ($module_list as $module) {
       
   568     if (module_exists($module)) {
       
   569       // Check if node_access table needs rebuilding.
       
   570       if (!node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
       
   571         node_access_needs_rebuild(TRUE);
       
   572       }
       
   573 
       
   574       module_load_install($module);
       
   575       module_invoke($module, 'disable');
       
   576       db_update('system')
       
   577         ->fields(array('status' => 0))
       
   578         ->condition('type', 'module')
       
   579         ->condition('name', $module)
       
   580         ->execute();
       
   581       $invoke_modules[] = $module;
       
   582       watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO);
       
   583     }
       
   584   }
       
   585 
       
   586   if (!empty($invoke_modules)) {
       
   587     // Refresh the module list to exclude the disabled modules.
       
   588     system_list_reset();
       
   589     module_list(TRUE);
       
   590     module_implements('', FALSE, TRUE);
       
   591     entity_info_cache_clear();
       
   592     // Invoke hook_modules_disabled before disabling modules,
       
   593     // so we can still call module hooks to get information.
       
   594     module_invoke_all('modules_disabled', $invoke_modules);
       
   595     // Update the registry to remove the newly-disabled module.
       
   596     registry_update();
       
   597     _system_update_bootstrap_status();
       
   598     // Update the theme registry to remove the newly-disabled module.
       
   599     drupal_theme_rebuild();
       
   600   }
       
   601 
       
   602   // If there remains no more node_access module, rebuilding will be
       
   603   // straightforward, we can do it right now.
       
   604   if (node_access_needs_rebuild() && count(module_implements('node_grants')) == 0) {
       
   605     node_access_rebuild();
       
   606   }
       
   607 }
       
   608 
       
   609 /**
       
   610  * @defgroup hooks Hooks
       
   611  * @{
       
   612  * Allow modules to interact with the Drupal core.
       
   613  *
       
   614  * Drupal's module system is based on the concept of "hooks". A hook is a PHP
       
   615  * function that is named foo_bar(), where "foo" is the name of the module
       
   616  * (whose filename is thus foo.module) and "bar" is the name of the hook. Each
       
   617  * hook has a defined set of parameters and a specified result type.
       
   618  *
       
   619  * To extend Drupal, a module need simply implement a hook. When Drupal wishes
       
   620  * to allow intervention from modules, it determines which modules implement a
       
   621  * hook and calls that hook in all enabled modules that implement it.
       
   622  *
       
   623  * The available hooks to implement are explained here in the Hooks section of
       
   624  * the developer documentation. The string "hook" is used as a placeholder for
       
   625  * the module name in the hook definitions. For example, if the module file is
       
   626  * called example.module, then hook_help() as implemented by that module would
       
   627  * be defined as example_help().
       
   628  *
       
   629  * The example functions included are not part of the Drupal core, they are
       
   630  * just models that you can modify. Only the hooks implemented within modules
       
   631  * are executed when running Drupal.
       
   632  *
       
   633  * @see themeable
       
   634  * @see callbacks
       
   635  */
       
   636 
       
   637  /**
       
   638   * @defgroup callbacks Callbacks
       
   639   * @{
       
   640   * Callback function signatures.
       
   641   *
       
   642   * Drupal's API sometimes uses callback functions to allow you to define how
       
   643   * some type of processing happens. A callback is a function with a defined
       
   644   * signature, which you define in a module. Then you pass the function name as
       
   645   * a parameter to a Drupal API function or return it as part of a hook
       
   646   * implementation return value, and your function is called at an appropriate
       
   647   * time. For instance, when setting up batch processing you might need to
       
   648   * provide a callback function for each processing step and/or a callback for
       
   649   * when processing is finished; you would do that by defining these functions
       
   650   * and passing their names into the batch setup function.
       
   651   *
       
   652   * Callback function signatures, like hook definitions, are described by
       
   653   * creating and documenting dummy functions in a *.api.php file; normally, the
       
   654   * dummy callback function's name should start with "callback_", and you should
       
   655   * document the parameters and return value and provide a sample function body.
       
   656   * Then your API documentation can refer to this callback function in its
       
   657   * documentation. A user of your API can usually name their callback function
       
   658   * anything they want, although a standard name would be to replace "callback_"
       
   659   * with the module name.
       
   660   *
       
   661   * @see hooks
       
   662   * @see themeable
       
   663   *
       
   664   * @}
       
   665   */
       
   666 
       
   667 /**
       
   668  * Determines whether a module implements a hook.
       
   669  *
       
   670  * @param $module
       
   671  *   The name of the module (without the .module extension).
       
   672  * @param $hook
       
   673  *   The name of the hook (e.g. "help" or "menu").
       
   674  *
       
   675  * @return
       
   676  *   TRUE if the module is both installed and enabled, and the hook is
       
   677  *   implemented in that module.
       
   678  */
       
   679 function module_hook($module, $hook) {
       
   680   $function = $module . '_' . $hook;
       
   681   if (function_exists($function)) {
       
   682     return TRUE;
       
   683   }
       
   684   // If the hook implementation does not exist, check whether it may live in an
       
   685   // optional include file registered via hook_hook_info().
       
   686   $hook_info = module_hook_info();
       
   687   if (isset($hook_info[$hook]['group'])) {
       
   688     module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
       
   689     if (function_exists($function)) {
       
   690       return TRUE;
       
   691     }
       
   692   }
       
   693   return FALSE;
       
   694 }
       
   695 
       
   696 /**
       
   697  * Determines which modules are implementing a hook.
       
   698  *
       
   699  * Lazy-loaded include files specified with "group" via hook_hook_info() or
       
   700  * hook_module_implements_alter() will be automatically included by this
       
   701  * function when necessary.
       
   702  *
       
   703  * @param string $hook
       
   704  *   The name of the hook (e.g. "help" or "menu").
       
   705  * @param bool $sort
       
   706  *   By default, modules are ordered by weight and filename, settings this option
       
   707  *   to TRUE, module list will be ordered by module name.
       
   708  * @param bool $reset
       
   709  *   For internal use only: Whether to force the stored list of hook
       
   710  *   implementations to be regenerated (such as after enabling a new module,
       
   711  *   before processing hook_enable).
       
   712  *
       
   713  * @return
       
   714  *   An array with the names of the modules which are implementing this hook.
       
   715  *
       
   716  * @see module_implements_write_cache()
       
   717  */
       
   718 function module_implements($hook, $sort = FALSE, $reset = FALSE) {
       
   719   // Use the advanced drupal_static() pattern, since this is called very often.
       
   720   static $drupal_static_fast;
       
   721   if (!isset($drupal_static_fast)) {
       
   722     $drupal_static_fast['implementations'] = &drupal_static(__FUNCTION__);
       
   723     $drupal_static_fast['verified'] = &drupal_static(__FUNCTION__ . ':verified');
       
   724   }
       
   725   $implementations = &$drupal_static_fast['implementations'];
       
   726   $verified = &$drupal_static_fast['verified'];
       
   727 
       
   728   // We maintain a persistent cache of hook implementations in addition to the
       
   729   // static cache to avoid looping through every module and every hook on each
       
   730   // request. Benchmarks show that the benefit of this caching outweighs the
       
   731   // additional database hit even when using the default database caching
       
   732   // backend and only a small number of modules are enabled. The cost of the
       
   733   // cache_get() is more or less constant and reduced further when non-database
       
   734   // caching backends are used, so there will be more significant gains when a
       
   735   // large number of modules are installed or hooks invoked, since this can
       
   736   // quickly lead to module_hook() being called several thousand times
       
   737   // per request.
       
   738   if ($reset) {
       
   739     $implementations = array();
       
   740     $verified = array();
       
   741     cache_set('module_implements', array(), 'cache_bootstrap');
       
   742     drupal_static_reset('module_hook_info');
       
   743     drupal_static_reset('drupal_alter');
       
   744     cache_clear_all('hook_info', 'cache_bootstrap');
       
   745     cache_clear_all('system_cache_tables', 'cache');
       
   746     return;
       
   747   }
       
   748 
       
   749   // Fetch implementations from cache.
       
   750   // This happens on the first call to module_implements(*, *, FALSE) during a
       
   751   // request, but also when $implementations have been reset, e.g. after
       
   752   // module_enable().
       
   753   if (empty($implementations)) {
       
   754     $implementations = cache_get('module_implements', 'cache_bootstrap');
       
   755     if ($implementations === FALSE) {
       
   756       $implementations = array();
       
   757     }
       
   758     else {
       
   759       $implementations = $implementations->data;
       
   760     }
       
   761     // Forget all previously "verified" hooks, in case that $implementations
       
   762     // were cleared via drupal_static_reset('module_implements') instead of
       
   763     // module_implements(*, *, TRUE).
       
   764     $verified = array();
       
   765   }
       
   766 
       
   767   if (!isset($implementations[$hook])) {
       
   768     // The hook is not cached, so ensure that whether or not it has
       
   769     // implementations, that the cache is updated at the end of the request.
       
   770     $implementations['#write_cache'] = TRUE;
       
   771     // Discover implementations for this hook.
       
   772     $hook_info = module_hook_info();
       
   773     $implementations[$hook] = array();
       
   774     $list = module_list(FALSE, FALSE, $sort);
       
   775     foreach ($list as $module) {
       
   776       $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
       
   777       // Since module_hook() may needlessly try to load the include file again,
       
   778       // function_exists() is used directly here.
       
   779       if (function_exists($module . '_' . $hook)) {
       
   780         $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE;
       
   781       }
       
   782     }
       
   783     // Allow modules to change the weight of specific implementations, but avoid
       
   784     // an infinite loop.
       
   785     if ($hook != 'module_implements_alter') {
       
   786       // Remember the implementations before hook_module_implements_alter().
       
   787       $implementations_before = $implementations[$hook];
       
   788       drupal_alter('module_implements', $implementations[$hook], $hook);
       
   789       // Verify implementations that were added or modified.
       
   790       foreach (array_diff_assoc($implementations[$hook], $implementations_before) as $module => $group) {
       
   791         // If drupal_alter('module_implements') changed or added a $group, the
       
   792         // respective file needs to be included.
       
   793         if ($group) {
       
   794           module_load_include('inc', $module, "$module.$group");
       
   795         }
       
   796         // If a new implementation was added, verify that the function exists.
       
   797         if (!function_exists($module . '_' . $hook)) {
       
   798           unset($implementations[$hook][$module]);
       
   799         }
       
   800       }
       
   801     }
       
   802     // Implementations for this hook are now "verified".
       
   803     $verified[$hook] = TRUE;
       
   804   }
       
   805   elseif (!isset($verified[$hook])) {
       
   806     // Implementations for this hook were in the cache, but they are not
       
   807     // "verified" yet.
       
   808     foreach ($implementations[$hook] as $module => $group) {
       
   809       // If this hook implementation is stored in a lazy-loaded file, so include
       
   810       // that file first.
       
   811       if ($group) {
       
   812         module_load_include('inc', $module, "$module.$group");
       
   813       }
       
   814       // It is possible that a module removed a hook implementation without the
       
   815       // implementations cache being rebuilt yet, so we check whether the
       
   816       // function exists on each request to avoid undefined function errors.
       
   817       // Since module_hook() may needlessly try to load the include file again,
       
   818       // function_exists() is used directly here.
       
   819       if (!function_exists($module . '_' . $hook)) {
       
   820         // Clear out the stale implementation from the cache and force a cache
       
   821         // refresh to forget about no longer existing hook implementations.
       
   822         unset($implementations[$hook][$module]);
       
   823         $implementations['#write_cache'] = TRUE;
       
   824       }
       
   825     }
       
   826     $verified[$hook] = TRUE;
       
   827   }
       
   828 
       
   829   return array_keys($implementations[$hook]);
       
   830 }
       
   831 
       
   832 /**
       
   833  * Retrieves a list of hooks that are declared through hook_hook_info().
       
   834  *
       
   835  * @return
       
   836  *   An associative array whose keys are hook names and whose values are an
       
   837  *   associative array containing a group name. The structure of the array
       
   838  *   is the same as the return value of hook_hook_info().
       
   839  *
       
   840  * @see hook_hook_info()
       
   841  */
       
   842 function module_hook_info() {
       
   843   // This function is indirectly invoked from bootstrap_invoke_all(), in which
       
   844   // case common.inc, subsystems, and modules are not loaded yet, so it does not
       
   845   // make sense to support hook groups resp. lazy-loaded include files prior to
       
   846   // full bootstrap.
       
   847   if (drupal_bootstrap(NULL, FALSE) != DRUPAL_BOOTSTRAP_FULL) {
       
   848     return array();
       
   849   }
       
   850   $hook_info = &drupal_static(__FUNCTION__);
       
   851 
       
   852   if (!isset($hook_info)) {
       
   853     $hook_info = array();
       
   854     $cache = cache_get('hook_info', 'cache_bootstrap');
       
   855     if ($cache === FALSE) {
       
   856       // Rebuild the cache and save it.
       
   857       // We can't use module_invoke_all() here or it would cause an infinite
       
   858       // loop.
       
   859       foreach (module_list() as $module) {
       
   860         $function = $module . '_hook_info';
       
   861         if (function_exists($function)) {
       
   862           $result = $function();
       
   863           if (isset($result) && is_array($result)) {
       
   864             $hook_info = array_merge_recursive($hook_info, $result);
       
   865           }
       
   866         }
       
   867       }
       
   868       // We can't use drupal_alter() for the same reason as above.
       
   869       foreach (module_list() as $module) {
       
   870         $function = $module . '_hook_info_alter';
       
   871         if (function_exists($function)) {
       
   872           $function($hook_info);
       
   873         }
       
   874       }
       
   875       cache_set('hook_info', $hook_info, 'cache_bootstrap');
       
   876     }
       
   877     else {
       
   878       $hook_info = $cache->data;
       
   879     }
       
   880   }
       
   881 
       
   882   return $hook_info;
       
   883 }
       
   884 
       
   885 /**
       
   886  * Writes the hook implementation cache.
       
   887  *
       
   888  * @see module_implements()
       
   889  */
       
   890 function module_implements_write_cache() {
       
   891   // The list of implementations includes vital modules only before full
       
   892   // bootstrap, so do not write cache if we are not fully bootstrapped yet.
       
   893   if (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) {
       
   894     return;
       
   895   }
       
   896   $implementations = &drupal_static('module_implements');
       
   897   if (isset($implementations['#write_cache'])) {
       
   898     unset($implementations['#write_cache']);
       
   899     cache_set('module_implements', $implementations, 'cache_bootstrap');
       
   900   }
       
   901 }
       
   902 
       
   903 /**
       
   904  * Invokes a hook in a particular module.
       
   905  *
       
   906  * All arguments are passed by value. Use drupal_alter() if you need to pass
       
   907  * arguments by reference.
       
   908  *
       
   909  * @param $module
       
   910  *   The name of the module (without the .module extension).
       
   911  * @param $hook
       
   912  *   The name of the hook to invoke.
       
   913  * @param ...
       
   914  *   Arguments to pass to the hook implementation.
       
   915  *
       
   916  * @return
       
   917  *   The return value of the hook implementation.
       
   918  *
       
   919  * @see drupal_alter()
       
   920  */
       
   921 function module_invoke($module, $hook) {
       
   922   $args = func_get_args();
       
   923   // Remove $module and $hook from the arguments.
       
   924   unset($args[0], $args[1]);
       
   925   if (module_hook($module, $hook)) {
       
   926     return call_user_func_array($module . '_' . $hook, $args);
       
   927   }
       
   928 }
       
   929 
       
   930 /**
       
   931  * Invokes a hook in all enabled modules that implement it.
       
   932  *
       
   933  * All arguments are passed by value. Use drupal_alter() if you need to pass
       
   934  * arguments by reference.
       
   935  *
       
   936  * @param $hook
       
   937  *   The name of the hook to invoke.
       
   938  * @param ...
       
   939  *   Arguments to pass to the hook.
       
   940  *
       
   941  * @return
       
   942  *   An array of return values of the hook implementations. If modules return
       
   943  *   arrays from their implementations, those are merged into one array
       
   944  *   recursively. Note: integer keys in arrays will be lost, as the merge is
       
   945  *   done using array_merge_recursive().
       
   946  *
       
   947  * @see drupal_alter()
       
   948  */
       
   949 function module_invoke_all($hook) {
       
   950   $args = func_get_args();
       
   951   // Remove $hook from the arguments.
       
   952   unset($args[0]);
       
   953   $return = array();
       
   954   foreach (module_implements($hook) as $module) {
       
   955     $function = $module . '_' . $hook;
       
   956     if (function_exists($function)) {
       
   957       $result = call_user_func_array($function, $args);
       
   958       if (isset($result) && is_array($result)) {
       
   959         $return = array_merge_recursive($return, $result);
       
   960       }
       
   961       elseif (isset($result)) {
       
   962         $return[] = $result;
       
   963       }
       
   964     }
       
   965   }
       
   966 
       
   967   return $return;
       
   968 }
       
   969 
       
   970 /**
       
   971  * @} End of "defgroup hooks".
       
   972  */
       
   973 
       
   974 /**
       
   975  * Returns an array of modules required by core.
       
   976  */
       
   977 function drupal_required_modules() {
       
   978   $files = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'modules', 'name', 0);
       
   979   $required = array();
       
   980 
       
   981   // An installation profile is required and one must always be loaded.
       
   982   $required[] = drupal_get_profile();
       
   983 
       
   984   foreach ($files as $name => $file) {
       
   985     $info = drupal_parse_info_file($file->uri);
       
   986     if (!empty($info) && !empty($info['required']) && $info['required']) {
       
   987       $required[] = $name;
       
   988     }
       
   989   }
       
   990 
       
   991   return $required;
       
   992 }
       
   993 
       
   994 /**
       
   995  * Passes alterable variables to specific hook_TYPE_alter() implementations.
       
   996  *
       
   997  * This dispatch function hands off the passed-in variables to type-specific
       
   998  * hook_TYPE_alter() implementations in modules. It ensures a consistent
       
   999  * interface for all altering operations.
       
  1000  *
       
  1001  * A maximum of 2 alterable arguments is supported (a third is supported for
       
  1002  * legacy reasons, but should not be used in new code). In case more arguments
       
  1003  * need to be passed and alterable, modules provide additional variables
       
  1004  * assigned by reference in the last $context argument:
       
  1005  * @code
       
  1006  *   $context = array(
       
  1007  *     'alterable' => &$alterable,
       
  1008  *     'unalterable' => $unalterable,
       
  1009  *     'foo' => 'bar',
       
  1010  *   );
       
  1011  *   drupal_alter('mymodule_data', $alterable1, $alterable2, $context);
       
  1012  * @endcode
       
  1013  *
       
  1014  * Note that objects are always passed by reference in PHP5. If it is absolutely
       
  1015  * required that no implementation alters a passed object in $context, then an
       
  1016  * object needs to be cloned:
       
  1017  * @code
       
  1018  *   $context = array(
       
  1019  *     'unalterable_object' => clone $object,
       
  1020  *   );
       
  1021  *   drupal_alter('mymodule_data', $data, $context);
       
  1022  * @endcode
       
  1023  *
       
  1024  * @param $type
       
  1025  *   A string describing the type of the alterable $data. 'form', 'links',
       
  1026  *   'node_content', and so on are several examples. Alternatively can be an
       
  1027  *   array, in which case hook_TYPE_alter() is invoked for each value in the
       
  1028  *   array, ordered first by module, and then for each module, in the order of
       
  1029  *   values in $type. For example, when Form API is using drupal_alter() to
       
  1030  *   execute both hook_form_alter() and hook_form_FORM_ID_alter()
       
  1031  *   implementations, it passes array('form', 'form_' . $form_id) for $type.
       
  1032  * @param $data
       
  1033  *   The variable that will be passed to hook_TYPE_alter() implementations to be
       
  1034  *   altered. The type of this variable depends on the value of the $type
       
  1035  *   argument. For example, when altering a 'form', $data will be a structured
       
  1036  *   array. When altering a 'profile', $data will be an object.
       
  1037  * @param $context1
       
  1038  *   (optional) An additional variable that is passed by reference.
       
  1039  * @param $context2
       
  1040  *   (optional) An additional variable that is passed by reference. If more
       
  1041  *   context needs to be provided to implementations, then this should be an
       
  1042  *   associative array as described above.
       
  1043  * @param $context3
       
  1044  *   (optional) An additional variable that is passed by reference. This
       
  1045  *   parameter is deprecated and will not exist in Drupal 8; consequently, it
       
  1046  *   should not be used for new Drupal 7 code either. It is here only for
       
  1047  *   backwards compatibility with older code that passed additional arguments
       
  1048  *   to drupal_alter().
       
  1049  */
       
  1050 function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL, &$context3 = NULL) {
       
  1051   // Use the advanced drupal_static() pattern, since this is called very often.
       
  1052   static $drupal_static_fast;
       
  1053   if (!isset($drupal_static_fast)) {
       
  1054     $drupal_static_fast['functions'] = &drupal_static(__FUNCTION__);
       
  1055   }
       
  1056   $functions = &$drupal_static_fast['functions'];
       
  1057 
       
  1058   // Most of the time, $type is passed as a string, so for performance,
       
  1059   // normalize it to that. When passed as an array, usually the first item in
       
  1060   // the array is a generic type, and additional items in the array are more
       
  1061   // specific variants of it, as in the case of array('form', 'form_FORM_ID').
       
  1062   if (is_array($type)) {
       
  1063     $cid = implode(',', $type);
       
  1064     $extra_types = $type;
       
  1065     $type = array_shift($extra_types);
       
  1066     // Allow if statements in this function to use the faster isset() rather
       
  1067     // than !empty() both when $type is passed as a string, or as an array with
       
  1068     // one item.
       
  1069     if (empty($extra_types)) {
       
  1070       unset($extra_types);
       
  1071     }
       
  1072   }
       
  1073   else {
       
  1074     $cid = $type;
       
  1075   }
       
  1076 
       
  1077   // Some alter hooks are invoked many times per page request, so statically
       
  1078   // cache the list of functions to call, and on subsequent calls, iterate
       
  1079   // through them quickly.
       
  1080   if (!isset($functions[$cid])) {
       
  1081     $functions[$cid] = array();
       
  1082     $hook = $type . '_alter';
       
  1083     $modules = module_implements($hook);
       
  1084     if (!isset($extra_types)) {
       
  1085       // For the more common case of a single hook, we do not need to call
       
  1086       // function_exists(), since module_implements() returns only modules with
       
  1087       // implementations.
       
  1088       foreach ($modules as $module) {
       
  1089         $functions[$cid][] = $module . '_' . $hook;
       
  1090       }
       
  1091     }
       
  1092     else {
       
  1093       // For multiple hooks, we need $modules to contain every module that
       
  1094       // implements at least one of them.
       
  1095       $extra_modules = array();
       
  1096       foreach ($extra_types as $extra_type) {
       
  1097         $extra_modules = array_merge($extra_modules, module_implements($extra_type . '_alter'));
       
  1098       }
       
  1099       // If any modules implement one of the extra hooks that do not implement
       
  1100       // the primary hook, we need to add them to the $modules array in their
       
  1101       // appropriate order. module_implements() can only return ordered
       
  1102       // implementations of a single hook. To get the ordered implementations
       
  1103       // of multiple hooks, we mimic the module_implements() logic of first
       
  1104       // ordering by module_list(), and then calling
       
  1105       // drupal_alter('module_implements').
       
  1106       if (array_diff($extra_modules, $modules)) {
       
  1107         // Merge the arrays and order by module_list().
       
  1108         $modules = array_intersect(module_list(), array_merge($modules, $extra_modules));
       
  1109         // Since module_implements() already took care of loading the necessary
       
  1110         // include files, we can safely pass FALSE for the array values.
       
  1111         $implementations = array_fill_keys($modules, FALSE);
       
  1112         // Let modules adjust the order solely based on the primary hook. This
       
  1113         // ensures the same module order regardless of whether this if block
       
  1114         // runs. Calling drupal_alter() recursively in this way does not result
       
  1115         // in an infinite loop, because this call is for a single $type, so we
       
  1116         // won't end up in this code block again.
       
  1117         drupal_alter('module_implements', $implementations, $hook);
       
  1118         $modules = array_keys($implementations);
       
  1119       }
       
  1120       foreach ($modules as $module) {
       
  1121         // Since $modules is a merged array, for any given module, we do not
       
  1122         // know whether it has any particular implementation, so we need a
       
  1123         // function_exists().
       
  1124         $function = $module . '_' . $hook;
       
  1125         if (function_exists($function)) {
       
  1126           $functions[$cid][] = $function;
       
  1127         }
       
  1128         foreach ($extra_types as $extra_type) {
       
  1129           $function = $module . '_' . $extra_type . '_alter';
       
  1130           if (function_exists($function)) {
       
  1131             $functions[$cid][] = $function;
       
  1132           }
       
  1133         }
       
  1134       }
       
  1135     }
       
  1136     // Allow the theme to alter variables after the theme system has been
       
  1137     // initialized.
       
  1138     global $theme, $base_theme_info;
       
  1139     if (isset($theme)) {
       
  1140       $theme_keys = array();
       
  1141       foreach ($base_theme_info as $base) {
       
  1142         $theme_keys[] = $base->name;
       
  1143       }
       
  1144       $theme_keys[] = $theme;
       
  1145       foreach ($theme_keys as $theme_key) {
       
  1146         $function = $theme_key . '_' . $hook;
       
  1147         if (function_exists($function)) {
       
  1148           $functions[$cid][] = $function;
       
  1149         }
       
  1150         if (isset($extra_types)) {
       
  1151           foreach ($extra_types as $extra_type) {
       
  1152             $function = $theme_key . '_' . $extra_type . '_alter';
       
  1153             if (function_exists($function)) {
       
  1154               $functions[$cid][] = $function;
       
  1155             }
       
  1156           }
       
  1157         }
       
  1158       }
       
  1159     }
       
  1160   }
       
  1161 
       
  1162   foreach ($functions[$cid] as $function) {
       
  1163     $function($data, $context1, $context2, $context3);
       
  1164   }
       
  1165 }