web/drupal/includes/module.inc
branchdrupal
changeset 74 0ff3ba646492
equal deleted inserted replaced
73:fcf75e232c5b 74:0ff3ba646492
       
     1 <?php
       
     2 // $Id: module.inc,v 1.115.2.1 2009/02/16 10:32:10 goba Exp $
       
     3 
       
     4 /**
       
     5  * @file
       
     6  * API for loading and interacting with Drupal modules.
       
     7  */
       
     8 
       
     9 /**
       
    10  * Load all the modules that have been enabled in the system table.
       
    11  */
       
    12 function module_load_all() {
       
    13   foreach (module_list(TRUE, FALSE) as $module) {
       
    14     drupal_load('module', $module);
       
    15   }
       
    16 }
       
    17 
       
    18 /**
       
    19  * Call a function repeatedly with each module in turn as an argument.
       
    20  */
       
    21 function module_iterate($function, $argument = '') {
       
    22   foreach (module_list() as $name) {
       
    23     $function($name, $argument);
       
    24   }
       
    25 }
       
    26 
       
    27 /**
       
    28  * Collect a list of all loaded modules. During the bootstrap, return only
       
    29  * vital modules. See bootstrap.inc
       
    30  *
       
    31  * @param $refresh
       
    32  *   Whether to force the module list to be regenerated (such as after the
       
    33  *   administrator has changed the system settings).
       
    34  * @param $bootstrap
       
    35  *   Whether to return the reduced set of modules loaded in "bootstrap mode"
       
    36  *   for cached pages. See bootstrap.inc.
       
    37  * @param $sort
       
    38  *   By default, modules are ordered by weight and filename, settings this option
       
    39  *   to TRUE, module list will be ordered by module name.
       
    40  * @param $fixed_list
       
    41  *   (Optional) Override the module list with the given modules. Stays until the
       
    42  *   next call with $refresh = TRUE.
       
    43  * @return
       
    44  *   An associative array whose keys and values are the names of all loaded
       
    45  *   modules.
       
    46  */
       
    47 function module_list($refresh = FALSE, $bootstrap = TRUE, $sort = FALSE, $fixed_list = NULL) {
       
    48   static $list, $sorted_list;
       
    49 
       
    50   if ($refresh || $fixed_list) {
       
    51     $list = array();
       
    52     $sorted_list = NULL;
       
    53     if ($fixed_list) {
       
    54       foreach ($fixed_list as $name => $module) {
       
    55         drupal_get_filename('module', $name, $module['filename']);
       
    56         $list[$name] = $name;
       
    57       }
       
    58     }
       
    59     else {
       
    60       if ($bootstrap) {
       
    61         $result = db_query("SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 AND bootstrap = 1 ORDER BY weight ASC, filename ASC");
       
    62       }
       
    63       else {
       
    64         $result = db_query("SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC");
       
    65       }
       
    66       while ($module = db_fetch_object($result)) {
       
    67         if (file_exists($module->filename)) {
       
    68           // Determine the current throttle status and see if the module should be
       
    69           // loaded based on server load. We have to directly access the throttle
       
    70           // variables, since throttle.module may not be loaded yet.
       
    71           $throttle = ($module->throttle && variable_get('throttle_level', 0) > 0);
       
    72           if (!$throttle) {
       
    73             drupal_get_filename('module', $module->name, $module->filename);
       
    74             $list[$module->name] = $module->name;
       
    75           }
       
    76         }
       
    77       }
       
    78     }
       
    79   }
       
    80   if ($sort) {
       
    81     if (!isset($sorted_list)) {
       
    82       $sorted_list = $list;
       
    83       ksort($sorted_list);
       
    84     }
       
    85     return $sorted_list;
       
    86   }
       
    87   return $list;
       
    88 }
       
    89 
       
    90 /**
       
    91  * Rebuild the database cache of module files.
       
    92  *
       
    93  * @return
       
    94  *   The array of filesystem objects used to rebuild the cache.
       
    95  */
       
    96 function module_rebuild_cache() {
       
    97   // Get current list of modules
       
    98   $files = drupal_system_listing('\.module$', 'modules', 'name', 0);
       
    99 
       
   100   // Extract current files from database.
       
   101   system_get_files_database($files, 'module');
       
   102 
       
   103   ksort($files);
       
   104 
       
   105   // Set defaults for module info
       
   106   $defaults = array(
       
   107     'dependencies' => array(),
       
   108     'dependents' => array(),
       
   109     'description' => '',
       
   110     'version' => NULL,
       
   111     'php' => DRUPAL_MINIMUM_PHP,
       
   112   );
       
   113 
       
   114   foreach ($files as $filename => $file) {
       
   115     // Look for the info file.
       
   116     $file->info = drupal_parse_info_file(dirname($file->filename) .'/'. $file->name .'.info');
       
   117 
       
   118     // Skip modules that don't provide info.
       
   119     if (empty($file->info)) {
       
   120       unset($files[$filename]);
       
   121       continue;
       
   122     }
       
   123     // Merge in defaults and save.
       
   124     $files[$filename]->info = $file->info + $defaults;
       
   125 
       
   126     // Invoke hook_system_info_alter() to give installed modules a chance to
       
   127     // modify the data in the .info files if necessary.
       
   128     drupal_alter('system_info', $files[$filename]->info, $files[$filename]);
       
   129 
       
   130     // Log the critical hooks implemented by this module.
       
   131     $bootstrap = 0;
       
   132     foreach (bootstrap_hooks() as $hook) {
       
   133       if (module_hook($file->name, $hook)) {
       
   134         $bootstrap = 1;
       
   135         break;
       
   136       }
       
   137     }
       
   138 
       
   139     // Update the contents of the system table:
       
   140     if (isset($file->status) || (isset($file->old_filename) && $file->old_filename != $file->filename)) {
       
   141       db_query("UPDATE {system} SET info = '%s', name = '%s', filename = '%s', bootstrap = %d WHERE filename = '%s'", serialize($files[$filename]->info), $file->name, $file->filename, $bootstrap, $file->old_filename);
       
   142     }
       
   143     else {
       
   144       // This is a new module.
       
   145       $files[$filename]->status = 0;
       
   146       $files[$filename]->throttle = 0;
       
   147       db_query("INSERT INTO {system} (name, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $file->name, serialize($files[$filename]->info), 'module', $file->filename, 0, 0, $bootstrap);
       
   148     }
       
   149   }
       
   150   $files = _module_build_dependencies($files);
       
   151   return $files;
       
   152 }
       
   153 
       
   154 /**
       
   155  * Find dependencies any level deep and fill in dependents information too.
       
   156  *
       
   157  * If module A depends on B which in turn depends on C then this function will
       
   158  * add C to the list of modules A depends on. This will be repeated until
       
   159  * module A has a list of all modules it depends on. If it depends on itself,
       
   160  * called a circular dependency, that's marked by adding a nonexistent module,
       
   161  * called -circular- to this list of modules. Because this does not exist,
       
   162  * it'll be impossible to switch module A on.
       
   163  *
       
   164  * Also we fill in a dependents array in $file->info. Using the names above,
       
   165  * the dependents array of module B lists A.
       
   166  *
       
   167  * @param $files
       
   168  *   The array of filesystem objects used to rebuild the cache.
       
   169  * @return
       
   170  *   The same array with dependencies and dependents added where applicable.
       
   171  */
       
   172 function _module_build_dependencies($files) {
       
   173   do {
       
   174     $new_dependency = FALSE;
       
   175     foreach ($files as $filename => $file) {
       
   176       // We will modify this object (module A, see doxygen for module A, B, C).
       
   177       $file = &$files[$filename];
       
   178       if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
       
   179         foreach ($file->info['dependencies'] as $dependency_name) {
       
   180           // This is a nonexistent module.
       
   181           if ($dependency_name == '-circular-' || !isset($files[$dependency_name])) {
       
   182             continue;
       
   183           }
       
   184           // $dependency_name is module B (again, see doxygen).
       
   185           $files[$dependency_name]->info['dependents'][$filename] = $filename;
       
   186           $dependency = $files[$dependency_name];
       
   187           if (isset($dependency->info['dependencies']) && is_array($dependency->info['dependencies'])) {
       
   188             // Let's find possible C modules.
       
   189             foreach ($dependency->info['dependencies'] as $candidate) {
       
   190               if (array_search($candidate, $file->info['dependencies']) === FALSE) {
       
   191                 // Is this a circular dependency?
       
   192                 if ($candidate == $filename) {
       
   193                   // As a module name can not contain dashes, this makes
       
   194                   // impossible to switch on the module.
       
   195                   $candidate = '-circular-';
       
   196                   // Do not display the message or add -circular- more than once.
       
   197                   if (array_search($candidate, $file->info['dependencies']) !== FALSE) {
       
   198                     continue;
       
   199                   }
       
   200                   drupal_set_message(t('%module is part of a circular dependency. This is not supported and you will not be able to switch it on.', array('%module' => $file->info['name'])), 'error');
       
   201                 }
       
   202                 else {
       
   203                   // We added a new dependency to module A. The next loop will
       
   204                   // be able to use this as "B module" thus finding even
       
   205                   // deeper dependencies.
       
   206                   $new_dependency = TRUE;
       
   207                 }
       
   208                 $file->info['dependencies'][] = $candidate;
       
   209               }
       
   210             }
       
   211           }
       
   212         }
       
   213       }
       
   214       // Don't forget to break the reference.
       
   215       unset($file);
       
   216     }
       
   217   } while ($new_dependency);
       
   218   return $files;
       
   219 }
       
   220 
       
   221 /**
       
   222  * Determine whether a given module exists.
       
   223  *
       
   224  * @param $module
       
   225  *   The name of the module (without the .module extension).
       
   226  * @return
       
   227  *   TRUE if the module is both installed and enabled.
       
   228  */
       
   229 function module_exists($module) {
       
   230   $list = module_list();
       
   231   return array_key_exists($module, $list);
       
   232 }
       
   233 
       
   234 /**
       
   235  * Load a module's installation hooks.
       
   236  */
       
   237 function module_load_install($module) {
       
   238   // Make sure the installation API is available
       
   239   include_once './includes/install.inc';
       
   240 
       
   241   module_load_include('install', $module);
       
   242 }
       
   243 
       
   244 /**
       
   245  * Load a module include file.
       
   246  *
       
   247  * @param $type
       
   248  *   The include file's type (file extension).
       
   249  * @param $module
       
   250  *   The module to which the include file belongs.
       
   251  * @param $name
       
   252  *   Optionally, specify the file name. If not set, the module's name is used.
       
   253  */
       
   254 function module_load_include($type, $module, $name = NULL) {
       
   255   if (empty($name)) {
       
   256     $name = $module;
       
   257   }
       
   258 
       
   259   $file = './'. drupal_get_path('module', $module) ."/$name.$type";
       
   260 
       
   261   if (is_file($file)) {
       
   262     require_once $file;
       
   263   }
       
   264   else {
       
   265     return FALSE;
       
   266   }
       
   267 }
       
   268 
       
   269 /**
       
   270  * Load an include file for each of the modules that have been enabled in
       
   271  * the system table.
       
   272  */
       
   273 function module_load_all_includes($type, $name = NULL) {
       
   274   $modules = module_list();
       
   275   foreach ($modules as $module) {
       
   276     module_load_include($type, $module, $name);
       
   277   }
       
   278 }
       
   279 
       
   280 /**
       
   281  * Enable a given list of modules.
       
   282  *
       
   283  * @param $module_list
       
   284  *   An array of module names.
       
   285  */
       
   286 function module_enable($module_list) {
       
   287   $invoke_modules = array();
       
   288   foreach ($module_list as $module) {
       
   289     $existing = db_fetch_object(db_query("SELECT status FROM {system} WHERE type = '%s' AND name = '%s'", 'module', $module));
       
   290     if ($existing->status == 0) {
       
   291       module_load_install($module);
       
   292       db_query("UPDATE {system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s'", 1, 0, 'module', $module);
       
   293       drupal_load('module', $module);
       
   294       $invoke_modules[] = $module;
       
   295     }
       
   296   }
       
   297 
       
   298   if (!empty($invoke_modules)) {
       
   299     // Refresh the module list to include the new enabled module.
       
   300     module_list(TRUE, FALSE);
       
   301     // Force to regenerate the stored list of hook implementations.
       
   302     module_implements('', FALSE, TRUE);
       
   303   }
       
   304 
       
   305   foreach ($invoke_modules as $module) {
       
   306     module_invoke($module, 'enable');
       
   307     // Check if node_access table needs rebuilding.
       
   308     // We check for the existence of node_access_needs_rebuild() since
       
   309     // at install time, module_enable() could be called while node.module
       
   310     // is not enabled yet.
       
   311     if (function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
       
   312       node_access_needs_rebuild(TRUE);
       
   313     }
       
   314   }
       
   315 }
       
   316 
       
   317 /**
       
   318  * Disable a given set of modules.
       
   319  *
       
   320  * @param $module_list
       
   321  *   An array of module names.
       
   322  */
       
   323 function module_disable($module_list) {
       
   324   $invoke_modules = array();
       
   325   foreach ($module_list as $module) {
       
   326     if (module_exists($module)) {
       
   327       // Check if node_access table needs rebuilding.
       
   328       if (!node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
       
   329         node_access_needs_rebuild(TRUE);
       
   330       }
       
   331 
       
   332       module_load_install($module);
       
   333       module_invoke($module, 'disable');
       
   334       db_query("UPDATE {system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s'", 0, 0, 'module', $module);
       
   335       $invoke_modules[] = $module;
       
   336     }
       
   337   }
       
   338 
       
   339   if (!empty($invoke_modules)) {
       
   340     // Refresh the module list to exclude the disabled modules.
       
   341     module_list(TRUE, FALSE);
       
   342     // Force to regenerate the stored list of hook implementations.
       
   343     module_implements('', FALSE, TRUE);
       
   344   }
       
   345 
       
   346   // If there remains no more node_access module, rebuilding will be
       
   347   // straightforward, we can do it right now.
       
   348   if (node_access_needs_rebuild() && count(module_implements('node_grants')) == 0) {
       
   349     node_access_rebuild();
       
   350   }
       
   351 }
       
   352 
       
   353 /**
       
   354  * @defgroup hooks Hooks
       
   355  * @{
       
   356  * Allow modules to interact with the Drupal core.
       
   357  *
       
   358  * Drupal's module system is based on the concept of "hooks". A hook is a PHP
       
   359  * function that is named foo_bar(), where "foo" is the name of the module (whose
       
   360  * filename is thus foo.module) and "bar" is the name of the hook. Each hook has
       
   361  * a defined set of parameters and a specified result type.
       
   362  *
       
   363  * To extend Drupal, a module need simply implement a hook. When Drupal wishes to
       
   364  * allow intervention from modules, it determines which modules implement a hook
       
   365  * and call that hook in all enabled modules that implement it.
       
   366  *
       
   367  * The available hooks to implement are explained here in the Hooks section of
       
   368  * the developer documentation. The string "hook" is used as a placeholder for
       
   369  * the module name is the hook definitions. For example, if the module file is
       
   370  * called example.module, then hook_help() as implemented by that module would be
       
   371  * defined as example_help().
       
   372  */
       
   373 
       
   374 /**
       
   375  * Determine whether a module implements a hook.
       
   376  *
       
   377  * @param $module
       
   378  *   The name of the module (without the .module extension).
       
   379  * @param $hook
       
   380  *   The name of the hook (e.g. "help" or "menu").
       
   381  * @return
       
   382  *   TRUE if the module is both installed and enabled, and the hook is
       
   383  *   implemented in that module.
       
   384  */
       
   385 function module_hook($module, $hook) {
       
   386   return function_exists($module .'_'. $hook);
       
   387 }
       
   388 
       
   389 /**
       
   390  * Determine which modules are implementing a hook.
       
   391  *
       
   392  * @param $hook
       
   393  *   The name of the hook (e.g. "help" or "menu").
       
   394  * @param $sort
       
   395  *   By default, modules are ordered by weight and filename, settings this option
       
   396  *   to TRUE, module list will be ordered by module name.
       
   397  * @param $refresh
       
   398  *   For internal use only: Whether to force the stored list of hook
       
   399  *   implementations to be regenerated (such as after enabling a new module,
       
   400  *   before processing hook_enable).
       
   401  * @return
       
   402  *   An array with the names of the modules which are implementing this hook.
       
   403  */
       
   404 function module_implements($hook, $sort = FALSE, $refresh = FALSE) {
       
   405   static $implementations;
       
   406 
       
   407   if ($refresh) {
       
   408     $implementations = array();
       
   409     return;
       
   410   }
       
   411 
       
   412   if (!isset($implementations[$hook])) {
       
   413     $implementations[$hook] = array();
       
   414     $list = module_list(FALSE, TRUE, $sort);
       
   415     foreach ($list as $module) {
       
   416       if (module_hook($module, $hook)) {
       
   417         $implementations[$hook][] = $module;
       
   418       }
       
   419     }
       
   420   }
       
   421 
       
   422   // The explicit cast forces a copy to be made. This is needed because
       
   423   // $implementations[$hook] is only a reference to an element of
       
   424   // $implementations and if there are nested foreaches (due to nested node
       
   425   // API calls, for example), they would both manipulate the same array's
       
   426   // references, which causes some modules' hooks not to be called.
       
   427   // See also http://www.zend.com/zend/art/ref-count.php.
       
   428   return (array)$implementations[$hook];
       
   429 }
       
   430 
       
   431 /**
       
   432  * Invoke a hook in a particular module.
       
   433  *
       
   434  * @param $module
       
   435  *   The name of the module (without the .module extension).
       
   436  * @param $hook
       
   437  *   The name of the hook to invoke.
       
   438  * @param ...
       
   439  *   Arguments to pass to the hook implementation.
       
   440  * @return
       
   441  *   The return value of the hook implementation.
       
   442  */
       
   443 function module_invoke() {
       
   444   $args = func_get_args();
       
   445   $module = $args[0];
       
   446   $hook = $args[1];
       
   447   unset($args[0], $args[1]);
       
   448   $function = $module .'_'. $hook;
       
   449   if (module_hook($module, $hook)) {
       
   450     return call_user_func_array($function, $args);
       
   451   }
       
   452 }
       
   453 /**
       
   454  * Invoke a hook in all enabled modules that implement it.
       
   455  *
       
   456  * @param $hook
       
   457  *   The name of the hook to invoke.
       
   458  * @param ...
       
   459  *   Arguments to pass to the hook.
       
   460  * @return
       
   461  *   An array of return values of the hook implementations. If modules return
       
   462  *   arrays from their implementations, those are merged into one array.
       
   463  */
       
   464 function module_invoke_all() {
       
   465   $args = func_get_args();
       
   466   $hook = $args[0];
       
   467   unset($args[0]);
       
   468   $return = array();
       
   469   foreach (module_implements($hook) as $module) {
       
   470     $function = $module .'_'. $hook;
       
   471     $result = call_user_func_array($function, $args);
       
   472     if (isset($result) && is_array($result)) {
       
   473       $return = array_merge_recursive($return, $result);
       
   474     }
       
   475     else if (isset($result)) {
       
   476       $return[] = $result;
       
   477     }
       
   478   }
       
   479 
       
   480   return $return;
       
   481 }
       
   482 
       
   483 /**
       
   484  * @} End of "defgroup hooks".
       
   485  */
       
   486 
       
   487 /**
       
   488  * Array of modules required by core.
       
   489  */
       
   490 function drupal_required_modules() {
       
   491   return array('block', 'filter', 'node', 'system', 'user');
       
   492 }