Simpletest Coverage - includes/module.inc

1 <?php
2 // $Id: module.inc,v 1.152 2009/08/13 03:03:03 webchick Exp $
3
4 /**
5 * @file
6 * API for loading and interacting with Drupal modules.
7 */
8
9 /**
10 * Pass this to module_implements when its cache needs to be written.
11 */
12 define('MODULE_IMPLEMENTS_WRITE_CACHE', -1);
13
14 /**
15 * Pass this to module_implements when its cache needs to be cleared.
16 */
17 define('MODULE_IMPLEMENTS_CLEAR_CACHE', -2);
18
19
20 /**
21 * Load all the modules that have been enabled in the system table.
22 */
23 function module_load_all() {
24 foreach (module_list(TRUE) as $module) {
25 drupal_load('module', $module);
26 }
27 }
28
29 /**
30 * Collect a list of all loaded modules. During the bootstrap, return only
31 * vital modules. See bootstrap.inc
32 *
33 * @param $refresh
34 * Whether to force the module list to be regenerated (such as after the
35 * administrator has changed the system settings).
36 * @param $sort
37 * By default, modules are ordered by weight and module name. Set this option
38 * to TRUE to return a module list ordered only by module name.
39 * @param $fixed_list
40 * (Optional) Override the module list with the given modules. Stays until the
41 * next call with $refresh = TRUE.
42 * @return
43 * An associative array whose keys and values are the names of all loaded
44 * modules.
45 */
46 function module_list($refresh = FALSE, $sort = FALSE, $fixed_list = NULL) {
47 static $list = array(), $sorted_list;
48
49 if (empty($list) || $refresh || $fixed_list) {
50 $list = array();
51 $sorted_list = NULL;
52 if ($fixed_list) {
53 foreach ($fixed_list as $name => $module) {
54 drupal_get_filename('module', $name, $module['filename']);
55 $list[$name] = $name;
56 }
57 }
58 else {
59 // The module name (rather than the filename) is used as the fallback
60 // weighting in order to guarantee consistent behavior across different
61 // Drupal installations, which might have modules installed in different
62 // locations in the file system. The ordering here must also be
63 // consistent with the one used in module_implements().
64 $result = db_query("SELECT name, filename FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, name ASC");
65 foreach ($result as $module) {
66 if (file_exists($module->filename)) {
67 drupal_get_filename('module', $module->name, $module->filename);
68 $list[$module->name] = $module->name;
69 }
70 }
71 }
72 }
73 if ($sort) {
74 if (!isset($sorted_list)) {
75 $sorted_list = $list;
76 ksort($sorted_list);
77 }
78 return $sorted_list;
79 }
80 return $list;
81 }
82
83 /**
84 * Find dependencies any level deep and fill in required by information too.
85 *
86 * @param $files
87 * The array of filesystem objects used to rebuild the cache.
88 * @return
89 * The same array with the new keys for each module:
90 * - requires: An array with the keys being the modules that this module
91 * requires.
92 * - required_by: An array with the keys being the modules that will not work
93 * without this module.
94 */
95 function _module_build_dependencies($files) {
96 require_once DRUPAL_ROOT . '/includes/graph.inc';
97 $roots = $files;
98 foreach ($files as $filename => $file) {
99 $graph[$file->name]['edges'] = array();
100 if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
101 foreach ($file->info['dependencies'] as $dependency) {
102 $dependency_data = drupal_parse_dependency($dependency);
103 $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data;
104 unset($roots[$dependency_data['name']]);
105 }
106 }
107 }
108 drupal_depth_first_search($graph, array_keys($roots));
109 foreach ($graph as $module => $data) {
110 $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array();
111 $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array();
112 $files[$module]->sort = $data['weight'];
113 }
114 return $files;
115 }
116
117 /**
118 * Determine whether a given module exists.
119 *
120 * @param $module
121 * The name of the module (without the .module extension).
122 * @return
123 * TRUE if the module is both installed and enabled.
124 */
125 function module_exists($module) {
126 $list = module_list();
127 return isset($list[$module]);
128 }
129
130 /**
131 * Load a module's installation hooks.
132 */
133 function module_load_install($module) {
134 // Make sure the installation API is available
135 include_once DRUPAL_ROOT . '/includes/install.inc';
136
137 module_load_include('install', $module);
138 }
139
140 /**
141 * Load a module include file.
142 *
143 * Examples:
144 * @code
145 * // Load node.admin.inc from the node module.
146 * module_load_include('inc', 'node', 'node.admin');
147 * // Load content_types.inc from the node module.
148 * module_load_include('inc', 'node', 'content_types');
149 * @endcode
150 *
151 * Do not use this function to load an install file. Use module_load_install()
152 * instead.
153 *
154 * @param $type
155 * The include file's type (file extension).
156 * @param $module
157 * The module to which the include file belongs.
158 * @param $name
159 * Optionally, specify the base file name (without the $type extension).
160 * If not set, $module is used.
161 */
162 function module_load_include($type, $module, $name = NULL) {
163 if (empty($name)) {
164 $name = $module;
165 }
166
167 if (drupal_function_exists('drupal_get_path')) {
168 $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
169 if (is_file($file)) {
170 require_once $file;
171 return $file;
172 }
173 }
174 return FALSE;
175 }
176
177 /**
178 * Load an include file for each of the modules that have been enabled in
179 * the system table.
180 */
181 function module_load_all_includes($type, $name = NULL) {
182 $modules = module_list();
183 foreach ($modules as $module) {
184 module_load_include($type, $module, $name);
185 }
186 }
187
188 /**
189 * Enable a given list of modules.
190 *
191 * @param $module_list
192 * An array of module names.
193 * @param $disable_modules_installed_hook
194 * Normally just testing wants to set this to TRUE.
195 */
196 function module_enable($module_list, $disable_modules_installed_hook = FALSE) {
197 $invoke_modules = array();
198
199 // Try to install the enabled modules and collect which were installed.
200 // $module_list is not changed and already installed modules are ignored.
201 $modules_installed = array_filter($module_list, '_drupal_install_module');
202 foreach ($module_list as $module) {
203 $existing = db_query("SELECT status FROM {system} WHERE type = :type AND name = :name", array(
204 ':type' => 'module',
205 ':name' => $module))
206 ->fetchObject();
207 if ($existing->status == 0) {
208 module_load_install($module);
209 db_update('system')
210 ->fields(array('status' => 1))
211 ->condition('type', 'module')
212 ->condition('name', $module)
213 ->execute();
214 drupal_load('module', $module);
215 $invoke_modules[] = $module;
216 watchdog('system', '%module module enabled.', array('%module' => $module), WATCHDOG_INFO);
217 }
218 }
219
220 if (!empty($invoke_modules)) {
221 // Refresh the module list to include the new enabled module.
222 module_list(TRUE);
223 // Force to regenerate the stored list of hook implementations.
224 registry_rebuild();
225
226 // If any modules were newly installed, execute the hook for them.
227 if (!$disable_modules_installed_hook && !empty($modules_installed)) {
228 module_invoke_all('modules_installed', $modules_installed);
229 }
230 }
231
232 foreach ($invoke_modules as $module) {
233 module_invoke($module, 'enable');
234 // Check if node_access table needs rebuilding.
235 // We check for the existence of node_access_needs_rebuild() since
236 // at install time, module_enable() could be called while node.module
237 // is not enabled yet.
238 if (drupal_function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
239 node_access_needs_rebuild(TRUE);
240 }
241 }
242
243 if (!empty($invoke_modules)) {
244 // Invoke the hook_module_enable after all the modules have been
245 // enabled.
246 module_invoke_all('modules_enabled', $invoke_modules);
247 }
248 }
249
250 /**
251 * Disable a given set of modules.
252 *
253 * @param $module_list
254 * An array of module names.
255 */
256 function module_disable($module_list) {
257 $invoke_modules = array();
258 foreach ($module_list as $module) {
259 if (module_exists($module)) {
260 // Check if node_access table needs rebuilding.
261 if (!node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
262 node_access_needs_rebuild(TRUE);
263 }
264
265 module_load_install($module);
266 module_invoke($module, 'disable');
267 db_update('system')
268 ->fields(array('status' => 0))
269 ->condition('type', 'module')
270 ->condition('name', $module)
271 ->execute();
272 $invoke_modules[] = $module;
273 watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO);
274 }
275 }
276
277 if (!empty($invoke_modules)) {
278 // Invoke hook_module_disable before disabling modules,
279 // so we can still call module hooks to get information.
280 module_invoke_all('modules_disabled', $invoke_modules);
281 // Refresh the module list to exclude the disabled modules.
282 module_list(TRUE);
283 // Force to regenerate the stored list of hook implementations.
284 registry_rebuild();
285 }
286
287 // If there remains no more node_access module, rebuilding will be
288 // straightforward, we can do it right now.
289 if (node_access_needs_rebuild() && count(module_implements('node_grants')) == 0) {
290 node_access_rebuild();
291 }
292 }
293
294 /**
295 * @defgroup hooks Hooks
296 * @{
297 * Allow modules to interact with the Drupal core.
298 *
299 * Drupal's module system is based on the concept of "hooks". A hook is a PHP
300 * function that is named foo_bar(), where "foo" is the name of the module (whose
301 * filename is thus foo.module) and "bar" is the name of the hook. Each hook has
302 * a defined set of parameters and a specified result type.
303 *
304 * To extend Drupal, a module need simply implement a hook. When Drupal wishes to
305 * allow intervention from modules, it determines which modules implement a hook
306 * and call that hook in all enabled modules that implement it.
307 *
308 * The available hooks to implement are explained here in the Hooks section of
309 * the developer documentation. The string "hook" is used as a placeholder for
310 * the module name is the hook definitions. For example, if the module file is
311 * called example.module, then hook_help() as implemented by that module would be
312 * defined as example_help().
313 */
314
315 /**
316 * Determine whether a module implements a hook.
317 *
318 * @param $module
319 * The name of the module (without the .module extension).
320 * @param $hook
321 * The name of the hook (e.g. "help" or "menu").
322 * @return
323 * TRUE if the module is both installed and enabled, and the hook is
324 * implemented in that module.
325 */
326 function module_hook($module, $hook) {
327 $function = $module . '_' . $hook;
328 return function_exists($function) || drupal_function_exists($function);
329 }
330
331 /**
332 * Determine which modules are implementing a hook.
333 *
334 * @param $hook
335 * The name of the hook (e.g. "help" or "menu"). Special cases:
336 * MODULE_IMPLEMENTS_CLEAR_CACHE: Force the stored list of hook
337 * implementations to be regenerated (such as after enabling a new module,
338 * before processing hook_enable).
339 * MODULE_IMPLEMENTS_WRITE_CACHE: Write the stored list of hook
340 * implementations into the cache_registry table.
341 * @param $sort
342 * By default, modules are ordered by weight and module name. By setting this
343 * option to TRUE, modules will be ordered by module name.
344 * @return
345 * An array with the names of the modules which are implementing this hook.
346 * All enabled modules are taken into consideration and the files containing
347 * the implementations are loaded as necessary.
348 */
349 function module_implements($hook, $sort = FALSE) {
350 static $implementations = array(), $sorted_implementations = array(), $loaded = array(), $cached_hooks = 0, $maintenance;
351
352 // Use a static variable for maintenance mode to avoid the overhead of
353 // calling defined() each time the function is called.
354 if (!isset($maintenance)) {
355 $maintenance = defined('MAINTENANCE_MODE');
356 }
357
358 if ($maintenance) {
359 return _module_implements_maintenance($hook, $sort);
360 }
361 if ($hook === MODULE_IMPLEMENTS_CLEAR_CACHE) {
362 $implementations = array();
363 $sorted_implementations = array();
364 $loaded = array();
365 $cached_hooks = 0;
366 cache_clear_all('hooks', 'cache_registry');
367 return;
368 }
369 if ($hook === MODULE_IMPLEMENTS_WRITE_CACHE) {
370 // Only write this to cache if we loaded new implementations.
371 if (count($implementations) > $cached_hooks) {
372 cache_set('hooks', $implementations, 'cache_registry');
373 }
374 return;
375 }
376
377 if (!isset($loaded[$hook])) {
378 if (empty($implementations) && ($cache = cache_get('hooks', 'cache_registry'))) {
379 $implementations = $cache->data;
380 $cached_hooks = count($implementations);
381 }
382 if (!isset($implementations[$hook])) {
383 // The module name (rather than the filename) is used as the fallback
384 // weighting in order to guarantee consistent behavior across different
385 // Drupal installations, which might have modules installed in different
386 // locations in the file system. The ordering here must also be
387 // consistent with the one used in module_list().
388 $implementations[$hook] = db_query("SELECT module FROM {registry} WHERE type = 'function' AND suffix = :hook ORDER BY weight, module", array(':hook' => $hook))->fetchCol();
389 }
390 foreach ($implementations[$hook] as $module) {
391 $function = $module . '_' . $hook;
392 if (!function_exists($function)) {
393 drupal_function_exists($function);
394 }
395 }
396 $loaded[$hook] = TRUE;
397 }
398
399 if ($sort) {
400 if (!isset($sorted_implementations[$hook])) {
401 $sorted_implementations[$hook] = $implementations[$hook];
402 sort($sorted_implementations[$hook]);
403 }
404 return $sorted_implementations[$hook];
405 }
406 else {
407 return $implementations[$hook];
408 }
409 }
410
411 /**
412 * This is the maintenance version of module_implements for internal use only.
413 *
414 * This function is called whenever MAINTENANCE_MODE is defined and is a
415 * safe code path for Drupal installation or upgrade because it does not use
416 * the database, instead it uses module_list. @see module_list $fixed_list on
417 * how to make module_list also DB independent.
418 *
419 * @param $hook
420 * The name of the hook (e.g. "help" or "menu").
421 * @param $sort
422 * By default, modules are ordered by weight and filename, settings this
423 * option to TRUE, module list will be ordered by module name.
424 * @return
425 * An array with the names of the modules which are implementing this hook.
426 * Only enabled and already loaded modules are taken into consideration.
427 */
428 function _module_implements_maintenance($hook, $sort = FALSE) {
429 $implementations = array();
430 foreach (module_list() as $module) {
431 $function = $module . '_' . $hook;
432 if (function_exists($function)) {
433 $implementations[] = $module;
434 }
435 if ($sort) {
436 sort($implementations);
437 }
438 }
439 return $implementations;
440 }
441
442 /**
443 * Invoke a hook in a particular module.
444 *
445 * @param $module
446 * The name of the module (without the .module extension).
447 * @param $hook
448 * The name of the hook to invoke.
449 * @param ...
450 * Arguments to pass to the hook implementation.
451 * @return
452 * The return value of the hook implementation.
453 */
454 function module_invoke() {
455 $args = func_get_args();
456 $module = $args[0];
457 $hook = $args[1];
458 unset($args[0], $args[1]);
459 if (module_hook($module, $hook)) {
460 return call_user_func_array($module . '_' . $hook, $args);
461 }
462 }
463 /**
464 * Invoke a hook in all enabled modules that implement it.
465 *
466 * @param $hook
467 * The name of the hook to invoke.
468 * @param ...
469 * Arguments to pass to the hook.
470 * @return
471 * An array of return values of the hook implementations. If modules return
472 * arrays from their implementations, those are merged into one array.
473 */
474 function module_invoke_all() {
475 $args = func_get_args();
476 $hook = $args[0];
477 unset($args[0]);
478 $return = array();
479 foreach (module_implements($hook) as $module) {
480 $function = $module . '_' . $hook;
481 if (drupal_function_exists($function)) {
482 $result = call_user_func_array($function, $args);
483 if (isset($result) && is_array($result)) {
484 $return = array_merge_recursive($return, $result);
485 }
486 elseif (isset($result)) {
487 $return[] = $result;
488 }
489 }
490 }
491
492 return $return;
493 }
494
495 /**
496 * @} End of "defgroup hooks".
497 */
498
499 /**
500 * Array of modules required by core.
501 */
502 function drupal_required_modules() {
503 $files = drupal_system_listing('/\.info$/', 'modules', 'name', 0);
504 $required = array();
505 foreach ($files as $name => $file) {
506 $info = drupal_parse_info_file($file->filepath);
507 if (!empty($info) && !empty($info['required']) && $info['required']) {
508 $required[] = $name;
509 }
510 }
511 return $required;
512 }
513

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.