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 .
2004-12-01 22:16:50 +00:00
*/
2006-06-08 21:23:40 +00:00
function module_load_all () {
2005-07-29 07:09:30 +00:00
foreach ( module_list ( TRUE , FALSE ) as $module ) {
drupal_load ( 'module' , $module );
}
2004-12-01 22:16:50 +00:00
}
2004-07-14 20:42:20 +00:00
/**
* Call a function repeatedly with each module in turn as an argument .
*/
function module_iterate ( $function , $argument = '' ) {
2002-10-17 18:34:38 +00:00
foreach ( module_list () as $name ) {
$function ( $name , $argument );
}
2002-01-13 13:18:48 +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 ) .
* @ param $bootstrap
2005-04-03 08:03:18 +00:00
* 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
* By default , modules are ordered by weight and filename , settings this option
* to TRUE , module list will be ordered 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 .
2004-07-14 20:42:20 +00:00
* @ return
* An associative array whose keys and values are the names of all loaded
* modules .
*/
2006-07-13 13:14:25 +00:00
function module_list ( $refresh = FALSE , $bootstrap = TRUE , $sort = FALSE , $fixed_list = NULL ) {
2006-02-27 15:04:45 +00:00
static $list , $sorted_list ;
2002-01-13 13:18:48 +00:00
2006-07-13 13:14:25 +00:00
if ( $refresh || $fixed_list ) {
2006-06-28 21:18:30 +00:00
unset ( $sorted_list );
2004-11-25 06:14:59 +00:00
$list = array ();
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 {
2006-07-13 13:14:25 +00:00
if ( $bootstrap ) {
2007-05-07 11:59:40 +00:00
$result = db_query ( " SELECT name, filename, throttle FROM { system} WHERE type = 'module' AND status = 1 AND bootstrap = 1 ORDER BY weight ASC, filename ASC " );
2006-07-13 13:14:25 +00:00
}
else {
2007-05-07 11:59:40 +00:00
$result = db_query ( " SELECT name, filename, throttle FROM { system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC " );
2006-07-13 13:14:25 +00:00
}
while ( $module = db_fetch_object ( $result )) {
if ( file_exists ( $module -> filename )) {
// Determine the current throttle status and see if the module should be
// loaded based on server load. We have to directly access the throttle
// variables, since throttle.module may not be loaded yet.
$throttle = ( $module -> throttle && variable_get ( 'throttle_level' , 0 ) > 0 );
if ( ! $throttle ) {
drupal_get_filename ( 'module' , $module -> name , $module -> filename );
$list [ $module -> name ] = $module -> name ;
}
2003-11-18 19:44:36 +00:00
}
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 ;
}
2006-08-03 01:02:51 +00:00
/**
* Rebuild the database cache of module files .
2006-08-03 07:06:36 +00:00
*
2006-08-03 01:02:51 +00:00
* @ return
* The array of filesystem objects used to rebuild the cache .
*/
function module_rebuild_cache () {
// Get current list of modules
2006-10-23 06:45:17 +00:00
$files = drupal_system_listing ( '\.module$' , 'modules' , 'name' , 0 );
2006-08-03 01:02:51 +00:00
// Extract current files from database.
system_get_files_database ( $files , 'module' );
ksort ( $files );
2007-04-17 07:19:39 +00:00
// Set defaults for module info
$defaults = array (
'dependencies' => array (),
'dependents' => array (),
'description' => '' ,
2007-06-27 17:54:49 +00:00
'version' => NULL ,
'php' => DRUPAL_MINIMUM_PHP ,
2007-04-17 07:19:39 +00:00
);
2006-08-03 01:02:51 +00:00
foreach ( $files as $filename => $file ) {
2007-04-17 07:19:39 +00:00
// Look for the info file.
$file -> info = drupal_parse_info_file ( dirname ( $file -> filename ) . '/' . $file -> name . '.info' );
2006-08-31 20:22:37 +00:00
// Skip modules that don't provide info.
if ( empty ( $file -> info )) {
unset ( $files [ $filename ]);
continue ;
}
2007-04-17 07:19:39 +00:00
// Merge in defaults and save.
$files [ $filename ] -> info = $file -> info + $defaults ;
2006-08-03 01:02:51 +00:00
2007-06-27 21:59:33 +00:00
// Invoke hook_system_info_alter() to give installed modules a chance to
// modify the data in the .info files if necessary.
drupal_alter ( 'system_info' , $files [ $filename ] -> info , $files [ $filename ]);
2007-04-17 07:19:39 +00:00
// Log the critical hooks implemented by this module.
2006-08-03 01:02:51 +00:00
$bootstrap = 0 ;
foreach ( bootstrap_hooks () as $hook ) {
if ( module_hook ( $file -> name , $hook )) {
$bootstrap = 1 ;
break ;
}
}
// Update the contents of the system table:
if ( isset ( $file -> status ) || ( isset ( $file -> old_filename ) && $file -> old_filename != $file -> filename )) {
2007-08-08 20:04:38 +00:00
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 );
2006-08-03 01:02:51 +00:00
}
else {
// This is a new module.
2007-01-31 15:49:26 +00:00
$files [ $filename ] -> status = 0 ;
$files [ $filename ] -> throttle = 0 ;
2007-08-08 20:04:38 +00:00
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 );
2006-08-03 01:02:51 +00:00
}
}
2007-12-13 10:46:43 +00:00
$files = _module_build_dependencies ( $files );
2006-10-02 16:49:08 +00:00
return $files ;
}
2006-08-03 01:02:51 +00:00
2006-10-02 16:49:08 +00:00
/**
2007-12-13 10:46:43 +00:00
* Find dependencies any level deep and fill in dependents information too .
2006-10-02 16:49:08 +00:00
*
2007-12-13 10:46:43 +00:00
* If module A depends on B which in turn depends on C then this function will
* add C to the list of modules A depends on . This will be repeated until
* module A has a list of all modules it depends on . If it depends on itself ,
* called a circular dependency , that ' s marked by adding a nonexistent module ,
* called - circular - to this list of modules . Because this does not exist ,
* it ' ll be impossible to switch module A on .
*
* Also we fill in a dependents array in $file -> info . Using the names above ,
* the dependents array of module B lists A .
*
* @ param $files
* The array of filesystem objects used to rebuild the cache .
2006-10-02 16:49:08 +00:00
* @ return
2007-12-13 10:46:43 +00:00
* The same array with dependencies and dependents added where applicable .
2006-10-02 16:49:08 +00:00
*/
2007-12-13 10:46:43 +00:00
function _module_build_dependencies ( $files ) {
do {
$new_dependency = FALSE ;
foreach ( $files as $filename => $file ) {
// We will modify this object (module A, see doxygen for module A, B, C).
$file = & $files [ $filename ];
if ( isset ( $file -> info [ 'dependencies' ]) && is_array ( $file -> info [ 'dependencies' ])) {
foreach ( $file -> info [ 'dependencies' ] as $dependency_name ) {
// This is a nonexistent module.
if ( $dependency_name == '-circular-' ) {
continue ;
}
// $dependency_name is module B (again, see doxygen).
$files [ $dependency_name ] -> info [ 'dependents' ][ $filename ] = $filename ;
$dependency = $files [ $dependency_name ];
if ( isset ( $dependency -> info [ 'dependencies' ]) && is_array ( $dependency -> info [ 'dependencies' ])) {
// Let's find possible C modules.
foreach ( $dependency -> info [ 'dependencies' ] as $candidate ) {
if ( array_search ( $candidate , $file -> info [ 'dependencies' ]) === FALSE ) {
// Is this a circular dependency?
if ( $candidate == $filename ) {
// As a module name can not contain dashes, this makes
// impossible to switch on the module.
$candidate = '-circular-' ;
// Do not display the message or add -circular- more than once.
if ( array_search ( $candidate , $file -> info [ 'dependencies' ]) !== FALSE ) {
continue ;
}
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' );
}
else {
// We added a new dependency to module A. The next loop will
// be able to use this as "B module" thus finding even
// deeper dependencies.
$new_dependency = TRUE ;
}
$file -> info [ 'dependencies' ][] = $candidate ;
}
}
2006-10-02 16:49:08 +00:00
}
}
}
2007-12-13 10:46:43 +00:00
// Don't forget to break the reference.
unset ( $file );
2006-10-02 16:49:08 +00:00
}
2007-12-13 10:46:43 +00:00
} while ( $new_dependency );
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 ) .
* @ 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 ();
2004-07-14 20:42:20 +00:00
return array_key_exists ( $module , $list );
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
include_once './includes/install.inc' ;
2007-05-25 12:46:46 +00:00
module_load_include ( 'install' , $module );
}
/**
* Load a module include file .
*
* @ param $type
* The include file ' s type ( file extension ) .
* @ param $module
* The module to which the include file belongs .
* @ param $name
* Optionally , specify the file name . If not set , the module ' s name is used .
*/
function module_load_include ( $type , $module , $name = NULL ) {
if ( empty ( $name )) {
$name = $module ;
}
$file = './' . drupal_get_path ( 'module' , $module ) . " / $name . $type " ;
if ( is_file ( $file )) {
require_once $file ;
}
else {
return FALSE ;
}
}
/**
* 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
}
}
/**
2006-11-16 08:28:08 +00:00
* Enable a given list of modules .
2006-08-03 01:02:51 +00:00
*
2006-11-16 08:28:08 +00:00
* @ param $module_list
* An array of module names .
2006-08-03 01:02:51 +00:00
*/
2006-11-16 08:28:08 +00:00
function module_enable ( $module_list ) {
$invoke_modules = array ();
foreach ( $module_list as $module ) {
2007-08-22 08:36:34 +00:00
$existing = db_fetch_object ( db_query ( " SELECT status FROM { system} WHERE type = '%s' AND name = '%s' " , 'module' , $module ));
2007-08-08 07:22:03 +00:00
if ( $existing -> status == 0 ) {
2006-11-16 08:28:08 +00:00
module_load_install ( $module );
2007-08-22 08:36:34 +00:00
db_query ( " UPDATE { system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s' " , 1 , 0 , 'module' , $module );
2006-11-16 08:28:08 +00:00
drupal_load ( 'module' , $module );
$invoke_modules [] = $module ;
}
2006-08-03 01:02:51 +00:00
}
2006-11-16 08:28:08 +00:00
if ( ! empty ( $invoke_modules )) {
// Refresh the module list to include the new enabled module.
module_list ( TRUE , FALSE );
// Force to regenerate the stored list of hook implementations.
module_implements ( '' , FALSE , TRUE );
}
foreach ( $invoke_modules as $module ) {
module_invoke ( $module , 'enable' );
2007-09-02 14:42:30 +00:00
// Check if node_access table needs rebuilding.
2007-12-08 15:15:25 +00:00
// We check for the existence of node_access_needs_rebuild() since
// at install time, module_enable() could be called while node.module
// is not enabled yet.
if ( function_exists ( 'node_access_needs_rebuild' ) && ! node_access_needs_rebuild () && module_hook ( $module , 'node_grants' )) {
2007-09-02 14:42:30 +00:00
node_access_needs_rebuild ( 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 .
2006-08-03 01:02:51 +00:00
*/
2006-11-27 23:15:41 +00:00
function module_disable ( $module_list ) {
$invoke_modules = array ();
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' );
2007-08-22 08:36:34 +00:00
db_query ( " UPDATE { system} SET status = %d, throttle = %d WHERE type = '%s' AND name = '%s' " , 0 , 0 , 'module' , $module );
2006-11-27 23:15:41 +00:00
$invoke_modules [] = $module ;
}
2006-08-03 01:02:51 +00:00
}
2006-11-27 23:15:41 +00:00
if ( ! empty ( $invoke_modules )) {
// Refresh the module list to exclude the disabled modules.
module_list ( TRUE , FALSE );
// Force to regenerate the stored list of hook implementations.
module_implements ( '' , FALSE , TRUE );
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
* 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 .
*
* 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 call that hook in all enabled modules that implement it .
*
* 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
* the module name is 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 () .
*/
/**
* 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 " ) .
* @ return
* TRUE if the module is both installed and enabled , and the hook is
* implemented in that module .
*/
function module_hook ( $module , $hook ) {
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
* The name of the hook ( e . g . " help " or " menu " ) .
2006-02-27 15:04:45 +00:00
* @ param $sort
* By default , modules are ordered by weight and filename , settings this option
* to TRUE , module list will be ordered by module name .
2006-11-27 23:15:41 +00:00
* @ param $refresh
* 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 ) .
2005-01-16 18:44:49 +00:00
* @ return
* An array with the names of the modules which are implementing this hook .
*/
2006-11-27 23:15:41 +00:00
function module_implements ( $hook , $sort = FALSE , $refresh = FALSE ) {
2005-01-16 18:44:49 +00:00
static $implementations ;
2006-11-27 23:15:41 +00:00
if ( $refresh ) {
2007-07-18 14:10:14 +00:00
$implementations = array ();
2006-11-27 23:15:41 +00:00
return ;
}
2005-01-16 18:44:49 +00:00
if ( ! isset ( $implementations [ $hook ])) {
$implementations [ $hook ] = array ();
2006-02-27 15:04:45 +00:00
$list = module_list ( FALSE , TRUE , $sort );
2005-01-16 18:44:49 +00:00
foreach ( $list as $module ) {
if ( module_hook ( $module , $hook )) {
$implementations [ $hook ][] = $module ;
}
}
}
2006-05-07 00:08:36 +00:00
// The explicit cast forces a copy to be made. This is needed because
2005-12-14 18:46:21 +00:00
// $implementations[$hook] is only a reference to an element of
// $implementations and if there are nested foreaches (due to nested node
// API calls, for example), they would both manipulate the same array's
// references, which causes some modules' hooks not to be called.
// See also http://www.zend.com/zend/art/ref-count.php.
return ( array ) $implementations [ $hook ];
2005-01-16 18:44:49 +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 .
* @ 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 ]);
2004-07-14 20:42:20 +00:00
$function = $module . '_' . $hook ;
2005-03-01 20:23:35 +00:00
if ( module_hook ( $module , $hook )) {
return call_user_func_array ( $function , $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 .
* @ 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 ) {
$function = $module . '_' . $hook ;
$result = call_user_func_array ( $function , $args );
2005-12-14 20:10:45 +00:00
if ( isset ( $result ) && is_array ( $result )) {
2007-11-30 09:16:02 +00:00
$return = array_merge_recursive ( $return , $result );
2004-07-14 20:42:20 +00:00
}
2004-08-22 17:03:42 +00:00
else if ( isset ( $result )) {
$return [] = $result ;
}
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 () {
2007-04-10 10:10:27 +00:00
return array ( 'block' , 'filter' , 'node' , 'system' , 'user' );
2007-02-04 21:20:50 +00:00
}