diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index eac2f97..36047a0 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -891,22 +891,17 @@ function drupal_get_filename($type, $name, $filename = NULL) { // nothing } else { - // Verify that we have an keyvalue service before using it. This is required - // because this function is called during installation. - // @todo Inject database connection into KeyValueStore\DatabaseStorage. - if (drupal_container()->has('keyvalue') && function_exists('db_query')) { - try { - $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]; - } - } - catch (Exception $e) { - // The keyvalue service raised an exception because the backend might - // be down. We have a fallback for this case so we hide the error - // completely. + try { + $lists = system_list_load(FALSE); + if (isset($lists['filepaths'][$type][$name])) { + $files[$type][$name] = $lists['filepaths'][$type][$name]; } } + catch (Exception $e) { + // The php storage service raised an exception because the backend might + // be down. We have a fallback for this case so we hide the error + // 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])) { diff --git a/core/includes/module.inc b/core/includes/module.inc index 2db6e99..33e2b64 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -150,137 +150,166 @@ function system_list($type) { if (isset($lists['bootstrap'])) { return $lists['bootstrap']; } - if ($cached = cache('bootstrap')->get('bootstrap_modules')) { - $bootstrap_list = $cached->data; - } - else { - $bootstrap_list = state()->get('system.module.bootstrap') ?: 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 $name => $filename) { - drupal_classloader_register($name, dirname($filename)); - drupal_get_filename('module', $name, $filename); - } + $lists = system_list_load(); + system_list_register('module', $lists['bootstrap']); // We only return the module names here since module_list() doesn't need // the filename itself. - $lists['bootstrap'] = array_keys($bootstrap_list); + $lists['bootstrap'] = array_keys($lists['bootstrap']); } // Otherwise build the list for enabled modules and themes. - elseif (!isset($lists['module_enabled'])) { - if ($cached = cache('bootstrap')->get('system_list')) { - $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(). - $enabled_modules = (array) config('system.module')->get('enabled'); - $module_files = state()->get('system.module.files'); - foreach ($enabled_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], - ); - } + elseif (!isset($lists['items_registered'])) { + $lists = system_list_load(); + // To avoid a separate database lookup for the filepath, prime the + // drupal_get_filename() static cache with all enabled modules and themes. + system_list_register('module', $lists['filepaths']['module']); + system_list_register('theme', $lists['filepaths']['theme']); + $lists['items_registered'] = TRUE; + } - // Build a list of themes. - $enabled_themes = (array) config('system.theme')->get('enabled'); - // @todo Themes include all themes, including disabled/uninstalled. This - // system.theme.data state will go away entirely as soon as themes have - // a proper installation status. - // @see http://drupal.org/node/1067408 - $theme_data = state()->get('system.theme.data'); - if (empty($theme_data)) { - // @todo: system_list() may be called from _drupal_bootstrap_code() and - // module_load_all(), in which case system.module is not loaded yet. - // Prevent a filesystem scan in drupal_load() and include it directly. - // @see http://drupal.org/node/1067408 - require_once DRUPAL_ROOT . '/core/modules/system/system.module'; - $theme_data = system_rebuild_theme_data(); - } - foreach ($theme_data as $name => $theme) { - $theme->status = (int) isset($enabled_themes[$name]); - $lists['theme'][$name] = $theme; - // Build a list of filenames so drupal_get_filename can use it. - if (isset($enabled_themes[$name])) { - $lists['filepaths'][] = array( - 'type' => 'theme', - 'name' => $name, - 'filepath' => $theme->filename, - ); - } + return $lists[$type]; +} + +/** + * Prime the drupal_get_filename() static cache and the classloader. + */ +function system_list_register($type, $list) { + foreach ($list as $name => $filename) { + drupal_classloader_register($name, dirname($filename)); + drupal_get_filename($type, $name, $filename); + } +} + +/** + * Load the system load from disk and rebuild if necessary. + */ +function system_list_load($rebuild_allowed = TRUE) { + $lists = array(); + if (function_exists('system_list_get')) { + $lists = system_list_get(); + } + else { + drupal_php_storage('bootstrap')->load('system_list'); + if (function_exists('system_list_get')) { + $lists = system_list_get(); + } + } + if (empty($lists) && $rebuild_allowed) { + // We do not call system_rebuild_module_data() because it calls + // hook_system_info_alter() and we are not interested in module info. + require_once DRUPAL_ROOT . '/core/modules/system/system.module'; + $modules = system_list_find_modules(); + + $enabled_modules = (array) config('system.module')->get('enabled'); + $lists['module_enabled'] = array(); + $lists['filepaths']['module'] = array(); + foreach ($enabled_modules as $name => $weight) { + // Build a list of all enabled modules. + $lists['module_enabled'][$name] = $name; + $lists['filepaths']['module'][$name] = $modules[$name]->uri; + } + // We need to load enabled modules because system_list_find_themes wants + // to find themes defined by modules. Prime drupal_get_filename() to make + // drupal_load() work. + system_list_register('module', $lists['filepaths']['module']); + foreach ($lists['module_enabled'] as $module) { + drupal_load('module', $module); + } + $lists['theme'] = array(); + $lists['filepaths']['theme'] = array(); + $theme_data = system_rebuild_theme_data($enabled_modules); + foreach ($theme_data as $name => $theme) { + $lists['theme'][$name] = $theme; + // Build a list of filenames so drupal_get_filename can use it. + if ($theme->status) { + $lists['filepaths']['theme'][$name] = $theme->uri; } - // @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']; + } + // @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; } - else { - // A plain theme is its own base theme. - $base_key = $key; + // 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']; } - // Set the theme engine prefix. - $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; + // Add the base theme's theme engine info. + $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_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'])); + 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']; } + // Fill in $lists['bootstrap'] and save the list. + _system_update_bootstrap_status($lists); } - return $lists[$type]; + return $lists; } /** + * Save the system lists into a PHP file. + * + * @param $lists + * The lists from system_list(). + */ +function _system_list_save($lists) { + include_once DRUPAL_ROOT . '/core/includes/utility.inc'; + $code = "save('system_list', $code); + if (function_exists('system_list_get')) { + $stored_lists = &drupal_static('system_list_get'); + $stored_lists = $lists; + } + else { + drupal_php_storage('bootstrap')->load('system_list'); + } +} + +/** + * Find modules on the filesystem. + * + * @return array + */ +function system_list_find_modules() { + // 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(); + $modules[$profile] = $profiles[$profile]; + return $modules; +} + + +/** * Resets all system_list() caches. */ function system_list_reset() { drupal_static_reset('system_list'); drupal_static_reset('system_rebuild_module_data'); drupal_static_reset('list_themes'); - cache('bootstrap')->deleteMultiple(array('bootstrap_modules', 'system_list')); cache()->delete('system_info'); - // Remove last known theme data state. - // This causes system_list() to call system_rebuild_theme_data() on its next - // invocation. When enabling a module that implements hook_system_info_alter() - // to inject a new (testing) theme or manipulate an existing theme, then that - // will cause system_list_reset() to be called, but theme data is not - // necessarily rebuilt afterwards. - // @todo Obsolete with proper installation status for themes. - state()->delete('system.theme.data'); + drupal_php_storage('bootstrap')->delete('system_list'); + $lists = &drupal_static('system_list_get'); + $lists = array(); } /** diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 4475104..fe4295c 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2774,16 +2774,8 @@ function system_get_module_info($property) { * An associative array of module information. */ 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. + $modules = system_list_find_modules(); $profile = drupal_get_profile(); - $modules[$profile] = $profiles[$profile]; - // Installation profile hooks are always executed last. $modules[$profile]->weight = 1000; @@ -2882,10 +2874,6 @@ function system_rebuild_module_data() { } $modules = _module_build_dependencies($modules); $modules_cache = $modules; - - // Store filenames to allow system_list() and drupal_get_filename() to - // retrieve them without having to rebuild or scan the filesystem. - state()->set('system.module.files', $files); } return $modules_cache; } @@ -2897,14 +2885,27 @@ function system_rebuild_module_data() { * 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(); +function _system_update_bootstrap_status(&$lists = NULL) { + if (isset($lists)) { + $modules = $lists['module_enabled']; + $module_system_available = FALSE; + } + else { + $lists = system_list_load(); + $module_system_available = TRUE; + } + $lists['bootstrap'] = array(); foreach (bootstrap_hooks() as $hook) { - foreach (module_implements($hook) as $module) { - $bootstrap_modules[$module] = drupal_get_filename('module', $module); + if ($module_system_available) { + $modules = module_implements($hook); + } + foreach ($modules as $module) { + if ($module_system_available || function_exists($module . '_' . $hook)) { + $lists['bootstrap'][$module] = $lists['filepaths']['module'][$module]; + } } } - state()->set('system.module.bootstrap', $bootstrap_modules); + _system_list_save($lists); } /** @@ -2913,21 +2914,28 @@ function _system_update_bootstrap_status() { * @return * An associative array of themes information. */ -function _system_rebuild_theme_data() { +function _system_rebuild_theme_data($modules = NULL) { // Find themes $themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'themes'); // Allow modules to add further themes. - if ($module_themes = module_invoke_all('system_theme_info')) { - foreach ($module_themes as $name => $uri) { - // @see file_scan_directory() - $themes[$name] = (object) array( - 'uri' => $uri, - 'filename' => pathinfo($uri, PATHINFO_FILENAME), - 'name' => $name, - ); + $needs_check = TRUE; + if (!isset($modules)) { + $needs_check = FALSE; + $modules = module_invoke_all('system_theme_info'); + } + foreach ($modules as $module) { + $function = $module . '_system_theme_info'; + if (function_exists($function)) { + foreach ($function() as $name => $uri) { + // @see file_scan_directory() + $themes[$name] = (object) array( + 'uri' => $uri, + 'filename' => pathinfo($uri, PATHINFO_FILENAME), + 'name' => $name, + ); + } } } - // Find theme engines $engines = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines'); @@ -2966,7 +2974,12 @@ function _system_rebuild_theme_data() { // Invoke hook_system_info_alter() to give installed modules a chance to // modify the data in the .info files if necessary. $type = 'theme'; - drupal_alter('system_info', $themes[$key]->info, $themes[$key], $type); + foreach ($modules as $module) { + $function = $module . '_system_info_alter'; + if (!$needs_check || function_exists($function)) { + $function($themes[$key]->info, $themes[$key], $type); + } + } if (!empty($themes[$key]->info['base theme'])) { $sub_themes[] = $key; @@ -3033,8 +3046,8 @@ function _system_rebuild_theme_data() { * @return * Array of all available themes and their data. */ -function system_rebuild_theme_data() { - $themes = _system_rebuild_theme_data(); +function system_rebuild_theme_data($modules = NULL) { + $themes = _system_rebuild_theme_data($modules); ksort($themes); // @todo This function has no business in determining/setting the status of // a theme, but various other functions expect it to return themes with a @@ -3044,19 +3057,9 @@ function system_rebuild_theme_data() { // installation status. // @see http://drupal.org/node/1067408 $enabled_themes = (array) config('system.theme')->get('enabled'); - $files = array(); foreach ($themes as $name => $theme) { $theme->status = (int) isset($enabled_themes[$name]); - $files[$name] = $theme->filename; } - // Replace last known theme data state. - // @todo Obsolete with proper installation status for themes. - state()->set('system.theme.data', $themes); - - // Store filenames to allow system_list() and drupal_get_filename() to - // retrieve them without having to rebuild or scan the filesystem. - state()->set('system.theme.files', $files); - return $themes; }