includes.module.inc

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