diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index c81ccf1..eac1995 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -890,7 +890,7 @@ function drupal_get_filename($type, $name, $filename = NULL) { // because this function is called during installation. if (drupal_container()->hasDefinition('keyvalue') && function_exists('db_query')) { try { - $file_list = state()->get('system.' . $type . '_file_list'); + $file_list = state()->get('system.' . $type . '.files'); if ($file_list && isset($file_list[$name]) && file_exists(DRUPAL_ROOT . '/' . $file_list[$name])) { $files[$type][$name] = $file_list[$name]; } @@ -901,7 +901,6 @@ function drupal_get_filename($type, $name, $filename = NULL) { // completely. } } - // Fallback to searching the filesystem if the database could not find the // file or the file returned by the database is not found. if (!isset($files[$type][$name])) { @@ -2101,15 +2100,14 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) { ); // Not drupal_static(), because the only legitimate API to control this is to // call drupal_bootstrap() with a new phase parameter. - static $final_phase = -1; + static $final_phase; // Not drupal_static(), because it's impossible to roll back to an earlier // bootstrap state. static $stored_phase = -1; - // When not recursing, store the phase name so it's not forgotten during - // recursion. Additionally, ensure that $final_phase is never rolled back to an - // earlier bootstrap state. - if ($new_phase && $phase > $final_phase) { + // When not recursing, store the phase name so it's not forgotten while + // recursing. + if ($new_phase) { $final_phase = $phase; } if (isset($phase)) { @@ -2390,7 +2388,7 @@ function _drupal_bootstrap_page_header() { * @see drupal_bootstrap() */ function drupal_get_bootstrap_phase() { - return drupal_bootstrap(NULL, FALSE); + return drupal_bootstrap(); } /** diff --git a/core/includes/common.inc b/core/includes/common.inc index df744b3..ef63042 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -6830,6 +6830,11 @@ function drupal_flush_all_caches() { // actually loaded. module_load_all(); + // Update the list of bootstrap modules. + // Allows developers to get new hook_boot() implementations registered without + // having to write a hook_update_N() function. + _system_update_bootstrap_status(); + // Rebuild the schema and cache a fully-built schema based on new module data. // This is necessary for any invocation of index.php, because setting cache // table entries requires schema information and that occurs during bootstrap diff --git a/core/includes/install.inc b/core/includes/install.inc index 860ac3d..89ee357 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -413,14 +413,13 @@ function drupal_install_system() { drupal_container() ->get('keyvalue') ->get('system.module.schema') - ->set('system', max($system_versions)); + ->set('system', max($system_versions)); } - config('system.module') + config('system.module') ->set('system', 0) ->save(); - // Clear out module list and hook implementation statics before calling - // system_rebuild_theme_data(). + // Clear out module list and hook implementation statics. system_list_reset(); module_list_reset(); module_implements_reset(); diff --git a/core/includes/module.inc b/core/includes/module.inc index 30fa096..eea8088 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -15,14 +15,24 @@ * Whether to load only the reduced set of modules loaded in "bootstrap mode" * for cached pages. See bootstrap.inc. Pass NULL to only check the current * status without loading of modules. + * @param bool $reset + * (optional) Internal use only. Whether to reset the internal statically + * cached flag of whether modules have been loaded. If TRUE, all modules are + * (re)loaded in the same call. Used by the testing framework to override and + * persist a limited module list for the duration of a unit test (in which no + * module system exists). * * @return bool * A Boolean indicating whether all modules have been loaded. This means all * modules; the load status of bootstrap modules cannot be checked. */ -function module_load_all($bootstrap = FALSE) { +function module_load_all($bootstrap = FALSE, $reset = FALSE) { static $has_run = FALSE; + if ($reset) { + $has_run = FALSE; + } + // Unless $boostrap is NULL, load the requested set of modules. if (isset($bootstrap) && !$has_run) { $type = $bootstrap ? 'bootstrap' : 'module_enabled'; @@ -135,20 +145,22 @@ function system_list($type) { return $lists['bootstrap']; } if ($cached = cache('bootstrap')->get('bootstrap_modules')) { - $lists['bootstrap'] = $cached->data; - // To avoid a separate database lookup for the filepath, prime the - // drupal_get_filename() static cache for bootstrap modules only. - // The rest is stored separately to keep the bootstrap module cache small. - foreach ($lists['bootstrap'] as $module => $filename) { - _system_list_warm('module', $module, $filename); - } + $bootstrap_list = $cached->data; } else { - $lists = _system_list_prepare(); + $bootstrap_list = state()->get('system.bootstrap_modules') ?: array(); + cache('bootstrap')->set('bootstrap_modules', $bootstrap_list); + } + // To avoid a separate database lookup for the filepath, prime the + // drupal_get_filename() static cache for bootstrap modules only. + // The rest is stored separately to keep the bootstrap module cache small. + foreach ($bootstrap_list as $module) { + drupal_classloader_register($module->name, dirname($module->filename)); + 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($lists['bootstrap']); + $lists['bootstrap'] = array_keys($bootstrap_list); } // Otherwise build the list for enabled modules and themes. elseif (!isset($lists['module_enabled'])) { @@ -156,130 +168,93 @@ function system_list($type) { $lists = $cached->data; } else { - $lists = _system_list_prepare(); - $lists['bootstrap'] = array_keys($lists['bootstrap']); - } - foreach ($lists['filepaths'] as $item) { - _system_list_warm($item['type'], $item['name'], $item['filepath']); - } - } - - return $lists[$type]; -} - -/** - * Prepares the system list data structure. - * - * @return - * An associative array of modules and themes, keyed by list type: bootstrap, - * module_enabled, theme, filepaths. - */ -function _system_list_prepare() { - // 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(). - $enabled_themes = config('system.theme')->get(); - $enabled_modules = config('system.module')->get(); - _system_list_warm('module', 'system', 'core/modules/system.module', TRUE); - - $module_file_list = state()->get('system.module_file_list'); - $theme_data = state()->get('system.themes'); - // As themes do not need to be installed to be used there is an issue during - // installation. The state entry for system.themes does not exist and we need - // theme data in order for installation to work. This will be resolved by - // http://drupal.org/node/1067408. - if (empty($theme_data)) { - $theme_data = system_rebuild_theme_data(FALSE); - } - $lists = array( - 'bootstrap' => array(), - 'module_enabled' => array(), - 'theme' => array(), - 'filepaths' => array(), - ); - foreach ($theme_data as $key => $theme) { - $lists['theme'][$key] = $theme; - $status = isset($enabled_themes[$key]); - $lists['theme'][$key]->status = $status; - $lists['theme'][$key]->name = $key; - // Build a list of filenames so drupal_get_filename can use it. - if ($status) { - $lists['filepaths'][] = array( - 'type' => 'theme', - 'name' => $key, - 'filepath' => $theme->filename + $lists = array( + 'module_enabled' => array(), + 'theme' => array(), + 'filepaths' => array(), ); - } - } - foreach ($lists['theme'] as $key => $theme) { - if (!empty($theme->info['base theme'])) { - // Make a list of the theme's base themes. - require_once DRUPAL_ROOT . '/core/includes/theme.inc'; - $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key); - // Don't proceed if there was a problem with the root base theme. - if (!current($lists['theme'][$key]->base_themes)) { - continue; - } - // Determine the root base theme. - $base_key = key($lists['theme'][$key]->base_themes); - // Add to the list of sub-themes for each of the theme's base themes. - foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) { - $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name']; + // 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(). + $modules = config('system.module')->get(); + $module_files = state()->get('system.module.files'); + foreach ($modules as $name => $weight) { + // Build a list of all enabled modules. + $lists['module_enabled'][$name] = $name; + // Build a list of filenames so drupal_get_filename can use it. + $lists['filepaths'][] = array( + 'type' => 'module', + 'name' => $name, + 'filepath' => $module_files[$name], + ); } - // Add the base theme's theme engine info. - $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_key]->info['engine']; - } - else { - // A plain theme is its own base theme. - $base_key = $key; - } - // Set the theme engine prefix. - $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; - } - foreach ($enabled_modules as $name => $weight) { - // Build a list of all enabled modules. - $lists['module_enabled'][$name] = $name; - $filename = $module_file_list[$name]; - // Build a list of filenames so drupal_get_filename can use it. - $lists['filepaths'][] = array( - 'type' => 'module', - 'name' => $name, - 'filepath' => $filename, - ); - _system_list_warm('module', $name, $filename, TRUE); - foreach (bootstrap_hooks() as $hook) { - if (function_exists($name .'_' . $hook)) { - $lists['bootstrap'][$name] = $filename; + // Build a list of themes. + // Themes include all themes, including disabled and not installed ones. + // @see http://drupal.org/node/1067408 + /* + $themes = config('system.theme')->get(); + $theme_files = state()->get('system.theme.files'); + */ + $theme_data = state()->get('system.theme.data'); + if (empty($theme_data)) { + $theme_data = system_rebuild_theme_data(); + state()->set('system.theme.data', $theme_data); } + foreach ($theme_data as $key => $theme) { + $lists['theme'][$key] = $theme; + $status = isset($enabled_themes[$key]); + $lists['theme'][$key]->status = $status; + $lists['theme'][$key]->name = $key; + // Build a list of filenames so drupal_get_filename can use it. + if ($status) { + $lists['filepaths'][] = array( + 'type' => 'theme', + 'name' => $key, + 'filepath' => $theme->filename, + ); + } + } + // @todo Move into list_themes(). Read info for a *particular* requested + // theme from state instead. + foreach ($lists['theme'] as $key => $theme) { + if (!empty($theme->info['base theme'])) { + // Make a list of the theme's base themes. + require_once DRUPAL_ROOT . '/core/includes/theme.inc'; + $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key); + // Don't proceed if there was a problem with the root base theme. + if (!current($lists['theme'][$key]->base_themes)) { + continue; + } + // Determine the root base theme. + $base_key = key($lists['theme'][$key]->base_themes); + // Add to the list of sub-themes for each of the theme's base themes. + foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) { + $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name']; + } + // Add the base theme's theme engine info. + $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_key]->info['engine']; + } + else { + // A plain theme is its own base theme. + $base_key = $key; + } + // Set the theme engine prefix. + $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; + } + cache('bootstrap')->set('system_list', $lists); + } + // 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']); + drupal_classloader_register($item['name'], dirname($item['filepath'])); } } - cache('bootstrap')->set('system_list', $lists); - cache('bootstrap')->set('bootstrap_modules', $lists['bootstrap']); - - return $lists; -} -/** - * Prepares a module for loading and optionally calls drupal_load(). - * - * @param string $type - * The type of the extension (i.e. theme, theme_engine, module, profile). - * @param string $name - * The name of the extension. - * @param string $filename - * The filename of the extension. - * @param bool $load - * (optional) Call drupal_load() for the extension. Defaults to FALSE. - */ -function _system_list_warm($type, $name, $filename, $load = FALSE) { - drupal_classloader_register($name, dirname($filename)); - drupal_get_filename($type, $name, $filename); - if ($load) { - drupal_load($type, $name); - } + return $lists[$type]; } /** @@ -445,28 +420,27 @@ function module_load_all_includes($type, $name = NULL) { * @see hook_modules_enabled() */ function module_enable($module_list, $enable_dependencies = TRUE) { - // Get all module data so we can find dependencies, sort and copy to the - // installed modules config. - $all_modules = system_rebuild_module_data(); 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($all_modules[$module])) { + if (!isset($module_data[$module])) { // This module is not found in the filesystem, abort. return FALSE; } - if ($all_modules[$module]->status) { + if ($module_data[$module]->status) { // Skip already enabled modules. unset($module_list[$module]); continue; } - $module_list[$module] = $all_modules[$module]->sort; + $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 (array_keys($all_modules[$module]->requires) as $dependency) { + foreach (array_keys($module_data[$module]->requires) as $dependency) { if (!isset($module_list[$dependency])) { $module_list[$dependency] = 0; } @@ -489,9 +463,9 @@ function module_enable($module_list, $enable_dependencies = TRUE) { $modules_installed = array(); $modules_enabled = array(); $schema_store = drupal_container()->get('keyvalue')->get('system.module.schema'); + $config = config('system.module'); + $disabled_config = config('system.module.disabled'); foreach ($module_list as $module) { - $config = config('system.module'); - $disabled_config = config('system.module.disabled'); // Only process modules that are not already enabled. $enabled = TRUE; $weight = 0; @@ -519,6 +493,7 @@ function module_enable($module_list, $enable_dependencies = TRUE) { // Refresh the module list to include it. system_list_reset(); module_implements_reset(); + _system_update_bootstrap_status(); // Refresh the schema to include it. drupal_get_schema(NULL, TRUE); // Update the theme registry to include it. @@ -624,17 +599,15 @@ function module_disable($module_list, $disable_dependents = TRUE) { $invoke_modules = array(); + $config = config('system.module'); + $disabled_config = config('system.module.disabled'); foreach ($module_list as $module) { if (module_exists($module)) { - $config = config('system.module'); - $disabled_config = config('system.module.disabled'); module_load_install($module); module_invoke($module, 'disable'); - if ($weight = $config->get($module)) { - $disabled_config - ->set($module, $weight) - ->save(); - } + $disabled_config + ->set($module, $config->get($module)) + ->save(); $config ->clear($module) ->setData(module_config_sort($config->get())) @@ -652,6 +625,7 @@ function module_disable($module_list, $disable_dependents = TRUE) { // Invoke hook_modules_disabled before disabling modules, // so we can still call module hooks to get information. module_invoke_all('modules_disabled', $invoke_modules); + _system_update_bootstrap_status(); // Update the theme registry to remove the newly-disabled module. drupal_theme_rebuild(); } diff --git a/core/includes/schema.inc b/core/includes/schema.inc index b9ec5ea..58aa7c4 100644 --- a/core/includes/schema.inc +++ b/core/includes/schema.inc @@ -175,7 +175,7 @@ function drupal_get_installed_schema_version($module, $reset = FALSE, $array = F if (!$versions) { if (!$versions = drupal_container()->get('keyvalue')->get('system.module.schema')->getAll()) { - $versions= array(); + $versions = array(); } } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index d57b323..2195e64 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1412,19 +1412,19 @@ function theme_render_template($template_file, $variables) { */ function theme_enable($theme_list) { drupal_clear_css_cache(); - $config = config('system.theme'); + $enabled_themes = config('system.theme'); + $disabled_themes = config('system.theme.disabled'); foreach ($theme_list as $key) { - // The value is not used, it needs to be consistent with - // config('system.module') which uses the value for weight so set all of - // them to the same value. 1 looks like 'enabled' so let's use that. - $config->set($key, 1); + // The value is not used; the weight is ignored for themes currently. + $enabled_themes->set($key, 0); + $disabled_themes->clear($key); // Install default configuration of the theme. config_install_default_config('theme', $key); } - $config->save(); + $enabled_themes->save(); + $disabled_themes->save(); - system_list_reset(); - system_rebuild_theme_data(TRUE); + list_themes(TRUE); menu_router_rebuild(); drupal_theme_rebuild(); @@ -1449,11 +1449,15 @@ function theme_disable($theme_list) { drupal_clear_css_cache(); - $config = config('system.theme'); + $enabled_themes = config('system.theme'); + $disabled_themes = config('system.theme.disabled'); foreach ($theme_list as $key) { - $config->clear($key); + // The value is not used; the weight is ignored for themes currently. + $enabled_themes->clear($key); + $disabled_themes->set($key, 0); } - $config->save(); + $enabled_themes->save(); + $disabled_themes->save(); list_themes(TRUE); menu_router_rebuild(); diff --git a/core/includes/update.inc b/core/includes/update.inc index e36b9ef..0f0f391 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -413,9 +413,11 @@ function update_module_enable(array $modules) { } } // Enable the module with a weight of 0. + // @todo Missing sorting. config('system.module') ->set($module, 0) ->save(); + config('system.module.disabled')->clear($module)->save(); // Change the schema version from SCHEMA_UNINSTALLED to 0, so any module // updates since the module's inception are executed in a core upgrade. $store->set($module, 0); @@ -953,7 +955,7 @@ function update_retrieve_dependencies() { $return = array(); // Get a list of installed modules, arranged so that we invoke their hooks in // the same order that module_invoke_all() does. - foreach (config('system.module')->get() as $module => $data) { + foreach (config('system.module')->get() as $module => $data) { $function = $module . '_update_dependencies'; if (function_exists($function)) { $result = $function(); diff --git a/core/modules/action/lib/Drupal/action/Tests/LoopTest.php b/core/modules/action/lib/Drupal/action/Tests/LoopTest.php index fce4f2d..9f3a959 100644 --- a/core/modules/action/lib/Drupal/action/Tests/LoopTest.php +++ b/core/modules/action/lib/Drupal/action/Tests/LoopTest.php @@ -67,6 +67,7 @@ protected function triggerActions() { $expected[] = 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.'; $result = db_query("SELECT message FROM {watchdog} WHERE type = 'action_loop_test' OR type = 'action' ORDER BY wid"); + $loop_started = FALSE; foreach ($result as $row) { $expected_message = array_shift($expected); $this->assertEqual($row->message, $expected_message, format_string('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message))); diff --git a/core/modules/block/block.install b/core/modules/block/block.install index 5ad23aa..ee72e0f 100644 --- a/core/modules/block/block.install +++ b/core/modules/block/block.install @@ -213,6 +213,7 @@ function block_schema() { * Implements hook_install(). */ function block_install() { + // Block should go first so that other modules can alter its output // during hook_page_alter(). Almost everything on the page is a block, // so before block module runs, there will not be much to alter. diff --git a/core/modules/help/lib/Drupal/help/Tests/HelpTest.php b/core/modules/help/lib/Drupal/help/Tests/HelpTest.php index 260768a..7d5d20e 100644 --- a/core/modules/help/lib/Drupal/help/Tests/HelpTest.php +++ b/core/modules/help/lib/Drupal/help/Tests/HelpTest.php @@ -108,10 +108,8 @@ protected function verifyHelp($response = 200) { protected function getModuleList() { $modules = array(); $module_data = system_rebuild_module_data(); - foreach (config('system.module')->get() as $module => $data) { - if (file_exists($module_data[$module]->filename) && function_exists($module . '_help')) { - $modules[$module] = $module_data[$module]->info['name']; - } + foreach (module_implements('help') as $module) { + $modules[$module] = $module_data[$module]->info['name']; } return $modules; } diff --git a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php index ee40928..77aa4dd 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleLookup.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleLookup.php @@ -36,16 +36,7 @@ public function __construct($langcode, $context) { // Add the current user's role IDs to the cache key, this ensures that, for // example, strings for admin menu items and settings forms are not cached // for anonymous users. - if (isset($GLOBALS['user'])) { - $rids = implode(':', array_keys($GLOBALS['user']->roles)); - } - else { - // If there is no global user, surely it's an anonymous user. For - // example, during update install_ensure_config_directory() calls for - // stream wrappers which calls locale_stream_wrappers() which has t() - // calls in it. - $rids = DRUPAL_ANONYMOUS_RID; - } + $rids = implode(':', array_keys($GLOBALS['user']->roles)); parent::__construct("locale:$langcode:$context:$rids", 'cache', array('locale' => TRUE)); } diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php index 323c55d..112fee8 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php @@ -77,8 +77,6 @@ function testLocaleCompare() { // Return the locale test modules back to their hidden state. variable_del('locale_translation_test_system_info_alter'); - // Reset system lists to reflect changes. - system_list_reset(); } } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index a10324e..aeff65e 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -831,6 +831,7 @@ protected function tearDown() { // Reset module list and module load status. module_list_reset(); + module_load_all(FALSE, TRUE); // Restore original in-memory configuration. $conf = $this->originalConf; diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 71311af..016ffd0 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -9,6 +9,7 @@ use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; +use Drupal\Core\Database\ConnectionNotDefinedException; use PDO; use stdClass; use DOMDocument; @@ -1065,8 +1066,7 @@ protected function drupalGet($path, array $options = array(), array $headers = a // options set, it might change the GET into a POST. Make sure we clear out // previous options. $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers)); - // Ensure that any changes to variables in the other thread are picked up. - $this->refreshVariables(); + $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up. // Replace original page output with new output from redirected page(s). if ($new = $this->checkForMetaRefresh()) { diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index 98b62c9..0c996e7 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -1,7 +1,6 @@ 2.4-rc0)', ); - foreach ($dependencies as $i => $dependency) { - variable_set('dependency', $dependency); + variable_set('dependencies', $dependencies); + $n = count($dependencies); + for ($i = 0; $i < $n; $i++) { $this->drupalGet('admin/modules'); $checkbox = $this->xpath('//input[@id="edit-modules-testing-module-test-enable"]'); $this->assertEqual(!empty($checkbox[0]['disabled']), $i % 2, $dependencies[$i]); diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php index 2c64b08..b7e1106 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php @@ -189,7 +189,8 @@ protected function refreshVariables() { protected function performUpgrade($register_errors = TRUE) { // Load the first update screen. - $this->getUpdatePhp(); + $update_url = $GLOBALS['base_url'] . '/core/update.php'; + $this->drupalGet($update_url, array('external' => TRUE)); if (!$this->assertResponse(200)) { throw new Exception('Initial GET to update.php did not return HTTP 200 status.'); } @@ -229,7 +230,7 @@ protected function performUpgrade($register_errors = TRUE) { } // Check if there still are pending updates. - $this->getUpdatePhp(); + $this->drupalGet($update_url, array('external' => TRUE)); $this->drupalPost(NULL, array(), t('Continue')); if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) { throw new Exception('update.php still shows pending updates after execution.'); @@ -243,6 +244,7 @@ protected function performUpgrade($register_errors = TRUE) { // but not on the test client. system_list_reset(); module_implements_reset(); + module_load_all(FALSE, TRUE); // Rebuild caches. // @todo Remove the try/catch when UpgradePathTestBase::setup() is fixed to @@ -261,28 +263,4 @@ protected function performUpgrade($register_errors = TRUE) { return TRUE; } - /** - * Gets update.php without calling url(). - * - * This function is necessary because drupalGet calls t() before the - * database is ready for that. - */ - protected function getUpdatePhp() { - // We re-using a CURL connection here. If that connection still has certain - // options set, it might change the GET into a POST. Make sure we clear out - // previous options. - $path = $GLOBALS['base_url'] . '/core/update.php'; - $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $path, CURLOPT_NOBODY => FALSE)); - // Ensure that any changes to variables in the other thread are picked up. - $this->refreshVariables(); - - // Replace original page output with new output from redirected page(s). - if ($new = $this->checkForMetaRefresh()) { - $out = $new; - } - $this->verbose('GET request to: update.php' . - '
Ending URL: ' . $this->getUrl() . - '
' . $out); - return $out; - } } diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index ff9a59a..46c7a36 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -792,9 +792,8 @@ function system_modules($form, $form_state = array()) { // Remove hidden modules from display list. $visible_files = $files; - $profile = drupal_get_profile(); foreach ($visible_files as $filename => $file) { - if (!empty($file->info['hidden']) || $filename == $profile) { + if (!empty($file->info['hidden'])) { unset($visible_files[$filename]); } } @@ -1253,7 +1252,7 @@ function system_modules_uninstall($form, $form_state = NULL) { $all_modules = system_rebuild_module_data(); $disabled_modules = array(); foreach ($all_modules as $name => $module) { - if (empty($module->status) && drupal_get_installed_schema_version($name) > SCHEMA_UNINSTALLED) { + if (empty($module->status) && drupal_get_installed_schema_version($name) > SCHEMA_UNINSTALLED) { $disabled_modules[$name] = $module; } } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index c7b5ad5..b4af5e8 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -435,15 +435,6 @@ function system_requirements($phase) { if ($phase == 'update') { $profile = drupal_get_profile(); $files = system_rebuild_module_data(); - // Build dependencies based on non-system_info_alter-ed info files - // if we are fired before the alter could even run. - if (drupal_get_bootstrap_phase() < DRUPAL_BOOTSTRAP_CODE) { - foreach ($files as $record) { - $record->info = $record->parsed_info; - } - $files = _module_build_dependencies($files); - } - foreach ($files as $module => $file) { // Ignore disabled modules and installation profiles. if (!$file->status || $module == $profile) { diff --git a/core/modules/system/system.module b/core/modules/system/system.module index a07a4db..7302126 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2686,16 +2686,20 @@ function system_check_directory($form_element) { */ function system_get_info($type, $name = NULL) { $info = array(); - $function = 'system_rebuild_' . $type . '_data'; - $data = $function(); - foreach (array_keys(config("system.$type")->get()) as $enabled) { - $info[$enabled] = $data[$enabled]->info; + if ($type == 'module') { + $data = system_rebuild_module_data(); + foreach (module_list() as $module) { + $info[$module] = $data[$module]->info; + } } - $profile = drupal_get_profile(); - if (isset($info[$profile]) && !isset($info[$profile]['distribution_name'])) { - $info[$profile]['distribution_name'] = 'Drupal'; + else { + $list = system_list($type); + foreach ($list as $shortname => $item) { + if (!empty($item->status)) { + $info[$shortname] = $item->info; + } + } } - if (isset($name)) { return isset($info[$name]) ? $info[$name] : array(); } @@ -2729,97 +2733,81 @@ function system_get_module_info($property) { * @return * An associative array of module information. */ -function _system_rebuild_module_data($state_name = FALSE, $save = FALSE) { - static $module_store = array(), $profiles_seen; +function _system_rebuild_module_data() { + // Find modules + $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0); + + // Find installation profiles. + $profiles = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles', 'name', 0); + // Include the installation profile in modules that are loaded. $profile = drupal_get_profile(); - // Find modules not yet processed. - $modules = array(); - if (empty($module_store) && ($state_name || drupal_valid_test_ua()) && ($data = state()->get($state_name ?: __FUNCTION__))) { - // During a test run no modules can move, no info files can change so - // speed up test runs by only finding and parsing once. - $module_store = $data['module_store']; - $profiles_seen = $data['profiles_seen']; - } - // If this is the first time, start with all modules. - if (empty($module_store)) { - // Find modules and installation profiles. As these are files, they can't - // change within a request. However, it does depend on $profile. - $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0); - } - // If this profile is not yet parsed, add it. - if (!isset($profiles_seen[$profile])) { - $profiles = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles', 'name', 0); - // Include the install profile in modules that are loaded. - $modules[$profile] = $profiles[$profile]; - // Install profile hooks are always executed last. - $modules[$profile]->weight = 1000; - $profiles_seen[$profile] = TRUE; - } - // Now process the modules if necessary. - if ($modules) { - // Set defaults for module info. - $defaults = array( - 'dependencies' => array(), - 'description' => '', - 'package' => 'Other', - 'version' => NULL, - 'php' => DRUPAL_MINIMUM_PHP, - 'files' => array(), - 'bootstrap' => 0, - ); - // Read info files for each module. - foreach ($modules as $key => $module) { - // The module system uses the key 'filename' instead of 'uri' so copy the - // value so it will be used by the modules system. - $modules[$key]->filename = $module->uri; - - // Look for the info file. - $module->parsed_info = drupal_parse_info_file(dirname($module->uri) . '/' . $module->name . '.info'); - - // Add the info file modification time, so it becomes available for - // contributed modules to use for ordering module lists. - $module->parsed_info['mtime'] = filemtime(dirname($module->uri) . '/' . $module->name . '.info'); - - // Skip modules that don't provide info. - if (empty($module->parsed_info)) { - unset($modules[$key]); - continue; - } + $modules[$profile] = $profiles[$profile]; - // Merge in defaults and save. - $modules[$key]->parsed_info = $module->parsed_info + $defaults; + // Installation profile hooks are always executed last. + $modules[$profile]->weight = 1000; - // Prefix stylesheets and scripts with module path. - $path = dirname($module->uri); - if (isset($module->parsed_info['stylesheets'])) { - $module->parsed_info['stylesheets'] = _system_info_add_path($module->parsed_info['stylesheets'], $path); - } - if (isset($module->parsed_info['scripts'])) { - $module->parsed_info['scripts'] = _system_info_add_path($module->parsed_info['scripts'], $path); - } + // Set defaults for module info. + $defaults = array( + 'dependencies' => array(), + 'description' => '', + 'package' => 'Other', + 'version' => NULL, + 'php' => DRUPAL_MINIMUM_PHP, + 'files' => array(), + 'bootstrap' => 0, + ); - // Install profiles are hidden by default, unless explicitly specified - // otherwise in the .info file. - if ($key == $profile && !isset($modules[$key]->info['hidden'])) { - $modules[$key]->info['hidden'] = TRUE; - } + // Read info files for each module. + foreach ($modules as $key => $module) { + // The module system uses the key 'filename' instead of 'uri' so copy the + // value so it will be used by the modules system. + $modules[$key]->filename = $module->uri; + + // Look for the info file. + $module->info = drupal_parse_info_file(dirname($module->uri) . '/' . $module->name . '.info'); + + // Add the info file modification time, so it becomes available for + // contributed modules to use for ordering module lists. + $module->info['mtime'] = filemtime(dirname($module->uri) . '/' . $module->name . '.info'); + + // Skip modules that don't provide info. + if (empty($module->info)) { + unset($modules[$key]); + continue; } - // Store the processed modules. - $module_store += $modules; - } - if ($save || ($modules && drupal_valid_test_ua())) { - state()->set($state_name ?: __FUNCTION__, array( - 'module_store' => $module_store, - 'profiles_seen' => $profiles_seen, - )); + + // Merge in defaults and save. + $modules[$key]->info = $module->info + $defaults; + + // Prefix stylesheets and scripts with module path. + $path = dirname($module->uri); + if (isset($module->info['stylesheets'])) { + $module->info['stylesheets'] = _system_info_add_path($module->info['stylesheets'], $path); + } + if (isset($module->info['scripts'])) { + $module->info['scripts'] = _system_info_add_path($module->info['scripts'], $path); + } + + // Installation profiles are hidden by default, unless explicitly specified + // otherwise in the .info file. + if ($key == $profile && !isset($modules[$key]->info['hidden'])) { + $modules[$key]->info['hidden'] = TRUE; + } + + // Invoke hook_system_info_alter() to give installed modules a chance to + // modify the data in the .info files if necessary. + $type = 'module'; + drupal_alter('system_info', $modules[$key]->info, $modules[$key], $type); } - $modules = $module_store; - // Do not return the non-active profile modules. Typically at most one: when - // testing switches the profile. - foreach ($profiles_seen as $key => $value) { - if ($key != $profile) { - unset($modules[$key]); + + if (isset($modules[$profile])) { + // The installation profile is required, if it's a valid module. + $modules[$profile]->info['required'] = TRUE; + // Add a default distribution name if the profile did not provide one. This + // matches the default value used in install_profile_info(). + if (!isset($modules[$profile]->info['distribution_name'])) { + $modules[$profile]->info['distribution_name'] = 'Drupal'; } } @@ -2834,6 +2822,8 @@ function _system_rebuild_module_data($state_name = FALSE, $save = FALSE) { */ function system_rebuild_module_data($reset = TRUE) { $modules_cache = &drupal_static(__FUNCTION__, array()); + // @todo Revert the majority of the following changes. + // Only rebuild once per request. $modules and $modules_cache cannot be // combined into one variable, because the $modules_cache variable is reset by // reference from system_list_reset() during the rebuild. @@ -2863,7 +2853,7 @@ function system_rebuild_module_data($reset = TRUE) { $modules_cache['data'] = $modules; // Store module filenames so that if can be retrieved during system_list() // without a rebuild or drupal_get_filename() without scanning directories. - state()->set('system.module_file_list', $module_file_list); + state()->set('system.module.files', $module_file_list); } // Running the info alter function is not possible before full bootstrap. if (!isset($modules_cache['info_alter']) && drupal_get_bootstrap_phase() >= DRUPAL_BOOTSTRAP_CODE) { @@ -2874,10 +2864,6 @@ function system_rebuild_module_data($reset = TRUE) { $type = 'module'; // It is not impossible a function resets the modules cache. Keep it. $modules = $modules_cache['data']; - foreach ($modules as $record) { - // Copy parsed info. - $record->info = $record->parsed_info; - } foreach (config('system.module')->get() as $module => $weight) { $function = $module .'_system_info_alter'; if (function_exists($function)) { @@ -2904,6 +2890,23 @@ function system_rebuild_module_data($reset = TRUE) { } /** + * Refresh bootstrap column in the system table. + * + * This is called internally by module_enable/disable() to flag modules that + * implement hooks used during bootstrap, such as hook_boot(). These modules + * are loaded earlier to invoke the hooks. + */ +function _system_update_bootstrap_status() { + $bootstrap_modules = array(); + foreach (bootstrap_hooks() as $hook) { + foreach (module_implements($hook) as $module) { + $bootstrap_modules[$module] = drupal_get_filename('module', $module); + } + } + state()->set('system.bootstrap_modules', $bootstrap_modules); +} + +/** * Helper function to scan and collect theme .info data and their engines. * * @return @@ -2998,7 +3001,7 @@ function _system_rebuild_theme_data() { // Store theme filenames so that if can be retrieved during // drupal_get_filename() without scanning directories. if (drupal_container()->hasDefinition('keyvalue') && function_exists('db_query')) { - state()->set('system.theme_file_list', $theme_file_list); + state()->set('system.theme.files', $theme_file_list); } // Now that we've established all our master themes, go back and fill in data // for subthemes. @@ -3035,19 +3038,13 @@ function _system_rebuild_theme_data() { * @return * Array of all available themes and their data. */ -function system_rebuild_theme_data($reset = TRUE) { +function system_rebuild_theme_data() { $themes = _system_rebuild_theme_data(); ksort($themes); $themes_status = config('system.theme')->get(); foreach ($themes as $theme => $data) { $data->status = isset($themes_status[$theme]); } - if ($reset) { - system_list_reset(); - } - // Store theme data so that if can be retrieved during system_list() without - // a rebuild. - state()->set('system.themes', $themes); return $themes; } diff --git a/core/modules/system/tests/modules/system_test/system_test.module b/core/modules/system/tests/modules/system_test/system_test.module index ef06cb7..4949aec 100644 --- a/core/modules/system/tests/modules/system_test/system_test.module +++ b/core/modules/system/tests/modules/system_test/system_test.module @@ -247,10 +247,11 @@ function system_test_exit() { function system_test_system_info_alter(&$info, $file, $type) { // We need a static otherwise the last test will fail to alter common_test. static $test; - if (($dependency = variable_get('dependency', array())) || $test) { + if (($dependencies = variable_get('dependencies', array())) || $test) { if ($file->name == 'module_test') { $info['hidden'] = FALSE; - $info['dependencies'][] = $dependency; + $info['dependencies'][] = array_shift($dependencies); + variable_set('dependencies', $dependencies); $test = TRUE; } if ($file->name == 'common_test') {