2002-01-13 13:18:48 +00:00
< ? php
// $Id$
2004-07-22 16:06:54 +00:00
/**
* @ file
* API for loading and interacting with Drupal modules .
*/
2004-12-01 22:16:50 +00:00
/**
2006-06-08 21:23:40 +00:00
* Load all the modules that have been enabled in the system table .
2009-12-02 00:17:49 +00:00
*
2009-11-01 22:10:07 +00:00
* @ param $bootstrap
* Whether to load only the reduced set of modules loaded in " bootstrap mode "
* for cached pages . See bootstrap . inc .
2010-07-16 11:18:02 +00:00
*
2009-11-01 22:10:07 +00:00
* @ return
* If $bootstrap is NULL , return a boolean indicating whether all modules
* have been loaded .
2004-12-01 22:16:50 +00:00
*/
2009-08-24 00:14:23 +00:00
function module_load_all ( $bootstrap = FALSE ) {
2009-11-01 22:10:07 +00:00
static $has_run = FALSE ;
if ( isset ( $bootstrap )) {
foreach ( module_list ( TRUE , $bootstrap ) as $module ) {
drupal_load ( 'module' , $module );
}
// $has_run will be TRUE if $bootstrap is FALSE.
$has_run = ! $bootstrap ;
2005-07-29 07:09:30 +00:00
}
2009-11-01 22:10:07 +00:00
return $has_run ;
2004-12-01 22:16:50 +00:00
}
2009-11-01 22:10:07 +00:00
2004-07-14 20:42:20 +00:00
/**
2005-04-03 08:03:18 +00:00
* Collect a list of all loaded modules . During the bootstrap , return only
* vital modules . See bootstrap . inc
2004-07-14 20:42:20 +00:00
*
* @ param $refresh
* Whether to force the module list to be regenerated ( such as after the
* administrator has changed the system settings ) .
2009-08-24 00:14:23 +00:00
* @ param $bootstrap
* Whether to return the reduced set of modules loaded in " bootstrap mode "
* for cached pages . See bootstrap . inc .
2006-02-27 15:04:45 +00:00
* @ param $sort
2009-06-20 06:00:24 +00:00
* By default , modules are ordered by weight and module name . Set this option
* to TRUE to return a module list ordered only by module name .
2006-07-13 13:14:25 +00:00
* @ param $fixed_list
* ( Optional ) Override the module list with the given modules . Stays until the
* next call with $refresh = TRUE .
2010-07-16 11:18:02 +00:00
*
2004-07-14 20:42:20 +00:00
* @ return
* An associative array whose keys and values are the names of all loaded
* modules .
*/
2009-08-24 00:14:23 +00:00
function module_list ( $refresh = FALSE , $bootstrap = FALSE , $sort = FALSE , $fixed_list = NULL ) {
2008-08-21 19:36:39 +00:00
static $list = array (), $sorted_list ;
2002-01-13 13:18:48 +00:00
2008-08-21 19:36:39 +00:00
if ( empty ( $list ) || $refresh || $fixed_list ) {
2004-11-25 06:14:59 +00:00
$list = array ();
2009-01-03 08:45:28 +00:00
$sorted_list = NULL ;
2006-07-13 13:14:25 +00:00
if ( $fixed_list ) {
foreach ( $fixed_list as $name => $module ) {
drupal_get_filename ( 'module' , $name , $module [ 'filename' ]);
$list [ $name ] = $name ;
}
2004-01-26 18:55:43 +00:00
}
else {
2010-10-02 23:40:58 +00:00
if ( $refresh ) {
// For the $refresh case, make sure that system_list() returns fresh
// data.
drupal_static_reset ( 'system_list' );
}
2009-08-24 00:14:23 +00:00
if ( $bootstrap ) {
2009-11-08 09:29:07 +00:00
$list = system_list ( 'bootstrap' );
2009-08-24 00:14:23 +00:00
}
else {
2010-09-03 19:49:56 +00:00
// Not using drupal_map_assoc() here as that requires common.inc.
$list = array_keys ( system_list ( 'module_enabled' ));
$list = ( ! empty ( $list ) ? array_combine ( $list , $list ) : array ());
2002-06-01 21:57:29 +00:00
}
2002-01-13 13:18:48 +00:00
}
}
2006-02-27 15:04:45 +00:00
if ( $sort ) {
if ( ! isset ( $sorted_list )) {
$sorted_list = $list ;
ksort ( $sorted_list );
}
return $sorted_list ;
}
2002-01-13 13:18:48 +00:00
return $list ;
}
2009-11-08 09:29:07 +00:00
/**
* Build a list of bootstrap modules and enabled modules and themes .
*
* @ param $type
2009-11-09 19:00:03 +00:00
* The type of list to return :
* - module_enabled : All enabled modules .
* - bootstrap : All enabled modules required for bootstrap .
* - theme : All themes .
2009-11-08 09:29:07 +00:00
*
* @ return
2010-09-03 19:49:56 +00:00
* An associative array of modules or themes , keyed by name . For $type
* 'bootstrap' , the array values equal the keys . For $type 'module_enabled'
* or 'theme' , the array values are objects representing the respective
* database row , with the 'info' property already unserialized .
2009-11-08 09:29:07 +00:00
*
* @ see module_list ()
* @ see list_themes ()
*/
function system_list ( $type ) {
$lists = & drupal_static ( __FUNCTION__ );
2009-11-11 17:04:47 +00:00
// For bootstrap modules, attempt to fetch the list from cache if possible.
// if not fetch only the required information to fire bootstrap hooks
// in case we are going to serve the page from cache.
if ( $type == 'bootstrap' ) {
if ( $cached = cache_get ( 'bootstrap_modules' , 'cache_bootstrap' )) {
$bootstrap_list = $cached -> data ;
}
else {
$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' );
2009-11-18 19:09:52 +00:00
cache_set ( 'bootstrap_modules' , $bootstrap_list , 'cache_bootstrap' );
2009-11-11 17:04:47 +00:00
}
2009-12-02 00:17:49 +00:00
// To avoid a separate database lookup for the filepath, prime the
2009-11-26 18:57:16 +00:00
// drupal_get_filename() static cache for bootstrap modules only.
// The rest is stored separately to keep the bootstrap module cache small.
2009-11-11 17:04:47 +00:00
foreach ( $bootstrap_list as $module ) {
drupal_get_filename ( 'module' , $module -> name , $module -> filename );
}
// We only return the module names here since module_list() doesn't need
// the filename itself.
$lists [ 'bootstrap' ] = array_keys ( $bootstrap_list );
}
// Otherwise build the list for enabled modules and themes.
2009-12-02 00:17:49 +00:00
elseif ( ! isset ( $lists [ 'module_enabled' ])) {
2009-11-26 18:57:16 +00:00
if ( $cached = cache_get ( 'system_list' , 'cache_bootstrap' )) {
$lists = $cached -> data ;
}
else {
$lists = array (
'module_enabled' => array (),
'theme' => array (),
'filepaths' => array (),
);
// The module name (rather than the filename) is used as the fallback
// weighting in order to guarantee consistent behavior across different
// Drupal installations, which might have modules installed in different
// locations in the file system. The ordering here must also be
// consistent with the one used in module_implements().
2010-09-03 19:49:56 +00:00
$result = db_query ( " SELECT * FROM { system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC " );
2009-11-26 18:57:16 +00:00
foreach ( $result as $record ) {
2010-09-03 19:49:56 +00:00
$record -> info = unserialize ( $record -> info );
// Build a list of all enabled modules.
if ( $record -> type == 'module' ) {
$lists [ 'module_enabled' ][ $record -> name ] = $record ;
2009-11-26 18:57:16 +00:00
}
// Build a list of themes.
if ( $record -> type == 'theme' ) {
$lists [ 'theme' ][ $record -> name ] = $record ;
}
// Build a list of filenames so drupal_get_filename can use it.
if ( $record -> status ) {
$lists [ 'filepaths' ][] = array ( 'type' => $record -> type , 'name' => $record -> name , 'filepath' => $record -> filename );
}
2009-11-08 19:53:19 +00:00
}
2009-11-26 18:57:16 +00:00
cache_set ( 'system_list' , $lists , 'cache_bootstrap' );
}
2009-12-02 00:17:49 +00:00
// To avoid a separate database lookup for the filepath, prime the
2009-11-26 18:57:16 +00:00
// drupal_get_filename() static cache with all enabled modules and themes.
foreach ( $lists [ 'filepaths' ] as $item ) {
drupal_get_filename ( $item [ 'type' ], $item [ 'name' ], $item [ 'filepath' ]);
2009-11-08 09:29:07 +00:00
}
}
return $lists [ $type ];
}
2009-11-11 17:04:47 +00:00
/**
* Reset all system_list () caches .
*/
function system_list_reset () {
drupal_static_reset ( 'system_list' );
2010-06-24 17:32:01 +00:00
drupal_static_reset ( 'list_themes' );
2009-11-11 17:04:47 +00:00
cache_clear_all ( 'bootstrap_modules' , 'cache_bootstrap' );
2009-11-26 18:57:16 +00:00
cache_clear_all ( 'system_list' , 'cache_bootstrap' );
2009-11-11 17:04:47 +00:00
}
2006-10-02 16:49:08 +00:00
/**
2009-01-14 12:18:37 +00:00
* Find dependencies any level deep and fill in required by information too .
2007-12-13 10:46:43 +00:00
*
* @ param $files
* The array of filesystem objects used to rebuild the cache .
2010-07-16 11:18:02 +00:00
*
2006-10-02 16:49:08 +00:00
* @ return
2009-01-14 12:18:37 +00:00
* The same array with the new keys for each module :
* - requires : An array with the keys being the modules that this module
* requires .
* - required_by : An array with the keys being the modules that will not work
* without this module .
2006-10-02 16:49:08 +00:00
*/
2007-12-13 10:46:43 +00:00
function _module_build_dependencies ( $files ) {
2009-05-24 17:39:35 +00:00
require_once DRUPAL_ROOT . '/includes/graph.inc' ;
2009-01-14 12:18:37 +00:00
foreach ( $files as $filename => $file ) {
$graph [ $file -> name ][ 'edges' ] = array ();
if ( isset ( $file -> info [ 'dependencies' ]) && is_array ( $file -> info [ 'dependencies' ])) {
2009-08-13 03:03:04 +00:00
foreach ( $file -> info [ 'dependencies' ] as $dependency ) {
$dependency_data = drupal_parse_dependency ( $dependency );
$graph [ $file -> name ][ 'edges' ][ $dependency_data [ 'name' ]] = $dependency_data ;
2006-10-02 16:49:08 +00:00
}
}
2009-01-14 12:18:37 +00:00
}
2010-07-28 01:46:59 +00:00
drupal_depth_first_search ( $graph );
2009-01-14 12:18:37 +00:00
foreach ( $graph as $module => $data ) {
2009-04-11 17:58:18 +00:00
$files [ $module ] -> required_by = isset ( $data [ 'reverse_paths' ]) ? $data [ 'reverse_paths' ] : array ();
2009-01-14 12:18:37 +00:00
$files [ $module ] -> requires = isset ( $data [ 'paths' ]) ? $data [ 'paths' ] : array ();
$files [ $module ] -> sort = $data [ 'weight' ];
}
2006-08-03 01:02:51 +00:00
return $files ;
}
2004-07-14 20:42:20 +00:00
/**
* Determine whether a given module exists .
*
* @ param $module
* The name of the module ( without the . module extension ) .
2010-07-16 11:18:02 +00:00
*
2004-07-14 20:42:20 +00:00
* @ return
* TRUE if the module is both installed and enabled .
*/
2006-08-20 05:57:41 +00:00
function module_exists ( $module ) {
2002-01-13 13:18:48 +00:00
$list = module_list ();
2008-05-06 12:18:54 +00:00
return isset ( $list [ $module ]);
2002-01-13 13:18:48 +00:00
}
2006-08-03 01:02:51 +00:00
/**
* Load a module ' s installation hooks .
*/
function module_load_install ( $module ) {
// Make sure the installation API is available
2008-09-20 20:22:25 +00:00
include_once DRUPAL_ROOT . '/includes/install.inc' ;
2006-08-03 01:02:51 +00:00
2008-05-07 06:39:57 +00:00
module_load_include ( 'install' , $module );
2007-05-25 12:46:46 +00:00
}
/**
* Load a module include file .
2009-07-28 19:06:16 +00:00
*
2009-07-01 17:34:13 +00:00
* Examples :
* @ code
2009-07-10 04:58:08 +00:00
* // Load node.admin.inc from the node module.
2009-07-01 17:34:13 +00:00
* module_load_include ( 'inc' , 'node' , 'node.admin' );
2009-07-10 04:58:08 +00:00
* // Load content_types.inc from the node module.
2009-07-28 19:06:16 +00:00
* module_load_include ( 'inc' , 'node' , 'content_types' );
2009-07-01 17:34:13 +00:00
* @ endcode
2007-05-25 12:46:46 +00:00
*
2010-01-28 13:56:25 +00:00
* Do not use this function to load an install file , use module_load_install ()
* instead . Do not use this function in a global context since it requires
* Drupal to be fully bootstrapped , use require_once DRUPAL_ROOT . '/path/file'
2009-07-10 04:58:08 +00:00
* instead .
*
2007-05-25 12:46:46 +00:00
* @ param $type
* The include file ' s type ( file extension ) .
* @ param $module
* The module to which the include file belongs .
* @ param $name
2009-07-28 19:06:16 +00:00
* Optionally , specify the base file name ( without the $type extension ) .
2009-07-01 17:34:13 +00:00
* If not set , $module is used .
2007-05-25 12:46:46 +00:00
*/
function module_load_include ( $type , $module , $name = NULL ) {
if ( empty ( $name )) {
$name = $module ;
}
2009-08-24 00:14:23 +00:00
if ( function_exists ( 'drupal_get_path' )) {
2008-09-20 20:22:25 +00:00
$file = DRUPAL_ROOT . '/' . drupal_get_path ( 'module' , $module ) . " / $name . $type " ;
if ( is_file ( $file )) {
require_once $file ;
return $file ;
}
2007-05-25 12:46:46 +00:00
}
2008-09-20 20:22:25 +00:00
return FALSE ;
2007-05-25 12:46:46 +00:00
}
/**
* Load an include file for each of the modules that have been enabled in
* the system table .
*/
function module_load_all_includes ( $type , $name = NULL ) {
$modules = module_list ();
foreach ( $modules as $module ) {
module_load_include ( $type , $module , $name );
2006-08-03 01:02:51 +00:00
}
}
/**
2010-06-26 12:34:44 +00:00
* Enables or installs a given list of modules .
2006-08-03 01:02:51 +00:00
*
2010-06-26 12:34:44 +00:00
* Definitions :
* - " Enabling " is the process of activating a module for use by Drupal .
* - " Disabling " is the process of deactivating a module .
* - " Installing " is the process of enabling it for the first time or after it
* has been uninstalled .
* - " Uninstalling " is the process of removing all traces of a module .
*
* Order of events :
* - Gather and add module dependencies to $module_list ( if applicable ) .
* - For each module that is being enabled :
* - Install module schema and update system registries and caches .
* - If the module is being enabled for the first time or had been
* uninstalled , invoke hook_install () and add it to the list of installed
* modules .
* - Invoke hook_enable () .
* - Invoke hook_modules_installed () .
* - Invoke hook_modules_enabled () .
2010-07-16 11:18:02 +00:00
*
2006-11-16 08:28:08 +00:00
* @ param $module_list
* An array of module names .
2010-01-13 05:08:29 +00:00
* @ param $enable_dependencies
* If TRUE , dependencies will automatically be added and enabled in the
* correct order . This incurs a significant performance cost , so use FALSE
* if you know $module_list is already complete and in the correct order .
2010-03-02 09:07:09 +00:00
*
2010-01-13 05:08:29 +00:00
* @ return
* FALSE if one or more dependencies are missing , TRUE otherwise .
2010-06-26 12:34:44 +00:00
*
* @ see hook_install ()
* @ see hook_enable ()
* @ see hook_modules_installed ()
* @ see hook_modules_enabled ()
2006-08-03 01:02:51 +00:00
*/
2010-03-02 09:07:09 +00:00
function module_enable ( $module_list , $enable_dependencies = TRUE ) {
2010-01-13 05:08:29 +00:00
if ( $enable_dependencies ) {
// Get all module data so we can find dependencies and sort.
$module_data = system_rebuild_module_data ();
// Create an associative array with weights as values.
$module_list = array_flip ( array_values ( $module_list ));
while ( list ( $module ) = each ( $module_list )) {
if ( ! isset ( $module_data [ $module ])) {
// This module is not found in the filesystem, abort.
return FALSE ;
}
if ( $module_data [ $module ] -> status ) {
// Skip already enabled modules.
unset ( $module_list [ $module ]);
continue ;
}
$module_list [ $module ] = $module_data [ $module ] -> sort ;
// Add dependencies to the list, with a placeholder weight.
// The new modules will be processed as the while loop continues.
foreach ( $module_data [ $module ] -> info [ 'dependencies' ] as $dependency ) {
if ( ! isset ( $module_list [ $dependency ])) {
$module_list [ $dependency ] = 0 ;
}
}
}
if ( ! $module_list ) {
// Nothing to do. All modules already enabled.
return TRUE ;
}
// Sort the module list by pre-calculated weights.
arsort ( $module_list );
$module_list = array_keys ( $module_list );
}
2010-02-26 18:31:29 +00:00
// Required for module installation checks.
2010-02-15 22:16:58 +00:00
include_once DRUPAL_ROOT . '/includes/install.inc' ;
2010-02-26 18:31:29 +00:00
$modules_installed = array ();
$modules_enabled = array ();
2006-11-16 08:28:08 +00:00
foreach ( $module_list as $module ) {
2010-02-26 18:31:29 +00:00
// Only process modules that are not already enabled.
2009-01-04 18:09:34 +00:00
$existing = db_query ( " SELECT status FROM { system} WHERE type = :type AND name = :name " , array (
':type' => 'module' ,
':name' => $module ))
-> fetchObject ();
2007-08-08 07:22:03 +00:00
if ( $existing -> status == 0 ) {
2010-02-26 18:31:29 +00:00
// Load the module's code.
drupal_load ( 'module' , $module );
2006-11-16 08:28:08 +00:00
module_load_install ( $module );
2010-02-26 18:31:29 +00:00
// Update the database and module list to reflect the new module. This
// needs to be done first so that the module's hook implementations,
// hook_schema() in particular, can be called while it is being
// installed.
2009-01-04 18:09:34 +00:00
db_update ( 'system' )
-> fields ( array ( 'status' => 1 ))
-> condition ( 'type' , 'module' )
-> condition ( 'name' , $module )
-> execute ();
2010-02-26 18:31:29 +00:00
// Refresh the module list to include it.
2010-10-02 23:40:58 +00:00
system_list_reset ();
2010-02-26 18:31:29 +00:00
module_list ( TRUE );
module_implements ( '' , FALSE , TRUE );
_system_update_bootstrap_status ();
// Update the registry to include it.
registry_update ();
// Refresh the schema to include it.
drupal_get_schema ( NULL , TRUE );
2010-04-16 13:39:42 +00:00
// Clear entity cache.
entity_info_cache_clear ();
2010-02-26 18:31:29 +00:00
// Now install the module if necessary.
if ( drupal_get_installed_schema_version ( $module , TRUE ) == SCHEMA_UNINSTALLED ) {
drupal_install_schema ( $module );
2010-10-01 19:52:28 +00:00
// Set the schema version to the number of the last update provided
// by the module.
2010-02-26 18:31:29 +00:00
$versions = drupal_get_schema_versions ( $module );
2010-10-01 19:52:28 +00:00
$version = $versions ? max ( $versions ) : SCHEMA_INSTALLED ;
// If the module has no current updates, but has some that were
// previously removed, set the version to the value of
// hook_update_last_removed().
if ( $last_removed = module_invoke ( $module , 'update_last_removed' )) {
$version = max ( $version , $last_removed );
}
drupal_set_installed_schema_version ( $module , $version );
2010-05-09 13:49:33 +00:00
// Allow the module to perform install tasks.
module_invoke ( $module , 'install' );
2010-02-26 18:31:29 +00:00
// Record the fact that it was installed.
$modules_installed [] = $module ;
2010-03-03 07:38:08 +00:00
watchdog ( 'system' , '%module module installed.' , array ( '%module' => $module ), WATCHDOG_INFO );
2010-02-26 18:31:29 +00:00
}
2006-11-16 08:28:08 +00:00
2010-02-26 18:31:29 +00:00
// Enable the module.
module_invoke ( $module , 'enable' );
// Record the fact that it was enabled.
$modules_enabled [] = $module ;
watchdog ( 'system' , '%module module enabled.' , array ( '%module' => $module ), WATCHDOG_INFO );
2009-06-28 03:56:43 +00:00
}
2006-11-16 08:28:08 +00:00
}
2010-02-26 18:31:29 +00:00
// If any modules were newly installed, invoke hook_modules_installed().
2010-03-02 09:07:09 +00:00
if ( ! empty ( $modules_installed )) {
2010-02-26 18:31:29 +00:00
module_invoke_all ( 'modules_installed' , $modules_installed );
2006-08-03 01:02:51 +00:00
}
2008-10-11 22:46:22 +00:00
2010-02-26 18:31:29 +00:00
// If any modules were newly enabled, invoke hook_modules_enabled().
if ( ! empty ( $modules_enabled )) {
module_invoke_all ( 'modules_enabled' , $modules_enabled );
2008-10-11 22:46:22 +00:00
}
2010-01-13 05:08:29 +00:00
return TRUE ;
2006-08-03 01:02:51 +00:00
}
/**
2006-11-27 23:15:41 +00:00
* Disable a given set of modules .
2006-08-03 01:02:51 +00:00
*
2006-11-27 23:15:41 +00:00
* @ param $module_list
* An array of module names .
2010-01-13 05:08:29 +00:00
* @ param $disable_dependents
* If TRUE , dependent modules will automatically be added and disabled in the
* correct order . This incurs a significant performance cost , so use FALSE
* if you know $module_list is already complete and in the correct order .
2006-08-03 01:02:51 +00:00
*/
2010-01-13 05:08:29 +00:00
function module_disable ( $module_list , $disable_dependents = TRUE ) {
if ( $disable_dependents ) {
// Get all module data so we can find dependents and sort.
$module_data = system_rebuild_module_data ();
// Create an associative array with weights as values.
$module_list = array_flip ( array_values ( $module_list ));
2010-11-20 03:34:30 +00:00
$profile = drupal_get_profile ();
2010-01-13 05:08:29 +00:00
while ( list ( $module ) = each ( $module_list )) {
if ( ! isset ( $module_data [ $module ]) || ! $module_data [ $module ] -> status ) {
// This module doesn't exist or is already disabled, skip it.
unset ( $module_list [ $module ]);
continue ;
}
$module_list [ $module ] = $module_data [ $module ] -> sort ;
// Add dependent modules to the list, with a placeholder weight.
// The new modules will be processed as the while loop continues.
foreach ( $module_data [ $module ] -> required_by as $dependent => $dependent_data ) {
2010-11-20 03:34:30 +00:00
if ( ! isset ( $module_list [ $dependent ]) && $dependent != $profile ) {
2010-01-13 05:08:29 +00:00
$module_list [ $dependent ] = 0 ;
}
}
}
// Sort the module list by pre-calculated weights.
asort ( $module_list );
$module_list = array_keys ( $module_list );
}
2006-11-27 23:15:41 +00:00
$invoke_modules = array ();
2010-01-13 05:08:29 +00:00
2006-11-27 23:15:41 +00:00
foreach ( $module_list as $module ) {
if ( module_exists ( $module )) {
2007-09-02 14:42:30 +00:00
// Check if node_access table needs rebuilding.
if ( ! node_access_needs_rebuild () && module_hook ( $module , 'node_grants' )) {
node_access_needs_rebuild ( TRUE );
}
2006-11-27 23:15:41 +00:00
module_load_install ( $module );
module_invoke ( $module , 'disable' );
2009-01-04 18:09:34 +00:00
db_update ( 'system' )
-> fields ( array ( 'status' => 0 ))
-> condition ( 'type' , 'module' )
-> condition ( 'name' , $module )
-> execute ();
2006-11-27 23:15:41 +00:00
$invoke_modules [] = $module ;
2009-05-12 18:08:43 +00:00
watchdog ( 'system' , '%module module disabled.' , array ( '%module' => $module ), WATCHDOG_INFO );
2006-11-27 23:15:41 +00:00
}
2006-08-03 01:02:51 +00:00
}
2006-11-27 23:15:41 +00:00
if ( ! empty ( $invoke_modules )) {
2009-08-24 00:14:23 +00:00
// Refresh the module list to exclude the disabled modules.
2010-10-02 23:40:58 +00:00
system_list_reset ();
2009-08-24 00:14:23 +00:00
module_list ( TRUE );
module_implements ( '' , FALSE , TRUE );
2009-10-10 12:12:15 +00:00
// Invoke hook_modules_disabled before disabling modules,
2008-10-11 22:46:22 +00:00
// so we can still call module hooks to get information.
module_invoke_all ( 'modules_disabled' , $invoke_modules );
2009-12-28 10:48:51 +00:00
// Update the registry to remove the newly-disabled module.
registry_update ();
2010-01-13 05:08:29 +00:00
_system_update_bootstrap_status ();
2006-08-03 01:02:51 +00:00
}
2007-09-02 14:42:30 +00:00
2007-09-04 21:10:45 +00:00
// If there remains no more node_access module, rebuilding will be
2007-09-02 14:42:30 +00:00
// straightforward, we can do it right now.
if ( node_access_needs_rebuild () && count ( module_implements ( 'node_grants' )) == 0 ) {
node_access_rebuild ();
}
2006-08-03 01:02:51 +00:00
}
2004-07-14 20:42:20 +00:00
/**
* @ defgroup hooks Hooks
2004-09-09 05:51:08 +00:00
* @ {
* Allow modules to interact with the Drupal core .
2004-07-14 20:42:20 +00:00
*
* Drupal ' s module system is based on the concept of " hooks " . A hook is a PHP
2009-11-08 09:31:10 +00:00
* function that is named foo_bar (), where " foo " is the name of the module
* ( whose filename is thus foo . module ) and " bar " is the name of the hook . Each
* hook has a defined set of parameters and a specified result type .
2004-07-14 20:42:20 +00:00
*
2009-11-08 09:31:10 +00:00
* To extend Drupal , a module need simply implement a hook . When Drupal wishes
* to allow intervention from modules , it determines which modules implement a
* hook and calls that hook in all enabled modules that implement it .
2004-07-14 20:42:20 +00:00
*
* The available hooks to implement are explained here in the Hooks section of
* the developer documentation . The string " hook " is used as a placeholder for
2009-11-08 09:31:10 +00:00
* the module name in the hook definitions . For example , if the module file is
* called example . module , then hook_help () as implemented by that module would
* be defined as example_help () .
2010-04-20 18:50:36 +00:00
*
2010-04-21 07:53:45 +00:00
* The example functions included are not part of the Drupal core , they are
* just models that you can modify . Only the hooks implemented within modules
* are executed when running Drupal .
*
2010-04-20 18:50:36 +00:00
* See also @ link themeable the themeable group page . @ endlink
2004-07-14 20:42:20 +00:00
*/
/**
* Determine whether a module implements a hook .
*
* @ param $module
* The name of the module ( without the . module extension ) .
* @ param $hook
* The name of the hook ( e . g . " help " or " menu " ) .
2010-07-16 11:18:02 +00:00
*
2004-07-14 20:42:20 +00:00
* @ return
* TRUE if the module is both installed and enabled , and the hook is
* implemented in that module .
*/
function module_hook ( $module , $hook ) {
2009-08-24 00:14:23 +00:00
return function_exists ( $module . '_' . $hook );
2002-01-13 13:18:48 +00:00
}
2005-01-16 18:44:49 +00:00
/**
* Determine which modules are implementing a hook .
*
* @ param $hook
2009-08-24 00:14:23 +00:00
* The name of the hook ( e . g . " help " or " menu " ) .
2009-08-24 00:10:46 +00:00
* @ param $sort
2009-08-24 00:14:23 +00:00
* By default , modules are ordered by weight and filename , settings this option
* to TRUE , module list will be ordered by module name .
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
* @ param $reset
2009-08-24 00:14:23 +00:00
* For internal use only : Whether to force the stored list of hook
* implementations to be regenerated ( such as after enabling a new module ,
* before processing hook_enable ) .
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
*
2005-01-16 18:44:49 +00:00
* @ return
* An array with the names of the modules which are implementing this hook .
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
*
2010-03-26 17:14:46 +00:00
* @ see module_implements_write_cache ()
2005-01-16 18:44:49 +00:00
*/
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
function module_implements ( $hook , $sort = FALSE , $reset = FALSE ) {
2009-11-20 06:12:45 +00:00
// Use the advanced drupal_static() pattern, since this is called very often.
2010-01-07 04:54:18 +00:00
static $drupal_static_fast ;
if ( ! isset ( $drupal_static_fast )) {
$drupal_static_fast [ 'implementations' ] = & drupal_static ( __FUNCTION__ );
}
$implementations = & $drupal_static_fast [ 'implementations' ];
2009-08-08 15:03:29 +00:00
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
// We maintain a persistent cache of hook implementations in addition to the
// static cache to avoid looping through every module and every hook on each
// request. Benchmarks show that the benefit of this caching outweighs the
// additional database hit even when using the default database caching
// backend and only a small number of modules are enabled. The cost of the
// cache_get() is more or less constant and reduced further when non-database
// caching backends are used, so there will be more significant gains when a
// large number of modules are installed or hooks invoked, since this can
// quickly lead to module_hook() being called several thousand times
// per request.
if ( $reset ) {
2007-07-18 14:10:14 +00:00
$implementations = array ();
2009-11-10 22:06:09 +00:00
cache_set ( 'module_implements' , array (), 'cache_bootstrap' );
2009-10-16 03:01:55 +00:00
drupal_static_reset ( 'module_hook_info' );
2009-11-05 16:19:25 +00:00
drupal_static_reset ( 'drupal_alter' );
2009-11-10 22:06:09 +00:00
cache_clear_all ( 'hook_info' , 'cache_bootstrap' );
2008-10-31 02:18:22 +00:00
return ;
2006-11-27 23:15:41 +00:00
}
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
// Fetch implementations from cache.
if ( empty ( $implementations )) {
2009-11-10 22:06:09 +00:00
$implementations = cache_get ( 'module_implements' , 'cache_bootstrap' );
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
if ( $implementations === FALSE ) {
$implementations = array ();
}
else {
$implementations = $implementations -> data ;
}
}
2009-08-24 00:14:23 +00:00
if ( ! isset ( $implementations [ $hook ])) {
2010-06-24 21:40:06 +00:00
// The hook is not cached, so ensure that whether or not it has
// implementations, that the cache is updated at the end of the request.
$implementations [ '#write_cache' ] = TRUE ;
2009-10-16 03:01:55 +00:00
$hook_info = module_hook_info ();
2009-08-24 00:14:23 +00:00
$implementations [ $hook ] = array ();
$list = module_list ( FALSE , FALSE , $sort );
foreach ( $list as $module ) {
2010-04-22 18:56:53 +00:00
$include_file = isset ( $hook_info [ $hook ][ 'group' ]) && module_load_include ( 'inc' , $module , $module . '.' . $hook_info [ $hook ][ 'group' ]);
if ( module_hook ( $module , $hook )) {
2009-10-16 03:01:55 +00:00
$implementations [ $hook ][ $module ] = $include_file ? $hook_info [ $hook ][ 'group' ] : FALSE ;
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
}
}
2010-04-22 22:36:01 +00:00
// Allow modules to change the weight of specific implementations but avoid
// an infinite loop.
if ( $hook != 'module_implements_alter' ) {
drupal_alter ( 'module_implements' , $implementations [ $hook ], $hook );
}
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
}
else {
2009-10-16 03:01:55 +00:00
foreach ( $implementations [ $hook ] as $module => $group ) {
// If this hook implementation is stored in a lazy-loaded file, so include
// that file first.
if ( $group ) {
module_load_include ( 'inc' , $module , " $module . $group " );
}
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
// It is possible that a module removed a hook implementation without the
// implementations cache being rebuilt yet, so we check module_hook() on
// each request to avoid undefined function errors.
if ( ! module_hook ( $module , $hook )) {
// Clear out the stale implementation from the cache and force a cache
// refresh to forget about no longer existing hook implementations.
unset ( $implementations [ $hook ][ $module ]);
$implementations [ '#write_cache' ] = TRUE ;
2005-01-16 18:44:49 +00:00
}
}
2008-10-31 02:18:22 +00:00
}
2005-01-16 18:44:49 +00:00
2009-10-16 03:01:55 +00:00
return array_keys ( $implementations [ $hook ]);
}
/**
* Retrieve a list of what hooks are explicitly declared .
*/
function module_hook_info () {
$hook_info = & drupal_static ( __FUNCTION__ , array ());
if ( empty ( $hook_info )) {
2009-11-10 22:06:09 +00:00
$cache = cache_get ( 'hook_info' , 'cache_bootstrap' );
2009-10-16 03:01:55 +00:00
if ( $cache === FALSE ) {
// Rebuild the cache and save it.
// We can't use module_invoke_all() here or it would cause an infinite
// loop.
foreach ( module_list () as $module ) {
$function = $module . '_hook_info' ;
if ( function_exists ( $function )) {
$result = $function ();
if ( isset ( $result ) && is_array ( $result )) {
$hook_info = array_merge_recursive ( $hook_info , $result );
}
}
}
// We can't use drupal_alter() for the same reason as above.
foreach ( module_list () as $module ) {
$function = $module . '_hook_info_alter' ;
if ( function_exists ( $function )) {
$function ( $hook_info );
}
}
2009-11-10 22:06:09 +00:00
cache_set ( 'hook_info' , $hook_info , 'cache_bootstrap' );
2009-10-16 03:01:55 +00:00
}
else {
$hook_info = $cache -> data ;
}
}
return $hook_info ;
2008-10-31 02:18:22 +00:00
}
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
/**
* Writes the hook implementation cache .
*
* @ see module_implements ()
*/
function module_implements_write_cache () {
$implementations = & drupal_static ( 'module_implements' );
2009-09-29 18:08:28 +00:00
// Check whether we need to write the cache. We do not want to cache hooks
// which are only invoked on HTTP POST requests since these do not need to be
// optimized as tightly, and not doing so keeps the cache entry smaller.
if ( isset ( $implementations [ '#write_cache' ]) && ( $_SERVER [ 'REQUEST_METHOD' ] == 'GET' || $_SERVER [ 'REQUEST_METHOD' ] == 'HEAD' )) {
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
unset ( $implementations [ '#write_cache' ]);
2009-11-18 19:09:52 +00:00
cache_set ( 'module_implements' , $implementations , 'cache_bootstrap' );
- Patch #557542 by CorniI, catch, fago, Crell, sun | pwolanin, chx, webchick, mattyoung, alexanderpas, justinrandell, dropcube, moshe weitzman, Damien Tournoud, Rob Loach, Dries: cache module_implements() for better performance and scalability.
2009-09-27 11:08:45 +00:00
}
}
2004-07-14 20:42:20 +00:00
/**
* Invoke a hook in a particular module .
*
* @ param $module
* The name of the module ( without the . module extension ) .
* @ param $hook
* The name of the hook to invoke .
* @ param ...
* Arguments to pass to the hook implementation .
2010-07-16 11:18:02 +00:00
*
2004-07-14 20:42:20 +00:00
* @ return
* The return value of the hook implementation .
*/
2005-03-01 20:23:35 +00:00
function module_invoke () {
$args = func_get_args ();
2007-08-30 16:09:50 +00:00
$module = $args [ 0 ];
$hook = $args [ 1 ];
unset ( $args [ 0 ], $args [ 1 ]);
2005-03-01 20:23:35 +00:00
if ( module_hook ( $module , $hook )) {
2008-05-06 12:18:54 +00:00
return call_user_func_array ( $module . '_' . $hook , $args );
2004-07-14 20:42:20 +00:00
}
}
/**
* Invoke a hook in all enabled modules that implement it .
*
* @ param $hook
* The name of the hook to invoke .
* @ param ...
* Arguments to pass to the hook .
2010-07-16 11:18:02 +00:00
*
2004-07-14 20:42:20 +00:00
* @ return
* An array of return values of the hook implementations . If modules return
* arrays from their implementations , those are merged into one array .
*/
2005-03-01 20:23:35 +00:00
function module_invoke_all () {
$args = func_get_args ();
2007-08-30 16:09:50 +00:00
$hook = $args [ 0 ];
unset ( $args [ 0 ]);
2004-07-14 20:42:20 +00:00
$return = array ();
2005-03-01 20:23:35 +00:00
foreach ( module_implements ( $hook ) as $module ) {
2008-04-14 17:48:46 +00:00
$function = $module . '_' . $hook ;
2009-08-24 00:14:23 +00:00
if ( function_exists ( $function )) {
2008-05-06 12:18:54 +00:00
$result = call_user_func_array ( $function , $args );
if ( isset ( $result ) && is_array ( $result )) {
$return = array_merge_recursive ( $return , $result );
}
2008-10-12 04:30:09 +00:00
elseif ( isset ( $result )) {
2008-05-06 12:18:54 +00:00
$return [] = $result ;
}
2004-08-22 17:03:42 +00:00
}
2004-07-14 20:42:20 +00:00
}
return $return ;
}
/**
2004-09-09 05:51:08 +00:00
* @ } End of " defgroup hooks " .
2004-07-14 20:42:20 +00:00
*/
2007-02-04 21:20:50 +00:00
/**
* Array of modules required by core .
*/
function drupal_required_modules () {
2010-11-19 20:35:31 +00:00
$files = drupal_system_listing ( '/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/' , 'modules' , 'name' , 0 );
2008-10-12 01:23:07 +00:00
$required = array ();
2009-08-21 07:50:08 +00:00
// An install profile is required and one must always be loaded.
$required [] = drupal_get_profile ();
2008-10-12 01:23:07 +00:00
foreach ( $files as $name => $file ) {
2009-08-17 19:14:42 +00:00
$info = drupal_parse_info_file ( $file -> uri );
2008-10-12 01:23:07 +00:00
if ( ! empty ( $info ) && ! empty ( $info [ 'required' ]) && $info [ 'required' ]) {
$required [] = $name ;
}
}
2009-08-21 07:50:08 +00:00
2008-10-12 01:23:07 +00:00
return $required ;
2007-02-04 21:20:50 +00:00
}
2010-04-22 22:36:01 +00:00
/**
* Hands off alterable variables to type - specific * _alter implementations .
*
* This dispatch function hands off the passed in variables to type - specific
* hook_TYPE_alter () implementations in modules . It ensures a consistent
* interface for all altering operations .
*
* A maximum of 2 alterable arguments is supported . In case more arguments need
* to be passed and alterable , modules provide additional variables assigned by
* reference in the last $context argument :
* @ code
* $context = array (
* 'alterable' => & $alterable ,
* 'unalterable' => $unalterable ,
* 'foo' => 'bar' ,
* );
* drupal_alter ( 'mymodule_data' , $alterable1 , $alterable2 , $context );
* @ endcode
*
* Note that objects are always passed by reference in PHP5 . If it is absolutely
* required that no implementation alters a passed object in $context , then an
* object needs to be cloned :
* @ code
* $context = array (
* 'unalterable_object' => clone $object ,
* );
* drupal_alter ( 'mymodule_data' , $data , $context );
* @ endcode
*
* @ param $type
2010-10-03 02:04:55 +00:00
* A string describing the type of the alterable $data . 'form' , 'links' ,
2010-04-28 12:36:26 +00:00
* 'node_content' , and so on are several examples . Alternatively can be an
* array , in which case hook_TYPE_alter () is invoked for each value in the
* array , ordered first by module , and then for each module , in the order of
* values in $type . For example , when Form API is using drupal_alter () to
* execute both hook_form_alter () and hook_form_FORM_ID_alter ()
* implementations , it passes array ( 'form' , 'form_' . $form_id ) for $type .
2010-04-22 22:36:01 +00:00
* @ param & $data
2010-10-03 02:04:55 +00:00
* The variable that will be passed to hook_TYPE_alter () implementations to be
* altered . The type of this variable depends on the value of the $type
* argument . For example , when altering a 'form' , $data will be a structured
* array . When altering a 'profile' , $data will be an object .
2010-04-22 22:36:01 +00:00
* @ param & $context1
* ( optional ) An additional variable that is passed by reference .
* @ param & $context2
* ( optional ) An additional variable that is passed by reference . If more
* context needs to be provided to implementations , then this should be an
2010-10-03 02:04:55 +00:00
* associative array as described above .
2010-04-22 22:36:01 +00:00
*/
function drupal_alter ( $type , & $data , & $context1 = NULL , & $context2 = NULL ) {
// Use the advanced drupal_static() pattern, since this is called very often.
static $drupal_static_fast ;
if ( ! isset ( $drupal_static_fast )) {
$drupal_static_fast [ 'functions' ] = & drupal_static ( __FUNCTION__ );
}
$functions = & $drupal_static_fast [ 'functions' ];
2010-04-28 12:36:26 +00:00
// Most of the time, $type is passed as a string, so for performance,
// normalize it to that. When passed as an array, usually the first item in
// the array is a generic type, and additional items in the array are more
// specific variants of it, as in the case of array('form', 'form_FORM_ID').
if ( is_array ( $type )) {
$cid = implode ( ',' , $type );
$extra_types = $type ;
$type = array_shift ( $extra_types );
// Allow if statements in this function to use the faster isset() rather
// than !empty() both when $type is passed as a string, or as an array with
// one item.
if ( empty ( $extra_types )) {
unset ( $extra_types );
}
}
else {
$cid = $type ;
}
2010-04-22 22:36:01 +00:00
// Some alter hooks are invoked many times per page request, so statically
// cache the list of functions to call, and on subsequent calls, iterate
// through them quickly.
2010-04-28 12:36:26 +00:00
if ( ! isset ( $functions [ $cid ])) {
$functions [ $cid ] = array ();
2010-04-22 22:36:01 +00:00
$hook = $type . '_alter' ;
2010-04-28 12:36:26 +00:00
$modules = module_implements ( $hook );
if ( ! isset ( $extra_types )) {
// For the more common case of a single hook, we do not need to call
// function_exists(), since module_implements() returns only modules with
// implementations.
foreach ( $modules as $module ) {
$functions [ $cid ][] = $module . '_' . $hook ;
}
}
else {
// For multiple hooks, we need $modules to contain every module that
// implements at least one of them.
$extra_modules = array ();
foreach ( $extra_types as $extra_type ) {
$extra_modules = array_merge ( $extra_modules , module_implements ( $extra_type . '_alter' ));
}
// If any modules implement one of the extra hooks that do not implement
// the primary hook, we need to add them to the $modules array in their
// appropriate order.
if ( array_diff ( $extra_modules , $modules )) {
// Order the modules by the order returned by module_list().
$modules = array_intersect ( module_list (), array_merge ( $modules , $extra_modules ));
}
foreach ( $modules as $module ) {
// Since $modules is a merged array, for any given module, we do not
// know whether it has any particular implementation, so we need a
// function_exists().
$function = $module . '_' . $hook ;
if ( function_exists ( $function )) {
$functions [ $cid ][] = $function ;
}
foreach ( $extra_types as $extra_type ) {
$function = $module . '_' . $extra_type . '_alter' ;
if ( function_exists ( $function )) {
$functions [ $cid ][] = $function ;
}
}
}
2010-04-22 22:36:01 +00:00
}
// Allow the theme to alter variables after the theme system has been
// initialized.
global $theme , $base_theme_info ;
if ( isset ( $theme )) {
$theme_keys = array ();
foreach ( $base_theme_info as $base ) {
$theme_keys [] = $base -> name ;
}
$theme_keys [] = $theme ;
foreach ( $theme_keys as $theme_key ) {
$function = $theme_key . '_' . $hook ;
if ( function_exists ( $function )) {
2010-04-28 12:36:26 +00:00
$functions [ $cid ][] = $function ;
}
if ( isset ( $extra_types )) {
foreach ( $extra_types as $extra_type ) {
$function = $theme_key . '_' . $extra_type . '_alter' ;
if ( function_exists ( $function )) {
$functions [ $cid ][] = $function ;
}
}
2010-04-22 22:36:01 +00:00
}
}
}
}
2010-04-28 12:36:26 +00:00
foreach ( $functions [ $cid ] as $function ) {
2010-04-22 22:36:01 +00:00
$function ( $data , $context1 , $context2 );
}
}
2010-04-28 12:36:26 +00:00