Spike PHPCoverage Details: module.inc

Line #FrequencySource Line
1 <?php
2 // $Id: module.inc,v 1.115 2007/12/27 12:31:05 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) {
481  static $list, $sorted_list;
49 
501  if ($refresh || $fixed_list) {
511    unset($sorted_list);
52     $list = array();
531    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 {
601      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 {
641        $result = db_query("SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC");
65       }
661      while ($module = db_fetch_object($result)) {
671        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.
711          $throttle = ($module->throttle && variable_get('throttle_level', 0) > 0);
721          if (!$throttle) {
731            drupal_get_filename('module', $module->name, $module->filename);
741            $list[$module->name] = $module->name;
75           }
76         }
77       }
78     }
79   }
801  if ($sort) {
811    if (!isset($sorted_list)) {
821      $sorted_list = $list;
831      ksort($sorted_list);
84     }
851    return $sorted_list;
86   }
871  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
981  $files = drupal_system_listing('\.module$', 'modules', 'name', 0);
99 
100   // Extract current files from database.
1011  system_get_files_database($files, 'module');
102 
1031  ksort($files);
104 
105   // Set defaults for module info
106   $defaults = array(
1071    'dependencies' => array(),
108     'dependents' => array(),
109     'description' => '',
110     'version' => NULL,
111     'php' => DRUPAL_MINIMUM_PHP,
112   );
113 
1141  foreach ($files as $filename => $file) {
115     // Look for the info file.
1161    $file->info = drupal_parse_info_file(dirname($file->filename) .'/'. $file->name .'.info');
117 
118     // Skip modules that don't provide info.
1191    if (empty($file->info)) {
120       unset($files[$filename]);
121       continue;
122     }
123     // Merge in defaults and save.
1241    $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.
1281    drupal_alter('system_info', $files[$filename]->info, $files[$filename]);
129 
130     // Log the critical hooks implemented by this module.
1311    $bootstrap = 0;
1321    foreach (bootstrap_hooks() as $hook) {
1331      if (module_hook($file->name, $hook)) {
134         $bootstrap = 1;
135         break;
136       }
137     }
138 
139     // Update the contents of the system table:
1401    if (isset($file->status) || (isset($file->old_filename) && $file->old_filename != $file->filename)) {
1411      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.
1451      $files[$filename]->status = 0;
1461      $files[$filename]->throttle = 0;
1471      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   }
1501  $files = _module_build_dependencies($files);
1511  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 {
1741    $new_dependency = FALSE;
1751    foreach ($files as $filename => $file) {
176       // We will modify this object (module A, see doxygen for module A, B, C).
1771      $file = &$files[$filename];
1781      if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
1791        foreach ($file->info['dependencies'] as $dependency_name) {
180           // This is a nonexistent module.
1811          if ($dependency_name == '-circular-' || !isset($files[$dependency_name])) {
182             continue;
183           }
184           // $dependency_name is module B (again, see doxygen).
1851          $files[$dependency_name]->info['dependents'][$filename] = $filename;
1861          $dependency = $files[$dependency_name];
1871          if (isset($dependency->info['dependencies']) && is_array($dependency->info['dependencies'])) {
188             // Let's find possible C modules.
1891            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.
2151      unset($file);
216     }
217   } while ($new_dependency);
2181  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) {
2301  $list = module_list();
2311  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 
2411  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) {
2551  if (empty($name)) {
2561    $name = $module;
257   }
258 
2591  $file = './'. drupal_get_path('module', $module) ."/$name.$type";
260 
2611  if (is_file($file)) {
262     require_once $file;
263   }
264   else {
2651    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) {
2741  $modules = module_list();
2751  foreach ($modules as $module) {
2761    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();
2881  foreach ($module_list as $module) {
2891    $existing = db_fetch_object(db_query("SELECT status FROM {system} WHERE type = '%s' AND name = '%s'", 'module', $module));
2901    if ($existing->status == 0) {
2911      module_load_install($module);
2921      db_query("UPDATE {system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s'", 1, 0, 'module', $module);
2931      drupal_load('module', $module);
2941      $invoke_modules[] = $module;
295     }
296   }
297 
2981  if (!empty($invoke_modules)) {
299     // Refresh the module list to include the new enabled module.
3001    module_list(TRUE, FALSE);
301     // Force to regenerate the stored list of hook implementations.
3021    module_implements('', FALSE, TRUE);
303   }
304 
3051  foreach ($invoke_modules as $module) {
3061    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.
3111    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) {
3861  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) {
4051  static $implementations;
406 
4071  if ($refresh) {
408     $implementations = array();
4091    return;
410   }
411 
4121  if (!isset($implementations[$hook])) {
413     $implementations[$hook] = array();
4141    $list = module_list(FALSE, TRUE, $sort);
4151    foreach ($list as $module) {
4161      if (module_hook($module, $hook)) {
4171        $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.
4281  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() {
4441  $args = func_get_args();
4451  $module = $args[0];
4461  $hook = $args[1];
4471  unset($args[0], $args[1]);
4481  $function = $module .'_'. $hook;
4491  if (module_hook($module, $hook)) {
4501    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() {
4651  $args = func_get_args();
4661  $hook = $args[0];
4671  unset($args[0]);
468   $return = array();
4691  foreach (module_implements($hook) as $module) {
4701    $function = $module .'_'. $hook;
4711    $result = call_user_func_array($function, $args);
4721    if (isset($result) && is_array($result)) {
4731      $return = array_merge_recursive($return, $result);
474     }
4751    else if (isset($result)) {
476       $return[] = $result;
477     }
478   }
479 
4801  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() {
4911  return array('block', 'filter', 'node', 'system', 'user');
492 }