webmaster@1: $module) { webmaster@1: drupal_get_filename('module', $name, $module['filename']); webmaster@1: $list[$name] = $name; webmaster@1: } webmaster@1: } webmaster@1: else { webmaster@1: if ($bootstrap) { webmaster@1: $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: } webmaster@1: else { webmaster@1: $result = db_query("SELECT name, filename, throttle FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC"); webmaster@1: } webmaster@1: while ($module = db_fetch_object($result)) { webmaster@1: if (file_exists($module->filename)) { webmaster@1: // Determine the current throttle status and see if the module should be webmaster@1: // loaded based on server load. We have to directly access the throttle webmaster@1: // variables, since throttle.module may not be loaded yet. webmaster@1: $throttle = ($module->throttle && variable_get('throttle_level', 0) > 0); webmaster@1: if (!$throttle) { webmaster@1: drupal_get_filename('module', $module->name, $module->filename); webmaster@1: $list[$module->name] = $module->name; webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: if ($sort) { webmaster@1: if (!isset($sorted_list)) { webmaster@1: $sorted_list = $list; webmaster@1: ksort($sorted_list); webmaster@1: } webmaster@1: return $sorted_list; webmaster@1: } webmaster@1: return $list; webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Rebuild the database cache of module files. webmaster@1: * webmaster@1: * @return webmaster@1: * The array of filesystem objects used to rebuild the cache. webmaster@1: */ webmaster@1: function module_rebuild_cache() { webmaster@1: // Get current list of modules webmaster@1: $files = drupal_system_listing('\.module$', 'modules', 'name', 0); webmaster@1: webmaster@1: // Extract current files from database. webmaster@1: system_get_files_database($files, 'module'); webmaster@1: webmaster@1: ksort($files); webmaster@1: webmaster@1: // Set defaults for module info webmaster@1: $defaults = array( webmaster@1: 'dependencies' => array(), webmaster@1: 'dependents' => array(), webmaster@1: 'description' => '', webmaster@1: 'version' => NULL, webmaster@1: 'php' => DRUPAL_MINIMUM_PHP, webmaster@1: ); webmaster@1: webmaster@1: foreach ($files as $filename => $file) { webmaster@1: // Look for the info file. webmaster@1: $file->info = drupal_parse_info_file(dirname($file->filename) .'/'. $file->name .'.info'); webmaster@1: webmaster@1: // Skip modules that don't provide info. webmaster@1: if (empty($file->info)) { webmaster@1: unset($files[$filename]); webmaster@1: continue; webmaster@1: } webmaster@1: // Merge in defaults and save. webmaster@1: $files[$filename]->info = $file->info + $defaults; webmaster@1: webmaster@1: // Invoke hook_system_info_alter() to give installed modules a chance to webmaster@1: // modify the data in the .info files if necessary. webmaster@1: drupal_alter('system_info', $files[$filename]->info, $files[$filename]); webmaster@1: webmaster@1: // Log the critical hooks implemented by this module. webmaster@1: $bootstrap = 0; webmaster@1: foreach (bootstrap_hooks() as $hook) { webmaster@1: if (module_hook($file->name, $hook)) { webmaster@1: $bootstrap = 1; webmaster@1: break; webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: // Update the contents of the system table: webmaster@1: if (isset($file->status) || (isset($file->old_filename) && $file->old_filename != $file->filename)) { webmaster@1: 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: } webmaster@1: else { webmaster@1: // This is a new module. webmaster@1: $files[$filename]->status = 0; webmaster@1: $files[$filename]->throttle = 0; webmaster@1: 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: } webmaster@1: } webmaster@1: $files = _module_build_dependencies($files); webmaster@1: return $files; webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Find dependencies any level deep and fill in dependents information too. webmaster@1: * webmaster@1: * If module A depends on B which in turn depends on C then this function will webmaster@1: * add C to the list of modules A depends on. This will be repeated until webmaster@1: * module A has a list of all modules it depends on. If it depends on itself, webmaster@1: * called a circular dependency, that's marked by adding a nonexistent module, webmaster@1: * called -circular- to this list of modules. Because this does not exist, webmaster@1: * it'll be impossible to switch module A on. webmaster@1: * webmaster@1: * Also we fill in a dependents array in $file->info. Using the names above, webmaster@1: * the dependents array of module B lists A. webmaster@1: * webmaster@1: * @param $files webmaster@1: * The array of filesystem objects used to rebuild the cache. webmaster@1: * @return webmaster@1: * The same array with dependencies and dependents added where applicable. webmaster@1: */ webmaster@1: function _module_build_dependencies($files) { webmaster@1: do { webmaster@1: $new_dependency = FALSE; webmaster@1: foreach ($files as $filename => $file) { webmaster@1: // We will modify this object (module A, see doxygen for module A, B, C). webmaster@1: $file = &$files[$filename]; webmaster@1: if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) { webmaster@1: foreach ($file->info['dependencies'] as $dependency_name) { webmaster@1: // This is a nonexistent module. webmaster@1: if ($dependency_name == '-circular-' || !isset($files[$dependency_name])) { webmaster@1: continue; webmaster@1: } webmaster@1: // $dependency_name is module B (again, see doxygen). webmaster@1: $files[$dependency_name]->info['dependents'][$filename] = $filename; webmaster@1: $dependency = $files[$dependency_name]; webmaster@1: if (isset($dependency->info['dependencies']) && is_array($dependency->info['dependencies'])) { webmaster@1: // Let's find possible C modules. webmaster@1: foreach ($dependency->info['dependencies'] as $candidate) { webmaster@1: if (array_search($candidate, $file->info['dependencies']) === FALSE) { webmaster@1: // Is this a circular dependency? webmaster@1: if ($candidate == $filename) { webmaster@1: // As a module name can not contain dashes, this makes webmaster@1: // impossible to switch on the module. webmaster@1: $candidate = '-circular-'; webmaster@1: // Do not display the message or add -circular- more than once. webmaster@1: if (array_search($candidate, $file->info['dependencies']) !== FALSE) { webmaster@1: continue; webmaster@1: } webmaster@1: 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: } webmaster@1: else { webmaster@1: // We added a new dependency to module A. The next loop will webmaster@1: // be able to use this as "B module" thus finding even webmaster@1: // deeper dependencies. webmaster@1: $new_dependency = TRUE; webmaster@1: } webmaster@1: $file->info['dependencies'][] = $candidate; webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: // Don't forget to break the reference. webmaster@1: unset($file); webmaster@1: } webmaster@1: } while ($new_dependency); webmaster@1: return $files; webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Determine whether a given module exists. webmaster@1: * webmaster@1: * @param $module webmaster@1: * The name of the module (without the .module extension). webmaster@1: * @return webmaster@1: * TRUE if the module is both installed and enabled. webmaster@1: */ webmaster@1: function module_exists($module) { webmaster@1: $list = module_list(); webmaster@1: return array_key_exists($module, $list); webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Load a module's installation hooks. webmaster@1: */ webmaster@1: function module_load_install($module) { webmaster@1: // Make sure the installation API is available webmaster@1: include_once './includes/install.inc'; webmaster@1: webmaster@1: module_load_include('install', $module); webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Load a module include file. webmaster@1: * webmaster@1: * @param $type webmaster@1: * The include file's type (file extension). webmaster@1: * @param $module webmaster@1: * The module to which the include file belongs. webmaster@1: * @param $name webmaster@1: * Optionally, specify the file name. If not set, the module's name is used. webmaster@1: */ webmaster@1: function module_load_include($type, $module, $name = NULL) { webmaster@1: if (empty($name)) { webmaster@1: $name = $module; webmaster@1: } webmaster@1: webmaster@1: $file = './'. drupal_get_path('module', $module) ."/$name.$type"; webmaster@1: webmaster@1: if (is_file($file)) { webmaster@1: require_once $file; webmaster@1: } webmaster@1: else { webmaster@1: return FALSE; webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Load an include file for each of the modules that have been enabled in webmaster@1: * the system table. webmaster@1: */ webmaster@1: function module_load_all_includes($type, $name = NULL) { webmaster@1: $modules = module_list(); webmaster@1: foreach ($modules as $module) { webmaster@1: module_load_include($type, $module, $name); webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Enable a given list of modules. webmaster@1: * webmaster@1: * @param $module_list webmaster@1: * An array of module names. webmaster@1: */ webmaster@1: function module_enable($module_list) { webmaster@1: $invoke_modules = array(); webmaster@1: foreach ($module_list as $module) { webmaster@1: $existing = db_fetch_object(db_query("SELECT status FROM {system} WHERE type = '%s' AND name = '%s'", 'module', $module)); webmaster@1: if ($existing->status == 0) { webmaster@1: module_load_install($module); webmaster@1: db_query("UPDATE {system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s'", 1, 0, 'module', $module); webmaster@1: drupal_load('module', $module); webmaster@1: $invoke_modules[] = $module; webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: if (!empty($invoke_modules)) { webmaster@1: // Refresh the module list to include the new enabled module. webmaster@1: module_list(TRUE, FALSE); webmaster@1: // Force to regenerate the stored list of hook implementations. webmaster@1: module_implements('', FALSE, TRUE); webmaster@1: } webmaster@1: webmaster@1: foreach ($invoke_modules as $module) { webmaster@1: module_invoke($module, 'enable'); webmaster@1: // Check if node_access table needs rebuilding. webmaster@1: // We check for the existence of node_access_needs_rebuild() since webmaster@1: // at install time, module_enable() could be called while node.module webmaster@1: // is not enabled yet. webmaster@1: if (function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) { webmaster@1: node_access_needs_rebuild(TRUE); webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Disable a given set of modules. webmaster@1: * webmaster@1: * @param $module_list webmaster@1: * An array of module names. webmaster@1: */ webmaster@1: function module_disable($module_list) { webmaster@1: $invoke_modules = array(); webmaster@1: foreach ($module_list as $module) { webmaster@1: if (module_exists($module)) { webmaster@1: // Check if node_access table needs rebuilding. webmaster@1: if (!node_access_needs_rebuild() && module_hook($module, 'node_grants')) { webmaster@1: node_access_needs_rebuild(TRUE); webmaster@1: } webmaster@1: webmaster@1: module_load_install($module); webmaster@1: module_invoke($module, 'disable'); webmaster@1: db_query("UPDATE {system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s'", 0, 0, 'module', $module); webmaster@1: $invoke_modules[] = $module; webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: if (!empty($invoke_modules)) { webmaster@1: // Refresh the module list to exclude the disabled modules. webmaster@1: module_list(TRUE, FALSE); webmaster@1: // Force to regenerate the stored list of hook implementations. webmaster@1: module_implements('', FALSE, TRUE); webmaster@1: } webmaster@1: webmaster@1: // If there remains no more node_access module, rebuilding will be webmaster@1: // straightforward, we can do it right now. webmaster@1: if (node_access_needs_rebuild() && count(module_implements('node_grants')) == 0) { webmaster@1: node_access_rebuild(); webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * @defgroup hooks Hooks webmaster@1: * @{ webmaster@1: * Allow modules to interact with the Drupal core. webmaster@1: * webmaster@1: * Drupal's module system is based on the concept of "hooks". A hook is a PHP webmaster@1: * function that is named foo_bar(), where "foo" is the name of the module (whose webmaster@1: * filename is thus foo.module) and "bar" is the name of the hook. Each hook has webmaster@1: * a defined set of parameters and a specified result type. webmaster@1: * webmaster@1: * To extend Drupal, a module need simply implement a hook. When Drupal wishes to webmaster@1: * allow intervention from modules, it determines which modules implement a hook webmaster@1: * and call that hook in all enabled modules that implement it. webmaster@1: * webmaster@1: * The available hooks to implement are explained here in the Hooks section of webmaster@1: * the developer documentation. The string "hook" is used as a placeholder for webmaster@1: * the module name is the hook definitions. For example, if the module file is webmaster@1: * called example.module, then hook_help() as implemented by that module would be webmaster@1: * defined as example_help(). webmaster@1: */ webmaster@1: webmaster@1: /** webmaster@1: * Determine whether a module implements a hook. webmaster@1: * webmaster@1: * @param $module webmaster@1: * The name of the module (without the .module extension). webmaster@1: * @param $hook webmaster@1: * The name of the hook (e.g. "help" or "menu"). webmaster@1: * @return webmaster@1: * TRUE if the module is both installed and enabled, and the hook is webmaster@1: * implemented in that module. webmaster@1: */ webmaster@1: function module_hook($module, $hook) { webmaster@1: return function_exists($module .'_'. $hook); webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Determine which modules are implementing a hook. webmaster@1: * webmaster@1: * @param $hook webmaster@1: * The name of the hook (e.g. "help" or "menu"). webmaster@1: * @param $sort webmaster@1: * By default, modules are ordered by weight and filename, settings this option webmaster@1: * to TRUE, module list will be ordered by module name. webmaster@1: * @param $refresh webmaster@1: * For internal use only: Whether to force the stored list of hook webmaster@1: * implementations to be regenerated (such as after enabling a new module, webmaster@1: * before processing hook_enable). webmaster@1: * @return webmaster@1: * An array with the names of the modules which are implementing this hook. webmaster@1: */ webmaster@1: function module_implements($hook, $sort = FALSE, $refresh = FALSE) { webmaster@1: static $implementations; webmaster@1: webmaster@1: if ($refresh) { webmaster@1: $implementations = array(); webmaster@1: return; webmaster@1: } webmaster@1: webmaster@1: if (!isset($implementations[$hook])) { webmaster@1: $implementations[$hook] = array(); webmaster@1: $list = module_list(FALSE, TRUE, $sort); webmaster@1: foreach ($list as $module) { webmaster@1: if (module_hook($module, $hook)) { webmaster@1: $implementations[$hook][] = $module; webmaster@1: } webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: // The explicit cast forces a copy to be made. This is needed because webmaster@1: // $implementations[$hook] is only a reference to an element of webmaster@1: // $implementations and if there are nested foreaches (due to nested node webmaster@1: // API calls, for example), they would both manipulate the same array's webmaster@1: // references, which causes some modules' hooks not to be called. webmaster@1: // See also http://www.zend.com/zend/art/ref-count.php. webmaster@1: return (array)$implementations[$hook]; webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * Invoke a hook in a particular module. webmaster@1: * webmaster@1: * @param $module webmaster@1: * The name of the module (without the .module extension). webmaster@1: * @param $hook webmaster@1: * The name of the hook to invoke. webmaster@1: * @param ... webmaster@1: * Arguments to pass to the hook implementation. webmaster@1: * @return webmaster@1: * The return value of the hook implementation. webmaster@1: */ webmaster@1: function module_invoke() { webmaster@1: $args = func_get_args(); webmaster@1: $module = $args[0]; webmaster@1: $hook = $args[1]; webmaster@1: unset($args[0], $args[1]); webmaster@1: $function = $module .'_'. $hook; webmaster@1: if (module_hook($module, $hook)) { webmaster@1: return call_user_func_array($function, $args); webmaster@1: } webmaster@1: } webmaster@1: /** webmaster@1: * Invoke a hook in all enabled modules that implement it. webmaster@1: * webmaster@1: * @param $hook webmaster@1: * The name of the hook to invoke. webmaster@1: * @param ... webmaster@1: * Arguments to pass to the hook. webmaster@1: * @return webmaster@1: * An array of return values of the hook implementations. If modules return webmaster@1: * arrays from their implementations, those are merged into one array. webmaster@1: */ webmaster@1: function module_invoke_all() { webmaster@1: $args = func_get_args(); webmaster@1: $hook = $args[0]; webmaster@1: unset($args[0]); webmaster@1: $return = array(); webmaster@1: foreach (module_implements($hook) as $module) { webmaster@1: $function = $module .'_'. $hook; webmaster@1: $result = call_user_func_array($function, $args); webmaster@1: if (isset($result) && is_array($result)) { webmaster@1: $return = array_merge_recursive($return, $result); webmaster@1: } webmaster@1: else if (isset($result)) { webmaster@1: $return[] = $result; webmaster@1: } webmaster@1: } webmaster@1: webmaster@1: return $return; webmaster@1: } webmaster@1: webmaster@1: /** webmaster@1: * @} End of "defgroup hooks". webmaster@1: */ webmaster@1: webmaster@1: /** webmaster@1: * Array of modules required by core. webmaster@1: */ webmaster@1: function drupal_required_modules() { webmaster@1: return array('block', 'filter', 'node', 'system', 'user'); webmaster@1: }