|
1 <?php |
|
2 |
|
3 /** |
|
4 * @file |
|
5 * API for loading and interacting with Drupal modules. |
|
6 */ |
|
7 |
|
8 /** |
|
9 * Loads all the modules that have been enabled in the system table. |
|
10 * |
|
11 * @param $bootstrap |
|
12 * Whether to load only the reduced set of modules loaded in "bootstrap mode" |
|
13 * for cached pages. See bootstrap.inc. |
|
14 * |
|
15 * @return |
|
16 * If $bootstrap is NULL, return a boolean indicating whether all modules |
|
17 * have been loaded. |
|
18 */ |
|
19 function module_load_all($bootstrap = FALSE) { |
|
20 static $has_run = FALSE; |
|
21 |
|
22 if (isset($bootstrap)) { |
|
23 foreach (module_list(TRUE, $bootstrap) as $module) { |
|
24 drupal_load('module', $module); |
|
25 } |
|
26 // $has_run will be TRUE if $bootstrap is FALSE. |
|
27 $has_run = !$bootstrap; |
|
28 } |
|
29 return $has_run; |
|
30 } |
|
31 |
|
32 |
|
33 /** |
|
34 * Returns a list of currently active modules. |
|
35 * |
|
36 * Usually, this returns a list of all enabled modules. When called early on in |
|
37 * the bootstrap, it will return a list of vital modules only (those needed to |
|
38 * generate cached pages). |
|
39 * |
|
40 * All parameters to this function are optional and should generally not be |
|
41 * changed from their defaults. |
|
42 * |
|
43 * @param $refresh |
|
44 * (optional) Whether to force the module list to be regenerated (such as |
|
45 * after the administrator has changed the system settings). Defaults to |
|
46 * FALSE. |
|
47 * @param $bootstrap_refresh |
|
48 * (optional) When $refresh is TRUE, setting $bootstrap_refresh to TRUE forces |
|
49 * the module list to be regenerated using the reduced set of modules loaded |
|
50 * in "bootstrap mode" for cached pages. Otherwise, setting $refresh to TRUE |
|
51 * generates the complete list of enabled modules. |
|
52 * @param $sort |
|
53 * (optional) By default, modules are ordered by weight and module name. Set |
|
54 * this option to TRUE to return a module list ordered only by module name. |
|
55 * @param $fixed_list |
|
56 * (optional) If an array of module names is provided, this will override the |
|
57 * module list with the given set of modules. This will persist until the next |
|
58 * call with $refresh set to TRUE or with a new $fixed_list passed in. This |
|
59 * parameter is primarily intended for internal use (e.g., in install.php and |
|
60 * update.php). |
|
61 * |
|
62 * @return |
|
63 * An associative array whose keys and values are the names of the modules in |
|
64 * the list. |
|
65 */ |
|
66 function module_list($refresh = FALSE, $bootstrap_refresh = FALSE, $sort = FALSE, $fixed_list = NULL) { |
|
67 static $list = array(), $sorted_list; |
|
68 |
|
69 if (empty($list) || $refresh || $fixed_list) { |
|
70 $list = array(); |
|
71 $sorted_list = NULL; |
|
72 if ($fixed_list) { |
|
73 foreach ($fixed_list as $name => $module) { |
|
74 drupal_get_filename('module', $name, $module['filename']); |
|
75 $list[$name] = $name; |
|
76 } |
|
77 } |
|
78 else { |
|
79 if ($refresh) { |
|
80 // For the $refresh case, make sure that system_list() returns fresh |
|
81 // data. |
|
82 drupal_static_reset('system_list'); |
|
83 } |
|
84 if ($bootstrap_refresh) { |
|
85 $list = system_list('bootstrap'); |
|
86 } |
|
87 else { |
|
88 // Not using drupal_map_assoc() here as that requires common.inc. |
|
89 $list = array_keys(system_list('module_enabled')); |
|
90 $list = (!empty($list) ? array_combine($list, $list) : array()); |
|
91 } |
|
92 } |
|
93 } |
|
94 if ($sort) { |
|
95 if (!isset($sorted_list)) { |
|
96 $sorted_list = $list; |
|
97 ksort($sorted_list); |
|
98 } |
|
99 return $sorted_list; |
|
100 } |
|
101 return $list; |
|
102 } |
|
103 |
|
104 /** |
|
105 * Builds a list of bootstrap modules and enabled modules and themes. |
|
106 * |
|
107 * @param $type |
|
108 * The type of list to return: |
|
109 * - module_enabled: All enabled modules. |
|
110 * - bootstrap: All enabled modules required for bootstrap. |
|
111 * - theme: All themes. |
|
112 * |
|
113 * @return |
|
114 * An associative array of modules or themes, keyed by name. For $type |
|
115 * 'bootstrap', the array values equal the keys. For $type 'module_enabled' |
|
116 * or 'theme', the array values are objects representing the respective |
|
117 * database row, with the 'info' property already unserialized. |
|
118 * |
|
119 * @see module_list() |
|
120 * @see list_themes() |
|
121 */ |
|
122 function system_list($type) { |
|
123 $lists = &drupal_static(__FUNCTION__); |
|
124 |
|
125 // For bootstrap modules, attempt to fetch the list from cache if possible. |
|
126 // if not fetch only the required information to fire bootstrap hooks |
|
127 // in case we are going to serve the page from cache. |
|
128 if ($type == 'bootstrap') { |
|
129 if (isset($lists['bootstrap'])) { |
|
130 return $lists['bootstrap']; |
|
131 } |
|
132 if ($cached = cache_get('bootstrap_modules', 'cache_bootstrap')) { |
|
133 $bootstrap_list = $cached->data; |
|
134 } |
|
135 else { |
|
136 $bootstrap_list = db_query("SELECT name, filename FROM {system} WHERE status = 1 AND bootstrap = 1 AND type = 'module' ORDER BY weight ASC, name ASC")->fetchAllAssoc('name'); |
|
137 cache_set('bootstrap_modules', $bootstrap_list, 'cache_bootstrap'); |
|
138 } |
|
139 // To avoid a separate database lookup for the filepath, prime the |
|
140 // drupal_get_filename() static cache for bootstrap modules only. |
|
141 // The rest is stored separately to keep the bootstrap module cache small. |
|
142 foreach ($bootstrap_list as $module) { |
|
143 drupal_get_filename('module', $module->name, $module->filename); |
|
144 } |
|
145 // We only return the module names here since module_list() doesn't need |
|
146 // the filename itself. |
|
147 $lists['bootstrap'] = array_keys($bootstrap_list); |
|
148 } |
|
149 // Otherwise build the list for enabled modules and themes. |
|
150 elseif (!isset($lists['module_enabled'])) { |
|
151 if ($cached = cache_get('system_list', 'cache_bootstrap')) { |
|
152 $lists = $cached->data; |
|
153 } |
|
154 else { |
|
155 $lists = array( |
|
156 'module_enabled' => array(), |
|
157 'theme' => array(), |
|
158 'filepaths' => array(), |
|
159 ); |
|
160 // The module name (rather than the filename) is used as the fallback |
|
161 // weighting in order to guarantee consistent behavior across different |
|
162 // Drupal installations, which might have modules installed in different |
|
163 // locations in the file system. The ordering here must also be |
|
164 // consistent with the one used in module_implements(). |
|
165 $result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC"); |
|
166 foreach ($result as $record) { |
|
167 $record->info = unserialize($record->info); |
|
168 // Build a list of all enabled modules. |
|
169 if ($record->type == 'module') { |
|
170 $lists['module_enabled'][$record->name] = $record; |
|
171 } |
|
172 // Build a list of themes. |
|
173 if ($record->type == 'theme') { |
|
174 $lists['theme'][$record->name] = $record; |
|
175 } |
|
176 // Build a list of filenames so drupal_get_filename can use it. |
|
177 if ($record->status) { |
|
178 $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename); |
|
179 } |
|
180 } |
|
181 foreach ($lists['theme'] as $key => $theme) { |
|
182 if (!empty($theme->info['base theme'])) { |
|
183 // Make a list of the theme's base themes. |
|
184 require_once DRUPAL_ROOT . '/includes/theme.inc'; |
|
185 $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key); |
|
186 // Don't proceed if there was a problem with the root base theme. |
|
187 if (!current($lists['theme'][$key]->base_themes)) { |
|
188 continue; |
|
189 } |
|
190 // Determine the root base theme. |
|
191 $base_key = key($lists['theme'][$key]->base_themes); |
|
192 // Add to the list of sub-themes for each of the theme's base themes. |
|
193 foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) { |
|
194 $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name']; |
|
195 } |
|
196 // Add the base theme's theme engine info. |
|
197 $lists['theme'][$key]->info['engine'] = isset($lists['theme'][$base_key]->info['engine']) ? $lists['theme'][$base_key]->info['engine'] : 'theme'; |
|
198 } |
|
199 else { |
|
200 // A plain theme is its own engine. |
|
201 $base_key = $key; |
|
202 if (!isset($lists['theme'][$key]->info['engine'])) { |
|
203 $lists['theme'][$key]->info['engine'] = 'theme'; |
|
204 } |
|
205 } |
|
206 // Set the theme engine prefix. |
|
207 $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; |
|
208 } |
|
209 cache_set('system_list', $lists, 'cache_bootstrap'); |
|
210 } |
|
211 // To avoid a separate database lookup for the filepath, prime the |
|
212 // drupal_get_filename() static cache with all enabled modules and themes. |
|
213 foreach ($lists['filepaths'] as $item) { |
|
214 drupal_get_filename($item['type'], $item['name'], $item['filepath']); |
|
215 } |
|
216 } |
|
217 |
|
218 return $lists[$type]; |
|
219 } |
|
220 |
|
221 /** |
|
222 * Resets all system_list() caches. |
|
223 */ |
|
224 function system_list_reset() { |
|
225 drupal_static_reset('system_list'); |
|
226 drupal_static_reset('system_rebuild_module_data'); |
|
227 drupal_static_reset('list_themes'); |
|
228 cache_clear_all('bootstrap_modules', 'cache_bootstrap'); |
|
229 cache_clear_all('system_list', 'cache_bootstrap'); |
|
230 |
|
231 // Clean up the bootstrap file scan cache. |
|
232 drupal_static_reset('_drupal_file_scan_cache'); |
|
233 cache_clear_all('_drupal_file_scan_cache', 'cache_bootstrap'); |
|
234 } |
|
235 |
|
236 /** |
|
237 * Determines which modules require and are required by each module. |
|
238 * |
|
239 * @param $files |
|
240 * The array of filesystem objects used to rebuild the cache. |
|
241 * |
|
242 * @return |
|
243 * The same array with the new keys for each module: |
|
244 * - requires: An array with the keys being the modules that this module |
|
245 * requires. |
|
246 * - required_by: An array with the keys being the modules that will not work |
|
247 * without this module. |
|
248 */ |
|
249 function _module_build_dependencies($files) { |
|
250 require_once DRUPAL_ROOT . '/includes/graph.inc'; |
|
251 foreach ($files as $filename => $file) { |
|
252 $graph[$file->name]['edges'] = array(); |
|
253 if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) { |
|
254 foreach ($file->info['dependencies'] as $dependency) { |
|
255 $dependency_data = drupal_parse_dependency($dependency); |
|
256 $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data; |
|
257 } |
|
258 } |
|
259 } |
|
260 drupal_depth_first_search($graph); |
|
261 foreach ($graph as $module => $data) { |
|
262 $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array(); |
|
263 $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array(); |
|
264 $files[$module]->sort = $data['weight']; |
|
265 } |
|
266 return $files; |
|
267 } |
|
268 |
|
269 /** |
|
270 * Determines whether a given module exists. |
|
271 * |
|
272 * @param string $module |
|
273 * The name of the module (without the .module extension). |
|
274 * |
|
275 * @return bool |
|
276 * TRUE if the module is both installed and enabled, FALSE otherwise. |
|
277 */ |
|
278 function module_exists($module) { |
|
279 $list = module_list(); |
|
280 return isset($list[$module]); |
|
281 } |
|
282 |
|
283 /** |
|
284 * Loads a module's installation hooks. |
|
285 * |
|
286 * @param $module |
|
287 * The name of the module (without the .module extension). |
|
288 * |
|
289 * @return |
|
290 * The name of the module's install file, if successful; FALSE otherwise. |
|
291 */ |
|
292 function module_load_install($module) { |
|
293 // Make sure the installation API is available |
|
294 include_once DRUPAL_ROOT . '/includes/install.inc'; |
|
295 |
|
296 return module_load_include('install', $module); |
|
297 } |
|
298 |
|
299 /** |
|
300 * Loads a module include file. |
|
301 * |
|
302 * Examples: |
|
303 * @code |
|
304 * // Load node.admin.inc from the node module. |
|
305 * module_load_include('inc', 'node', 'node.admin'); |
|
306 * // Load content_types.inc from the node module. |
|
307 * module_load_include('inc', 'node', 'content_types'); |
|
308 * @endcode |
|
309 * |
|
310 * Do not use this function to load an install file, use module_load_install() |
|
311 * instead. Do not use this function in a global context since it requires |
|
312 * Drupal to be fully bootstrapped, use require_once DRUPAL_ROOT . '/path/file' |
|
313 * instead. |
|
314 * |
|
315 * @param $type |
|
316 * The include file's type (file extension). |
|
317 * @param $module |
|
318 * The module to which the include file belongs. |
|
319 * @param $name |
|
320 * (optional) The base file name (without the $type extension). If omitted, |
|
321 * $module is used; i.e., resulting in "$module.$type" by default. |
|
322 * |
|
323 * @return |
|
324 * The name of the included file, if successful; FALSE otherwise. |
|
325 */ |
|
326 function module_load_include($type, $module, $name = NULL) { |
|
327 static $files = array(); |
|
328 |
|
329 if (!isset($name)) { |
|
330 $name = $module; |
|
331 } |
|
332 |
|
333 $key = $type . ':' . $module . ':' . $name; |
|
334 if (isset($files[$key])) { |
|
335 return $files[$key]; |
|
336 } |
|
337 |
|
338 if (function_exists('drupal_get_path')) { |
|
339 $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type"; |
|
340 if (is_file($file)) { |
|
341 require_once $file; |
|
342 $files[$key] = $file; |
|
343 return $file; |
|
344 } |
|
345 else { |
|
346 $files[$key] = FALSE; |
|
347 } |
|
348 } |
|
349 return FALSE; |
|
350 } |
|
351 |
|
352 /** |
|
353 * Loads an include file for each module enabled in the {system} table. |
|
354 */ |
|
355 function module_load_all_includes($type, $name = NULL) { |
|
356 $modules = module_list(); |
|
357 foreach ($modules as $module) { |
|
358 module_load_include($type, $module, $name); |
|
359 } |
|
360 } |
|
361 |
|
362 /** |
|
363 * Enables or installs a given list of modules. |
|
364 * |
|
365 * Definitions: |
|
366 * - "Enabling" is the process of activating a module for use by Drupal. |
|
367 * - "Disabling" is the process of deactivating a module. |
|
368 * - "Installing" is the process of enabling it for the first time or after it |
|
369 * has been uninstalled. |
|
370 * - "Uninstalling" is the process of removing all traces of a module. |
|
371 * |
|
372 * Order of events: |
|
373 * - Gather and add module dependencies to $module_list (if applicable). |
|
374 * - For each module that is being enabled: |
|
375 * - Install module schema and update system registries and caches. |
|
376 * - If the module is being enabled for the first time or had been |
|
377 * uninstalled, invoke hook_install() and add it to the list of installed |
|
378 * modules. |
|
379 * - Invoke hook_enable(). |
|
380 * - Invoke hook_modules_installed(). |
|
381 * - Invoke hook_modules_enabled(). |
|
382 * |
|
383 * @param string[] $module_list |
|
384 * An array of module names. |
|
385 * @param bool $enable_dependencies |
|
386 * If TRUE, dependencies will automatically be added and enabled in the |
|
387 * correct order. This incurs a significant performance cost, so use FALSE |
|
388 * if you know $module_list is already complete and in the correct order. |
|
389 * |
|
390 * @return bool |
|
391 * FALSE if one or more dependencies are missing, TRUE otherwise. |
|
392 * |
|
393 * @see hook_install() |
|
394 * @see hook_enable() |
|
395 * @see hook_modules_installed() |
|
396 * @see hook_modules_enabled() |
|
397 * @see module_disable() |
|
398 * @see drupal_uninstall_modules() |
|
399 */ |
|
400 function module_enable($module_list, $enable_dependencies = TRUE) { |
|
401 if ($enable_dependencies) { |
|
402 // Get all module data so we can find dependencies and sort. |
|
403 $module_data = system_rebuild_module_data(); |
|
404 // Create an associative array with weights as values. |
|
405 $module_list = array_flip(array_values($module_list)); |
|
406 |
|
407 while (list($module) = each($module_list)) { |
|
408 if (!isset($module_data[$module])) { |
|
409 // This module is not found in the filesystem, abort. |
|
410 return FALSE; |
|
411 } |
|
412 if ($module_data[$module]->status) { |
|
413 // Skip already enabled modules. |
|
414 unset($module_list[$module]); |
|
415 continue; |
|
416 } |
|
417 $module_list[$module] = $module_data[$module]->sort; |
|
418 |
|
419 // Add dependencies to the list, with a placeholder weight. |
|
420 // The new modules will be processed as the while loop continues. |
|
421 foreach (array_keys($module_data[$module]->requires) as $dependency) { |
|
422 if (!isset($module_list[$dependency])) { |
|
423 $module_list[$dependency] = 0; |
|
424 } |
|
425 } |
|
426 } |
|
427 |
|
428 if (!$module_list) { |
|
429 // Nothing to do. All modules already enabled. |
|
430 return TRUE; |
|
431 } |
|
432 |
|
433 // Sort the module list by pre-calculated weights. |
|
434 arsort($module_list); |
|
435 $module_list = array_keys($module_list); |
|
436 } |
|
437 |
|
438 // Required for module installation checks. |
|
439 include_once DRUPAL_ROOT . '/includes/install.inc'; |
|
440 |
|
441 $modules_installed = array(); |
|
442 $modules_enabled = array(); |
|
443 foreach ($module_list as $module) { |
|
444 // Only process modules that are not already enabled. |
|
445 $existing = db_query("SELECT status FROM {system} WHERE type = :type AND name = :name", array( |
|
446 ':type' => 'module', |
|
447 ':name' => $module)) |
|
448 ->fetchObject(); |
|
449 if ($existing->status == 0) { |
|
450 // Load the module's code. |
|
451 drupal_load('module', $module); |
|
452 module_load_install($module); |
|
453 |
|
454 // Update the database and module list to reflect the new module. This |
|
455 // needs to be done first so that the module's hook implementations, |
|
456 // hook_schema() in particular, can be called while it is being |
|
457 // installed. |
|
458 db_update('system') |
|
459 ->fields(array('status' => 1)) |
|
460 ->condition('type', 'module') |
|
461 ->condition('name', $module) |
|
462 ->execute(); |
|
463 // Refresh the module list to include it. |
|
464 system_list_reset(); |
|
465 module_list(TRUE); |
|
466 module_implements('', FALSE, TRUE); |
|
467 _system_update_bootstrap_status(); |
|
468 // Update the registry to include it. |
|
469 registry_update(); |
|
470 // Refresh the schema to include it. |
|
471 drupal_get_schema(NULL, TRUE); |
|
472 // Update the theme registry to include it. |
|
473 drupal_theme_rebuild(); |
|
474 // Clear entity cache. |
|
475 entity_info_cache_clear(); |
|
476 |
|
477 // Now install the module if necessary. |
|
478 if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) { |
|
479 drupal_install_schema($module); |
|
480 |
|
481 // Set the schema version to the number of the last update provided |
|
482 // by the module. |
|
483 $versions = drupal_get_schema_versions($module); |
|
484 $version = $versions ? max($versions) : SCHEMA_INSTALLED; |
|
485 |
|
486 // If the module has no current updates, but has some that were |
|
487 // previously removed, set the version to the value of |
|
488 // hook_update_last_removed(). |
|
489 if ($last_removed = module_invoke($module, 'update_last_removed')) { |
|
490 $version = max($version, $last_removed); |
|
491 } |
|
492 drupal_set_installed_schema_version($module, $version); |
|
493 // Allow the module to perform install tasks. |
|
494 module_invoke($module, 'install'); |
|
495 // Record the fact that it was installed. |
|
496 $modules_installed[] = $module; |
|
497 watchdog('system', '%module module installed.', array('%module' => $module), WATCHDOG_INFO); |
|
498 } |
|
499 |
|
500 // Enable the module. |
|
501 module_invoke($module, 'enable'); |
|
502 |
|
503 // Record the fact that it was enabled. |
|
504 $modules_enabled[] = $module; |
|
505 watchdog('system', '%module module enabled.', array('%module' => $module), WATCHDOG_INFO); |
|
506 } |
|
507 } |
|
508 |
|
509 // If any modules were newly installed, invoke hook_modules_installed(). |
|
510 if (!empty($modules_installed)) { |
|
511 module_invoke_all('modules_installed', $modules_installed); |
|
512 } |
|
513 |
|
514 // If any modules were newly enabled, invoke hook_modules_enabled(). |
|
515 if (!empty($modules_enabled)) { |
|
516 module_invoke_all('modules_enabled', $modules_enabled); |
|
517 } |
|
518 |
|
519 return TRUE; |
|
520 } |
|
521 |
|
522 /** |
|
523 * Disables a given set of modules. |
|
524 * |
|
525 * @param string[] $module_list |
|
526 * An array of module names. |
|
527 * @param bool $disable_dependents |
|
528 * If TRUE, dependent modules will automatically be added and disabled in the |
|
529 * correct order. This incurs a significant performance cost, so use FALSE |
|
530 * if you know $module_list is already complete and in the correct order. |
|
531 * |
|
532 * @see drupal_uninstall_modules() |
|
533 * @see module_enable() |
|
534 */ |
|
535 function module_disable($module_list, $disable_dependents = TRUE) { |
|
536 if ($disable_dependents) { |
|
537 // Get all module data so we can find dependents and sort. |
|
538 $module_data = system_rebuild_module_data(); |
|
539 // Create an associative array with weights as values. |
|
540 $module_list = array_flip(array_values($module_list)); |
|
541 |
|
542 $profile = drupal_get_profile(); |
|
543 while (list($module) = each($module_list)) { |
|
544 if (!isset($module_data[$module]) || !$module_data[$module]->status) { |
|
545 // This module doesn't exist or is already disabled, skip it. |
|
546 unset($module_list[$module]); |
|
547 continue; |
|
548 } |
|
549 $module_list[$module] = $module_data[$module]->sort; |
|
550 |
|
551 // Add dependent modules to the list, with a placeholder weight. |
|
552 // The new modules will be processed as the while loop continues. |
|
553 foreach ($module_data[$module]->required_by as $dependent => $dependent_data) { |
|
554 if (!isset($module_list[$dependent]) && $dependent != $profile) { |
|
555 $module_list[$dependent] = 0; |
|
556 } |
|
557 } |
|
558 } |
|
559 |
|
560 // Sort the module list by pre-calculated weights. |
|
561 asort($module_list); |
|
562 $module_list = array_keys($module_list); |
|
563 } |
|
564 |
|
565 $invoke_modules = array(); |
|
566 |
|
567 foreach ($module_list as $module) { |
|
568 if (module_exists($module)) { |
|
569 // Check if node_access table needs rebuilding. |
|
570 if (!node_access_needs_rebuild() && module_hook($module, 'node_grants')) { |
|
571 node_access_needs_rebuild(TRUE); |
|
572 } |
|
573 |
|
574 module_load_install($module); |
|
575 module_invoke($module, 'disable'); |
|
576 db_update('system') |
|
577 ->fields(array('status' => 0)) |
|
578 ->condition('type', 'module') |
|
579 ->condition('name', $module) |
|
580 ->execute(); |
|
581 $invoke_modules[] = $module; |
|
582 watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO); |
|
583 } |
|
584 } |
|
585 |
|
586 if (!empty($invoke_modules)) { |
|
587 // Refresh the module list to exclude the disabled modules. |
|
588 system_list_reset(); |
|
589 module_list(TRUE); |
|
590 module_implements('', FALSE, TRUE); |
|
591 entity_info_cache_clear(); |
|
592 // Invoke hook_modules_disabled before disabling modules, |
|
593 // so we can still call module hooks to get information. |
|
594 module_invoke_all('modules_disabled', $invoke_modules); |
|
595 // Update the registry to remove the newly-disabled module. |
|
596 registry_update(); |
|
597 _system_update_bootstrap_status(); |
|
598 // Update the theme registry to remove the newly-disabled module. |
|
599 drupal_theme_rebuild(); |
|
600 } |
|
601 |
|
602 // If there remains no more node_access module, rebuilding will be |
|
603 // straightforward, we can do it right now. |
|
604 if (node_access_needs_rebuild() && count(module_implements('node_grants')) == 0) { |
|
605 node_access_rebuild(); |
|
606 } |
|
607 } |
|
608 |
|
609 /** |
|
610 * @defgroup hooks Hooks |
|
611 * @{ |
|
612 * Allow modules to interact with the Drupal core. |
|
613 * |
|
614 * Drupal's module system is based on the concept of "hooks". A hook is a PHP |
|
615 * function that is named foo_bar(), where "foo" is the name of the module |
|
616 * (whose filename is thus foo.module) and "bar" is the name of the hook. Each |
|
617 * hook has a defined set of parameters and a specified result type. |
|
618 * |
|
619 * To extend Drupal, a module need simply implement a hook. When Drupal wishes |
|
620 * to allow intervention from modules, it determines which modules implement a |
|
621 * hook and calls that hook in all enabled modules that implement it. |
|
622 * |
|
623 * The available hooks to implement are explained here in the Hooks section of |
|
624 * the developer documentation. The string "hook" is used as a placeholder for |
|
625 * the module name in the hook definitions. For example, if the module file is |
|
626 * called example.module, then hook_help() as implemented by that module would |
|
627 * be defined as example_help(). |
|
628 * |
|
629 * The example functions included are not part of the Drupal core, they are |
|
630 * just models that you can modify. Only the hooks implemented within modules |
|
631 * are executed when running Drupal. |
|
632 * |
|
633 * @see themeable |
|
634 * @see callbacks |
|
635 */ |
|
636 |
|
637 /** |
|
638 * @defgroup callbacks Callbacks |
|
639 * @{ |
|
640 * Callback function signatures. |
|
641 * |
|
642 * Drupal's API sometimes uses callback functions to allow you to define how |
|
643 * some type of processing happens. A callback is a function with a defined |
|
644 * signature, which you define in a module. Then you pass the function name as |
|
645 * a parameter to a Drupal API function or return it as part of a hook |
|
646 * implementation return value, and your function is called at an appropriate |
|
647 * time. For instance, when setting up batch processing you might need to |
|
648 * provide a callback function for each processing step and/or a callback for |
|
649 * when processing is finished; you would do that by defining these functions |
|
650 * and passing their names into the batch setup function. |
|
651 * |
|
652 * Callback function signatures, like hook definitions, are described by |
|
653 * creating and documenting dummy functions in a *.api.php file; normally, the |
|
654 * dummy callback function's name should start with "callback_", and you should |
|
655 * document the parameters and return value and provide a sample function body. |
|
656 * Then your API documentation can refer to this callback function in its |
|
657 * documentation. A user of your API can usually name their callback function |
|
658 * anything they want, although a standard name would be to replace "callback_" |
|
659 * with the module name. |
|
660 * |
|
661 * @see hooks |
|
662 * @see themeable |
|
663 * |
|
664 * @} |
|
665 */ |
|
666 |
|
667 /** |
|
668 * Determines whether a module implements a hook. |
|
669 * |
|
670 * @param $module |
|
671 * The name of the module (without the .module extension). |
|
672 * @param $hook |
|
673 * The name of the hook (e.g. "help" or "menu"). |
|
674 * |
|
675 * @return |
|
676 * TRUE if the module is both installed and enabled, and the hook is |
|
677 * implemented in that module. |
|
678 */ |
|
679 function module_hook($module, $hook) { |
|
680 $function = $module . '_' . $hook; |
|
681 if (function_exists($function)) { |
|
682 return TRUE; |
|
683 } |
|
684 // If the hook implementation does not exist, check whether it may live in an |
|
685 // optional include file registered via hook_hook_info(). |
|
686 $hook_info = module_hook_info(); |
|
687 if (isset($hook_info[$hook]['group'])) { |
|
688 module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']); |
|
689 if (function_exists($function)) { |
|
690 return TRUE; |
|
691 } |
|
692 } |
|
693 return FALSE; |
|
694 } |
|
695 |
|
696 /** |
|
697 * Determines which modules are implementing a hook. |
|
698 * |
|
699 * Lazy-loaded include files specified with "group" via hook_hook_info() or |
|
700 * hook_module_implements_alter() will be automatically included by this |
|
701 * function when necessary. |
|
702 * |
|
703 * @param string $hook |
|
704 * The name of the hook (e.g. "help" or "menu"). |
|
705 * @param bool $sort |
|
706 * By default, modules are ordered by weight and filename, settings this option |
|
707 * to TRUE, module list will be ordered by module name. |
|
708 * @param bool $reset |
|
709 * For internal use only: Whether to force the stored list of hook |
|
710 * implementations to be regenerated (such as after enabling a new module, |
|
711 * before processing hook_enable). |
|
712 * |
|
713 * @return |
|
714 * An array with the names of the modules which are implementing this hook. |
|
715 * |
|
716 * @see module_implements_write_cache() |
|
717 */ |
|
718 function module_implements($hook, $sort = FALSE, $reset = FALSE) { |
|
719 // Use the advanced drupal_static() pattern, since this is called very often. |
|
720 static $drupal_static_fast; |
|
721 if (!isset($drupal_static_fast)) { |
|
722 $drupal_static_fast['implementations'] = &drupal_static(__FUNCTION__); |
|
723 $drupal_static_fast['verified'] = &drupal_static(__FUNCTION__ . ':verified'); |
|
724 } |
|
725 $implementations = &$drupal_static_fast['implementations']; |
|
726 $verified = &$drupal_static_fast['verified']; |
|
727 |
|
728 // We maintain a persistent cache of hook implementations in addition to the |
|
729 // static cache to avoid looping through every module and every hook on each |
|
730 // request. Benchmarks show that the benefit of this caching outweighs the |
|
731 // additional database hit even when using the default database caching |
|
732 // backend and only a small number of modules are enabled. The cost of the |
|
733 // cache_get() is more or less constant and reduced further when non-database |
|
734 // caching backends are used, so there will be more significant gains when a |
|
735 // large number of modules are installed or hooks invoked, since this can |
|
736 // quickly lead to module_hook() being called several thousand times |
|
737 // per request. |
|
738 if ($reset) { |
|
739 $implementations = array(); |
|
740 $verified = array(); |
|
741 cache_set('module_implements', array(), 'cache_bootstrap'); |
|
742 drupal_static_reset('module_hook_info'); |
|
743 drupal_static_reset('drupal_alter'); |
|
744 cache_clear_all('hook_info', 'cache_bootstrap'); |
|
745 cache_clear_all('system_cache_tables', 'cache'); |
|
746 return; |
|
747 } |
|
748 |
|
749 // Fetch implementations from cache. |
|
750 // This happens on the first call to module_implements(*, *, FALSE) during a |
|
751 // request, but also when $implementations have been reset, e.g. after |
|
752 // module_enable(). |
|
753 if (empty($implementations)) { |
|
754 $implementations = cache_get('module_implements', 'cache_bootstrap'); |
|
755 if ($implementations === FALSE) { |
|
756 $implementations = array(); |
|
757 } |
|
758 else { |
|
759 $implementations = $implementations->data; |
|
760 } |
|
761 // Forget all previously "verified" hooks, in case that $implementations |
|
762 // were cleared via drupal_static_reset('module_implements') instead of |
|
763 // module_implements(*, *, TRUE). |
|
764 $verified = array(); |
|
765 } |
|
766 |
|
767 if (!isset($implementations[$hook])) { |
|
768 // The hook is not cached, so ensure that whether or not it has |
|
769 // implementations, that the cache is updated at the end of the request. |
|
770 $implementations['#write_cache'] = TRUE; |
|
771 // Discover implementations for this hook. |
|
772 $hook_info = module_hook_info(); |
|
773 $implementations[$hook] = array(); |
|
774 $list = module_list(FALSE, FALSE, $sort); |
|
775 foreach ($list as $module) { |
|
776 $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']); |
|
777 // Since module_hook() may needlessly try to load the include file again, |
|
778 // function_exists() is used directly here. |
|
779 if (function_exists($module . '_' . $hook)) { |
|
780 $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE; |
|
781 } |
|
782 } |
|
783 // Allow modules to change the weight of specific implementations, but avoid |
|
784 // an infinite loop. |
|
785 if ($hook != 'module_implements_alter') { |
|
786 // Remember the implementations before hook_module_implements_alter(). |
|
787 $implementations_before = $implementations[$hook]; |
|
788 drupal_alter('module_implements', $implementations[$hook], $hook); |
|
789 // Verify implementations that were added or modified. |
|
790 foreach (array_diff_assoc($implementations[$hook], $implementations_before) as $module => $group) { |
|
791 // If drupal_alter('module_implements') changed or added a $group, the |
|
792 // respective file needs to be included. |
|
793 if ($group) { |
|
794 module_load_include('inc', $module, "$module.$group"); |
|
795 } |
|
796 // If a new implementation was added, verify that the function exists. |
|
797 if (!function_exists($module . '_' . $hook)) { |
|
798 unset($implementations[$hook][$module]); |
|
799 } |
|
800 } |
|
801 } |
|
802 // Implementations for this hook are now "verified". |
|
803 $verified[$hook] = TRUE; |
|
804 } |
|
805 elseif (!isset($verified[$hook])) { |
|
806 // Implementations for this hook were in the cache, but they are not |
|
807 // "verified" yet. |
|
808 foreach ($implementations[$hook] as $module => $group) { |
|
809 // If this hook implementation is stored in a lazy-loaded file, so include |
|
810 // that file first. |
|
811 if ($group) { |
|
812 module_load_include('inc', $module, "$module.$group"); |
|
813 } |
|
814 // It is possible that a module removed a hook implementation without the |
|
815 // implementations cache being rebuilt yet, so we check whether the |
|
816 // function exists on each request to avoid undefined function errors. |
|
817 // Since module_hook() may needlessly try to load the include file again, |
|
818 // function_exists() is used directly here. |
|
819 if (!function_exists($module . '_' . $hook)) { |
|
820 // Clear out the stale implementation from the cache and force a cache |
|
821 // refresh to forget about no longer existing hook implementations. |
|
822 unset($implementations[$hook][$module]); |
|
823 $implementations['#write_cache'] = TRUE; |
|
824 } |
|
825 } |
|
826 $verified[$hook] = TRUE; |
|
827 } |
|
828 |
|
829 return array_keys($implementations[$hook]); |
|
830 } |
|
831 |
|
832 /** |
|
833 * Retrieves a list of hooks that are declared through hook_hook_info(). |
|
834 * |
|
835 * @return |
|
836 * An associative array whose keys are hook names and whose values are an |
|
837 * associative array containing a group name. The structure of the array |
|
838 * is the same as the return value of hook_hook_info(). |
|
839 * |
|
840 * @see hook_hook_info() |
|
841 */ |
|
842 function module_hook_info() { |
|
843 // This function is indirectly invoked from bootstrap_invoke_all(), in which |
|
844 // case common.inc, subsystems, and modules are not loaded yet, so it does not |
|
845 // make sense to support hook groups resp. lazy-loaded include files prior to |
|
846 // full bootstrap. |
|
847 if (drupal_bootstrap(NULL, FALSE) != DRUPAL_BOOTSTRAP_FULL) { |
|
848 return array(); |
|
849 } |
|
850 $hook_info = &drupal_static(__FUNCTION__); |
|
851 |
|
852 if (!isset($hook_info)) { |
|
853 $hook_info = array(); |
|
854 $cache = cache_get('hook_info', 'cache_bootstrap'); |
|
855 if ($cache === FALSE) { |
|
856 // Rebuild the cache and save it. |
|
857 // We can't use module_invoke_all() here or it would cause an infinite |
|
858 // loop. |
|
859 foreach (module_list() as $module) { |
|
860 $function = $module . '_hook_info'; |
|
861 if (function_exists($function)) { |
|
862 $result = $function(); |
|
863 if (isset($result) && is_array($result)) { |
|
864 $hook_info = array_merge_recursive($hook_info, $result); |
|
865 } |
|
866 } |
|
867 } |
|
868 // We can't use drupal_alter() for the same reason as above. |
|
869 foreach (module_list() as $module) { |
|
870 $function = $module . '_hook_info_alter'; |
|
871 if (function_exists($function)) { |
|
872 $function($hook_info); |
|
873 } |
|
874 } |
|
875 cache_set('hook_info', $hook_info, 'cache_bootstrap'); |
|
876 } |
|
877 else { |
|
878 $hook_info = $cache->data; |
|
879 } |
|
880 } |
|
881 |
|
882 return $hook_info; |
|
883 } |
|
884 |
|
885 /** |
|
886 * Writes the hook implementation cache. |
|
887 * |
|
888 * @see module_implements() |
|
889 */ |
|
890 function module_implements_write_cache() { |
|
891 // The list of implementations includes vital modules only before full |
|
892 // bootstrap, so do not write cache if we are not fully bootstrapped yet. |
|
893 if (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) { |
|
894 return; |
|
895 } |
|
896 $implementations = &drupal_static('module_implements'); |
|
897 if (isset($implementations['#write_cache'])) { |
|
898 unset($implementations['#write_cache']); |
|
899 cache_set('module_implements', $implementations, 'cache_bootstrap'); |
|
900 } |
|
901 } |
|
902 |
|
903 /** |
|
904 * Invokes a hook in a particular module. |
|
905 * |
|
906 * All arguments are passed by value. Use drupal_alter() if you need to pass |
|
907 * arguments by reference. |
|
908 * |
|
909 * @param $module |
|
910 * The name of the module (without the .module extension). |
|
911 * @param $hook |
|
912 * The name of the hook to invoke. |
|
913 * @param ... |
|
914 * Arguments to pass to the hook implementation. |
|
915 * |
|
916 * @return |
|
917 * The return value of the hook implementation. |
|
918 * |
|
919 * @see drupal_alter() |
|
920 */ |
|
921 function module_invoke($module, $hook) { |
|
922 $args = func_get_args(); |
|
923 // Remove $module and $hook from the arguments. |
|
924 unset($args[0], $args[1]); |
|
925 if (module_hook($module, $hook)) { |
|
926 return call_user_func_array($module . '_' . $hook, $args); |
|
927 } |
|
928 } |
|
929 |
|
930 /** |
|
931 * Invokes a hook in all enabled modules that implement it. |
|
932 * |
|
933 * All arguments are passed by value. Use drupal_alter() if you need to pass |
|
934 * arguments by reference. |
|
935 * |
|
936 * @param $hook |
|
937 * The name of the hook to invoke. |
|
938 * @param ... |
|
939 * Arguments to pass to the hook. |
|
940 * |
|
941 * @return |
|
942 * An array of return values of the hook implementations. If modules return |
|
943 * arrays from their implementations, those are merged into one array |
|
944 * recursively. Note: integer keys in arrays will be lost, as the merge is |
|
945 * done using array_merge_recursive(). |
|
946 * |
|
947 * @see drupal_alter() |
|
948 */ |
|
949 function module_invoke_all($hook) { |
|
950 $args = func_get_args(); |
|
951 // Remove $hook from the arguments. |
|
952 unset($args[0]); |
|
953 $return = array(); |
|
954 foreach (module_implements($hook) as $module) { |
|
955 $function = $module . '_' . $hook; |
|
956 if (function_exists($function)) { |
|
957 $result = call_user_func_array($function, $args); |
|
958 if (isset($result) && is_array($result)) { |
|
959 $return = array_merge_recursive($return, $result); |
|
960 } |
|
961 elseif (isset($result)) { |
|
962 $return[] = $result; |
|
963 } |
|
964 } |
|
965 } |
|
966 |
|
967 return $return; |
|
968 } |
|
969 |
|
970 /** |
|
971 * @} End of "defgroup hooks". |
|
972 */ |
|
973 |
|
974 /** |
|
975 * Returns an array of modules required by core. |
|
976 */ |
|
977 function drupal_required_modules() { |
|
978 $files = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'modules', 'name', 0); |
|
979 $required = array(); |
|
980 |
|
981 // An installation profile is required and one must always be loaded. |
|
982 $required[] = drupal_get_profile(); |
|
983 |
|
984 foreach ($files as $name => $file) { |
|
985 $info = drupal_parse_info_file($file->uri); |
|
986 if (!empty($info) && !empty($info['required']) && $info['required']) { |
|
987 $required[] = $name; |
|
988 } |
|
989 } |
|
990 |
|
991 return $required; |
|
992 } |
|
993 |
|
994 /** |
|
995 * Passes alterable variables to specific hook_TYPE_alter() implementations. |
|
996 * |
|
997 * This dispatch function hands off the passed-in variables to type-specific |
|
998 * hook_TYPE_alter() implementations in modules. It ensures a consistent |
|
999 * interface for all altering operations. |
|
1000 * |
|
1001 * A maximum of 2 alterable arguments is supported (a third is supported for |
|
1002 * legacy reasons, but should not be used in new code). In case more arguments |
|
1003 * need to be passed and alterable, modules provide additional variables |
|
1004 * assigned by reference in the last $context argument: |
|
1005 * @code |
|
1006 * $context = array( |
|
1007 * 'alterable' => &$alterable, |
|
1008 * 'unalterable' => $unalterable, |
|
1009 * 'foo' => 'bar', |
|
1010 * ); |
|
1011 * drupal_alter('mymodule_data', $alterable1, $alterable2, $context); |
|
1012 * @endcode |
|
1013 * |
|
1014 * Note that objects are always passed by reference in PHP5. If it is absolutely |
|
1015 * required that no implementation alters a passed object in $context, then an |
|
1016 * object needs to be cloned: |
|
1017 * @code |
|
1018 * $context = array( |
|
1019 * 'unalterable_object' => clone $object, |
|
1020 * ); |
|
1021 * drupal_alter('mymodule_data', $data, $context); |
|
1022 * @endcode |
|
1023 * |
|
1024 * @param $type |
|
1025 * A string describing the type of the alterable $data. 'form', 'links', |
|
1026 * 'node_content', and so on are several examples. Alternatively can be an |
|
1027 * array, in which case hook_TYPE_alter() is invoked for each value in the |
|
1028 * array, ordered first by module, and then for each module, in the order of |
|
1029 * values in $type. For example, when Form API is using drupal_alter() to |
|
1030 * execute both hook_form_alter() and hook_form_FORM_ID_alter() |
|
1031 * implementations, it passes array('form', 'form_' . $form_id) for $type. |
|
1032 * @param $data |
|
1033 * The variable that will be passed to hook_TYPE_alter() implementations to be |
|
1034 * altered. The type of this variable depends on the value of the $type |
|
1035 * argument. For example, when altering a 'form', $data will be a structured |
|
1036 * array. When altering a 'profile', $data will be an object. |
|
1037 * @param $context1 |
|
1038 * (optional) An additional variable that is passed by reference. |
|
1039 * @param $context2 |
|
1040 * (optional) An additional variable that is passed by reference. If more |
|
1041 * context needs to be provided to implementations, then this should be an |
|
1042 * associative array as described above. |
|
1043 * @param $context3 |
|
1044 * (optional) An additional variable that is passed by reference. This |
|
1045 * parameter is deprecated and will not exist in Drupal 8; consequently, it |
|
1046 * should not be used for new Drupal 7 code either. It is here only for |
|
1047 * backwards compatibility with older code that passed additional arguments |
|
1048 * to drupal_alter(). |
|
1049 */ |
|
1050 function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL, &$context3 = NULL) { |
|
1051 // Use the advanced drupal_static() pattern, since this is called very often. |
|
1052 static $drupal_static_fast; |
|
1053 if (!isset($drupal_static_fast)) { |
|
1054 $drupal_static_fast['functions'] = &drupal_static(__FUNCTION__); |
|
1055 } |
|
1056 $functions = &$drupal_static_fast['functions']; |
|
1057 |
|
1058 // Most of the time, $type is passed as a string, so for performance, |
|
1059 // normalize it to that. When passed as an array, usually the first item in |
|
1060 // the array is a generic type, and additional items in the array are more |
|
1061 // specific variants of it, as in the case of array('form', 'form_FORM_ID'). |
|
1062 if (is_array($type)) { |
|
1063 $cid = implode(',', $type); |
|
1064 $extra_types = $type; |
|
1065 $type = array_shift($extra_types); |
|
1066 // Allow if statements in this function to use the faster isset() rather |
|
1067 // than !empty() both when $type is passed as a string, or as an array with |
|
1068 // one item. |
|
1069 if (empty($extra_types)) { |
|
1070 unset($extra_types); |
|
1071 } |
|
1072 } |
|
1073 else { |
|
1074 $cid = $type; |
|
1075 } |
|
1076 |
|
1077 // Some alter hooks are invoked many times per page request, so statically |
|
1078 // cache the list of functions to call, and on subsequent calls, iterate |
|
1079 // through them quickly. |
|
1080 if (!isset($functions[$cid])) { |
|
1081 $functions[$cid] = array(); |
|
1082 $hook = $type . '_alter'; |
|
1083 $modules = module_implements($hook); |
|
1084 if (!isset($extra_types)) { |
|
1085 // For the more common case of a single hook, we do not need to call |
|
1086 // function_exists(), since module_implements() returns only modules with |
|
1087 // implementations. |
|
1088 foreach ($modules as $module) { |
|
1089 $functions[$cid][] = $module . '_' . $hook; |
|
1090 } |
|
1091 } |
|
1092 else { |
|
1093 // For multiple hooks, we need $modules to contain every module that |
|
1094 // implements at least one of them. |
|
1095 $extra_modules = array(); |
|
1096 foreach ($extra_types as $extra_type) { |
|
1097 $extra_modules = array_merge($extra_modules, module_implements($extra_type . '_alter')); |
|
1098 } |
|
1099 // If any modules implement one of the extra hooks that do not implement |
|
1100 // the primary hook, we need to add them to the $modules array in their |
|
1101 // appropriate order. module_implements() can only return ordered |
|
1102 // implementations of a single hook. To get the ordered implementations |
|
1103 // of multiple hooks, we mimic the module_implements() logic of first |
|
1104 // ordering by module_list(), and then calling |
|
1105 // drupal_alter('module_implements'). |
|
1106 if (array_diff($extra_modules, $modules)) { |
|
1107 // Merge the arrays and order by module_list(). |
|
1108 $modules = array_intersect(module_list(), array_merge($modules, $extra_modules)); |
|
1109 // Since module_implements() already took care of loading the necessary |
|
1110 // include files, we can safely pass FALSE for the array values. |
|
1111 $implementations = array_fill_keys($modules, FALSE); |
|
1112 // Let modules adjust the order solely based on the primary hook. This |
|
1113 // ensures the same module order regardless of whether this if block |
|
1114 // runs. Calling drupal_alter() recursively in this way does not result |
|
1115 // in an infinite loop, because this call is for a single $type, so we |
|
1116 // won't end up in this code block again. |
|
1117 drupal_alter('module_implements', $implementations, $hook); |
|
1118 $modules = array_keys($implementations); |
|
1119 } |
|
1120 foreach ($modules as $module) { |
|
1121 // Since $modules is a merged array, for any given module, we do not |
|
1122 // know whether it has any particular implementation, so we need a |
|
1123 // function_exists(). |
|
1124 $function = $module . '_' . $hook; |
|
1125 if (function_exists($function)) { |
|
1126 $functions[$cid][] = $function; |
|
1127 } |
|
1128 foreach ($extra_types as $extra_type) { |
|
1129 $function = $module . '_' . $extra_type . '_alter'; |
|
1130 if (function_exists($function)) { |
|
1131 $functions[$cid][] = $function; |
|
1132 } |
|
1133 } |
|
1134 } |
|
1135 } |
|
1136 // Allow the theme to alter variables after the theme system has been |
|
1137 // initialized. |
|
1138 global $theme, $base_theme_info; |
|
1139 if (isset($theme)) { |
|
1140 $theme_keys = array(); |
|
1141 foreach ($base_theme_info as $base) { |
|
1142 $theme_keys[] = $base->name; |
|
1143 } |
|
1144 $theme_keys[] = $theme; |
|
1145 foreach ($theme_keys as $theme_key) { |
|
1146 $function = $theme_key . '_' . $hook; |
|
1147 if (function_exists($function)) { |
|
1148 $functions[$cid][] = $function; |
|
1149 } |
|
1150 if (isset($extra_types)) { |
|
1151 foreach ($extra_types as $extra_type) { |
|
1152 $function = $theme_key . '_' . $extra_type . '_alter'; |
|
1153 if (function_exists($function)) { |
|
1154 $functions[$cid][] = $function; |
|
1155 } |
|
1156 } |
|
1157 } |
|
1158 } |
|
1159 } |
|
1160 } |
|
1161 |
|
1162 foreach ($functions[$cid] as $function) { |
|
1163 $function($data, $context1, $context2, $context3); |
|
1164 } |
|
1165 } |