comparison includes/module.inc @ 1:c1f4ac30525a 6.0

Drupal 6.0
author Franck Deroche <webmaster@defr.org>
date Tue, 23 Dec 2008 14:28:28 +0100
parents
children
comparison
equal deleted inserted replaced
0:5a113a1c4740 1:c1f4ac30525a
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) {
48 static $list, $sorted_list;
49
50 if ($refresh || $fixed_list) {
51 unset($sorted_list);
52 $list = array();
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 }