| 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 } |