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