Index: includes/module.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/module.inc,v retrieving revision 1.177 diff -u -p -r1.177 module.inc --- includes/module.inc 28 Dec 2009 10:48:51 -0000 1.177 +++ includes/module.inc 30 Dec 2009 15:36:53 -0000 @@ -89,31 +89,36 @@ function module_list($refresh = FALSE, $ * * @param $type * The type of list to return: + * - module: All modules. * - module_enabled: All enabled modules. * - bootstrap: All enabled modules required for bootstrap. * - theme: All themes. + * - theme_enabled: All enabled themes. * * @return * An associative array of modules or themes, keyed by name, and having the - * respective database row as value. For $type 'module_enabled' and - * 'bootstrap', the array values equal the keys. + * respective database row as value. For $type 'module_enabled', + * 'theme_enabled', and 'bootstrap', the array values equal the keys. * * @see module_list() * @see list_themes() */ function system_list($type) { - $lists = &drupal_static(__FUNCTION__); + $lists = &drupal_static(__FUNCTION__, array()); // 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')) { + if (isset($lists[$type])) { + return $lists[$type]; + } + elseif ($cached = cache_get('system_list:bootstrap', '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'); - cache_set('bootstrap_modules', $bootstrap_list, 'cache_bootstrap'); + cache_set('system_list:bootstrap', $bootstrap_list, 'cache_bootstrap'); } // To avoid a separate database lookup for the filepath, prime the // drupal_get_filename() static cache for bootstrap modules only. @@ -123,49 +128,77 @@ function system_list($type) { } // We only return the module names here since module_list() doesn't need // the filename itself. - $lists['bootstrap'] = array_keys($bootstrap_list); + $lists[$type] = array_keys($bootstrap_list); } // Otherwise build the list for enabled modules and themes. - elseif (!isset($lists['module_enabled'])) { - if ($cached = cache_get('system_list', 'cache_bootstrap')) { + elseif (!isset($lists[$type])) { + // These types are special-cased, because they are required for each full bootstrap. + if ($type == 'module_enabled' || $type == 'theme') { + if ($cached = cache_get('system_list:bootstrap_full', 'cache_bootstrap')) { + $lists['module_enabled'] = $cached->data['module_enabled']; + $lists['theme'] = $cached->data['theme']; + $lists['filepaths'] = $cached->data['filepaths']; + } + } + elseif ($cached = cache_get('system_list:all', '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(). - $result = db_query("SELECT * FROM {system} ORDER BY weight ASC, name ASC"); - foreach ($result as $record) { - if ($record->type == 'module' && $record->status) { - // Build a list of all enabled modules. - $lists['module_enabled'][$record->name] = $record->name; + // Rebuild the lists if there was no cache hit. + if (!isset($lists[$type])) { + // During normal site operation, we can query the database. + try { + // 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(). + $result = db_query("SELECT * FROM {system} ORDER BY weight ASC, name ASC"); + foreach ($result as $record) { + // Unserialize the 'info' field before saving into lists. + $record->info = unserialize($record->info); + + // Build a generic list for each {system}.type. + $lists[$record->type][$record->name] = $record; + + if ($record->status) { + // Build a list of enabled types. + $lists[$record->type . '_enabled'][$record->name] = $record->name; + + // Build a list of filenames so drupal_get_filename() can use it. + $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename); + } } - // Build a list of themes. - if ($record->type == 'theme') { - $lists['theme'][$record->name] = $record; + + // Cache the lists separately, but only if we have results. + if (isset($lists[$type])) { + cache_set('system_list:bootstrap_full', array('module_enabled' => $lists['module_enabled'], 'theme' => $lists['theme'], 'filepaths' => $lists['filepaths']), 'cache_bootstrap'); + cache_set('system_list:all', $lists, 'cache_bootstrap'); } - // 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); + } + // Fall back to scanning the file system for modules and themes. + catch (Exception $e) { + $types = array('modules' => 'module', 'themes' => 'theme'); + foreach ($types as $dir => $file_type) { + $files = drupal_system_listing('/\.info$/', $dir, array('key' => 'name')); + foreach ($files as $name => $file) { + $file->type = $file_type; + $file->info = drupal_parse_info_file($file->uri); + $lists[$file->type][$file->name] = $file; + } } } - cache_set('system_list', $lists, 'cache_bootstrap'); } + // To avoid a separate database lookup for the filepath, prime the // 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']); + if (($type == 'module_enabled' || $type == 'theme') && isset($lists['filepaths'])) { + foreach ($lists['filepaths'] as $item) { + drupal_get_filename($item['type'], $item['name'], $item['filepath']); + } } } - return $lists[$type]; + return isset($lists[$type]) ? $lists[$type] : array(); } /** @@ -173,8 +206,72 @@ function system_list($type) { */ function system_list_reset() { drupal_static_reset('system_list'); - cache_clear_all('bootstrap_modules', 'cache_bootstrap'); - cache_clear_all('system_list', 'cache_bootstrap'); + cache_clear_all('system_list', 'cache_bootstrap', TRUE); +} + +/** + * Return a list of modules or themes defining a certain .info file property value. + * + * This function builds a list of all modules that are currently registered, + * including disabled modules. It can be used to easily find all modules that + * define a certain .info file property. For example: + * @code + * system_list_by_info('module', array('required'), TRUE); + * @endcode + * would list all required Drupal core modules. + * + * @code + * system_list_by_info('module_enabled', array('dependencies'), 'taxonomy'); + * @endcode + * would list all modules that require Taxonomy module. + * + * @param $type + * The type of system object to list, either 'module' or 'theme'. + * @param $properties + * A list of .info file property keys to search $value in, for example + * array('hidden'). + * @param $value + * The value to search for, for example 'TRUE'. If the last parent key defined + * in $parents is an array (such as the 'dependencies' definition), then + * $value is searched in the array of values defined by a module. + * @param $simple + * (optional) Boolean whether to return human-readable module names as values + * (TRUE) or fully populated module objects including .info file information + * as values (FALSE). Defaults to TRUE. + * + * @return + * An associative array containing the machine-readable module name as key, + * and the human-readable module name as value if $simple is TRUE, or a fully + * populated module object if $simple is FALSE. + * + * @see system_list() + */ +function system_list_by_info($type, array $properties, $value, $simple = TRUE) { + $list = array(); + $result = system_list($type); + foreach ($result as $name => $object) { + $info = $object->info; + foreach ($properties as $property) { + // If the (next) property is not found, skip to the next module. + if (!isset($info[$property])) { + continue 2; + } + $info = $info[$property]; + } + // Check whether $info is an array first. + if (is_array($info)) { + if (in_array($value, $info)) { + $list[$name] = ($simple ? $object->info['name'] : $object); + } + } + // Otherwise, look whether $info matches $value. + else { + if ($info == $value) { + $list[$name] = ($simple ? $object->info['name'] : $object); + } + } + } + return $list; } /** @@ -634,18 +731,14 @@ function module_invoke_all() { * Array of modules required by core. */ function drupal_required_modules() { - $files = drupal_system_listing('/\.info$/', 'modules', 'name', 0); $required = array(); // An install profile is required and one must always be loaded. $required[] = drupal_get_profile(); - foreach ($files as $name => $file) { - $info = drupal_parse_info_file($file->uri); - if (!empty($info) && !empty($info['required']) && $info['required']) { - $required[] = $name; - } - } + // Include any modules that have required = TRUE in their .info files. + $modules = system_list_by_info('module', array('required'), TRUE); + $required = array_merge($required, array_keys($modules)); return $required; } Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.559 diff -u -p -r1.559 theme.inc --- includes/theme.inc 28 Dec 2009 20:58:08 -0000 1.559 +++ includes/theme.inc 30 Dec 2009 13:59:56 -0000 @@ -573,7 +573,6 @@ function list_themes($refresh = FALSE) { if (db_is_active() && !defined('MAINTENANCE_MODE')) { foreach (system_list('theme') as $theme) { if (file_exists($theme->filename)) { - $theme->info = unserialize($theme->info); $themes[] = $theme; } }