Mercurial > defr > drupal > core
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 } |