diff --git a/core/authorize.php b/core/authorize.php index 7f80850..a5fabab 100644 --- a/core/authorize.php +++ b/core/authorize.php @@ -78,11 +78,11 @@ function authorize_access_allowed() { // We have to enable the user and system modules, even to check access and // display errors via the maintenance theme. -$module_list['system']['filename'] = 'core/modules/system/system.module'; -$module_list['user']['filename'] = 'core/modules/user/user.module'; -module_list(NULL, $module_list); -drupal_load('module', 'system'); -drupal_load('module', 'user'); +$module_list['system'] = 'core/modules/system/system.module'; +$module_list['user'] = 'core/modules/user/user.module'; +drupal_container()->get('extension_handler')->setModuleList($module_list); +drupal_container()->get('extension_handler')->loadModule('system'); +drupal_container()->get('extension_handler')->loadModule('user'); // Initialize the language system. drupal_language_initialize(); diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 12ab6e9..4f567d9 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -4,6 +4,8 @@ use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\ExtensionHandler; +use Drupal\Core\ExtensionHandlerMinimal; use Symfony\Component\ClassLoader\UniversalClassLoader; use Symfony\Component\ClassLoader\ApcUniversalClassLoader; use Symfony\Component\DependencyInjection\Container; @@ -884,6 +886,14 @@ function drupal_get_filename($type, $name, $filename = NULL) { // nothing } else { + if ($type == 'module') { + if (empty($files[$type])) { + $files[$type] = drupal_container()->get('extension_handler')->getModuleList(); + } + if (isset($files[$type][$name])) { + return $files[$type][$name]; + } + } // 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. @@ -1115,8 +1125,9 @@ function drupal_page_is_cacheable($allow_caching = NULL) { * @see bootstrap_hooks() */ function bootstrap_invoke_all($hook) { - foreach (module_list('bootstrap') as $module) { - drupal_load('module', $module); + $extension_handler = drupal_container()->get('extension_handler'); + foreach ($extension_handler->getBootstrapModules() as $module) { + $extension_handler->loadModule($module); module_invoke($module, $hook); } } @@ -1135,6 +1146,10 @@ function bootstrap_invoke_all($hook) { * TRUE if the item is loaded or has already been loaded. */ function drupal_load($type, $name) { + if ($type == 'module' && drupal_container()->get('extension_handler')->moduleExists($name)) { + return drupal_container()->get('extension_handler')->loadModule($name); + } + // Once a file is included this can't be reversed during a request so do not // use drupal_static() here. static $files = array(); @@ -2406,7 +2421,7 @@ function _drupal_bootstrap_variables() { $conf = variable_initialize(isset($conf) ? $conf : array()); // Load bootstrap modules. require_once DRUPAL_ROOT . '/core/includes/module.inc'; - module_load_all(TRUE); + drupal_container()->get('extension_handler')->loadBootstrapModules(); } /** @@ -2460,6 +2475,45 @@ function drupal_container(Container $new_container = NULL) { } /** + * Determines which modules are implementing a hook. + * + * @see ExtensionHandler::moduleImplements(). + */ +function module_implements($hook) { + return drupal_container()->get('extension_handler')->moduleImplements($hook); +} + +/** + * Invokes a hook in all enabled modules that implement it. + * + * @see ExtensionHandler::moduleInvokeAll(). + */ +function module_invoke_all($hook) { + $args = func_get_args(); + // Remove $hook from the arguments. + unset($args[0]); + return drupal_container()->get('extension_handler')->moduleInvokeAll($hook, $args); +} + +/** + * Passes alterable variables to specific hook_TYPE_alter() implementations. + * + * @see ExtensionHandler::alter(). + */ +function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { + return drupal_container()->get('extension_handler')->alter($type, $data, $context1, $context2); +} + +/** + * Determines whether a given module exists. + * + * @see ExtensionHandler::moduleExists(). + */ +function module_exists($module) { + return drupal_container()->get('extension_handler')->moduleExists($module); +} + +/** * Returns the state storage service. * * Use this to store machine-generated data, local to a specific environment diff --git a/core/includes/common.inc b/core/includes/common.inc index a8ba090..ca66cc0 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4774,7 +4774,7 @@ function _drupal_bootstrap_code() { require_once DRUPAL_ROOT . '/core/includes/entity.inc'; // Load all enabled modules - module_load_all(); + drupal_container()->get('extension_handler')->loadAllModules(); // Make sure all stream wrappers are registered. file_get_stream_wrappers(); @@ -6373,7 +6373,7 @@ function drupal_flush_all_caches() { // Ensure that all modules that are currently supposed to be enabled are // actually loaded. - module_load_all(); + drupal_container()->get('extension_handler')->loadAllModules(); // Update the list of bootstrap modules. // Allows developers to get new hook_boot() implementations registered without @@ -6447,68 +6447,10 @@ function debug($data, $label = NULL, $print_r = FALSE) { } /** - * Parses a dependency for comparison by drupal_check_incompatibility(). - * - * @param $dependency - * A dependency string, for example 'foo (>=8.x-4.5-beta5, 3.x)'. - * - * @return - * An associative array with three keys: - * - 'name' includes the name of the thing to depend on (e.g. 'foo'). - * - 'original_version' contains the original version string (which can be - * used in the UI for reporting incompatibilities). - * - 'versions' is a list of associative arrays, each containing the keys - * 'op' and 'version'. 'op' can be one of: '=', '==', '!=', '<>', '<', - * '<=', '>', or '>='. 'version' is one piece like '4.5-beta3'. - * Callers should pass this structure to drupal_check_incompatibility(). - * - * @see drupal_check_incompatibility() - */ -function drupal_parse_dependency($dependency) { - // We use named subpatterns and support every op that version_compare - // supports. Also, op is optional and defaults to equals. - $p_op = '(?P!=|==|=|<|<=|>|>=|<>)?'; - // Core version is always optional: 8.x-2.x and 2.x is treated the same. - $p_core = '(?:' . preg_quote(DRUPAL_CORE_COMPATIBILITY) . '-)?'; - $p_major = '(?P\d+)'; - // By setting the minor version to x, branches can be matched. - $p_minor = '(?P(?:\d+|x)(?:-[A-Za-z]+\d+)?)'; - $value = array(); - $parts = explode('(', $dependency, 2); - $value['name'] = trim($parts[0]); - if (isset($parts[1])) { - $value['original_version'] = ' (' . $parts[1]; - foreach (explode(',', $parts[1]) as $version) { - if (preg_match("/^\s*$p_op\s*$p_core$p_major\.$p_minor/", $version, $matches)) { - $op = !empty($matches['operation']) ? $matches['operation'] : '='; - if ($matches['minor'] == 'x') { - // Drupal considers "2.x" to mean any version that begins with - // "2" (e.g. 2.0, 2.9 are all "2.x"). PHP's version_compare(), - // on the other hand, treats "x" as a string; so to - // version_compare(), "2.x" is considered less than 2.0. This - // means that >=2.x and <2.x are handled by version_compare() - // as we need, but > and <= are not. - if ($op == '>' || $op == '<=') { - $matches['major']++; - } - // Equivalence can be checked by adding two restrictions. - if ($op == '=' || $op == '==') { - $value['versions'][] = array('op' => '<', 'version' => ($matches['major'] + 1) . '.x'); - $op = '>='; - } - } - $value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']); - } - } - } - return $value; -} - -/** * Checks whether a version is compatible with a given dependency. * * @param $v - * The parsed dependency structure from drupal_parse_dependency(). + * A parsed dependency structure e.g. from ExtensionHandler::parseDependency(). * @param $current_version * The version to check against (like 4.2). * @@ -6516,7 +6458,7 @@ function drupal_parse_dependency($dependency) { * NULL if compatible, otherwise the original dependency version string that * caused the incompatibility. * - * @see drupal_parse_dependency() + * @see ExtensionHandler::parseDependency() */ function drupal_check_incompatibility($v, $current_version) { if (!empty($v['versions'])) { diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 755c2c1..362e53c 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -330,6 +330,9 @@ function install_begin_request(&$install_state) { $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory') ->addArgument(new Reference('config.storage')) ->addArgument(new Reference('event_dispatcher')); + // Register an extension handler for managing enabled modules. + $container + ->register('extension_handler', 'Drupal\Core\ExtensionHandlerMinimal'); drupal_container($container); } @@ -337,10 +340,10 @@ function install_begin_request(&$install_state) { drupal_language_initialize(); require_once DRUPAL_ROOT . '/core/includes/ajax.inc'; + // Override the module list with a minimal set of modules. - $module_list['system']['filename'] = 'core/modules/system/system.module'; - module_list(NULL, $module_list); - drupal_load('module', 'system'); + drupal_container()->get('extension_handler')->setModuleList(array('system' => 'core/modules/system/system.module')); + drupal_container()->get('extension_handler')->loadModule('system'); // Load the cache infrastructure using a "fake" cache implementation that // does not attempt to write to the database. We need this during the initial @@ -1496,9 +1499,14 @@ function install_load_profile(&$install_state) { * An array of information about the current installation state. */ function install_bootstrap_full(&$install_state) { - // Clear the module list that was overriden earlier in the process. - // This will allow all freshly installed modules to be loaded. - module_list_reset(); + // Clear the statically cached extension handler. This will allow all freshly + // installed modules to be loaded. + drupal_static_reset('drupal_extension_handler'); + + // Instantiate the kernel. + $kernel = new DrupalKernel('prod', FALSE, drupal_classloader(), FALSE); + $kernel->boot(); + drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); } diff --git a/core/includes/install.inc b/core/includes/install.inc index 99cfcf6..9f44b00 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -444,10 +444,8 @@ function drupal_install_system() { ->save(); // Clear out module list and hook implementation statics. - system_list_reset(); - module_list_reset(); - module_implements_reset(); - + drupal_static_reset('drupal_extension_handler'); + drupal_container()->get('extension_handler')->setModuleList(array('system' => $system_path . '/system.module')); config_install_default_config('module', 'system'); module_invoke('system', 'install'); diff --git a/core/includes/module.inc b/core/includes/module.inc index 9be96cd..03680d7 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -9,114 +9,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Loads all enabled modules. - * - * @param bool $bootstrap - * 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, $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'; - foreach (module_list($type) as $module) { - drupal_load('module', $module); - } - // $has_run will be TRUE if $bootstrap is FALSE. - $has_run = !$bootstrap; - } - return $has_run; -} - -/** - * Returns a list of currently active modules. - * - * Acts as a wrapper around system_list(), returning either a list of all - * enabled modules, or just modules needed for bootstrap. - * - * The returned module list is always based on system_list(). The only exception - * to that is when a fixed list of modules has been passed in previously, in - * which case system_list() is omitted and the fixed list is always returned in - * subsequent calls until manually reverted via module_list_reset(). - * - * @param string $type - * The type of list to return: - * - module_enabled: All enabled modules. - * - bootstrap: All enabled modules required for bootstrap. - * @param array $fixed_list - * (optional) An array of module names to override the list of modules. This - * list will persist until the next call with a new $fixed_list passed in. - * Primarily intended for internal use (e.g., in install.php and update.php). - * Use module_list_reset() to undo the $fixed_list override. - * @param bool $reset - * (optional) Whether to reset/remove the $fixed_list. - * - * @return array - * An associative array whose keys and values are the names of the modules in - * the list. - * - * @see module_list_reset() - */ -function module_list($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) { - // This static is only used for $fixed_list. It must not be a drupal_static(), - // since any call to drupal_static_reset() in unit tests would cause an - // attempt to retrieve the list of modules from the database (which does not - // exist). - static $module_list; - - if ($reset) { - $module_list = NULL; - // Do nothing if no $type and no $fixed_list have been passed. - if (!isset($type) && !isset($fixed_list)) { - return; - } - } - - // The list that will be be returned. Separate from $module_list in order - // to not duplicate the static cache of system_list(). - $list = $module_list; - - if (isset($fixed_list)) { - $module_list = array(); - foreach ($fixed_list as $name => $module) { - system_register('module', $name, $module['filename']); - $module_list[$name] = $name; - } - $list = $module_list; - } - elseif (!isset($module_list)) { - $list = system_list($type); - } - return $list; -} - -/** - * Reverts an enforced fixed list of module_list(). - * - * Subsequent calls to module_list() will no longer use a fixed list. - */ -function module_list_reset() { - module_list(NULL, NULL, TRUE); -} - -/** * Builds a list of bootstrap modules and enabled modules and themes. * * @param $type @@ -131,7 +23,6 @@ function module_list_reset() { * For $type 'theme', the array values are objects representing the * respective database row, with the 'info' property already unserialized. * - * @see module_list() * @see list_themes() * * @todo There are too many layers/levels of caching involved for system_list() @@ -141,126 +32,80 @@ function module_list_reset() { */ function system_list($type) { $lists = &drupal_static(__FUNCTION__); - - // 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 (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) { - system_register('module', $name, $filename); - } - // We only return the module names here since module_list() doesn't need - // the filename itself. - $lists['bootstrap'] = array_keys($bootstrap_list); + if ($cached = cache('bootstrap')->get('system_list')) { + $lists = $cached->data; } - // 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. + else { + $lists = array( + 'theme' => array(), + 'filepaths' => array(), + ); + // 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(), 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' => 'module', + 'type' => 'theme', 'name' => $name, - 'filepath' => $module_files[$name], + 'filepath' => $theme->filename, ); } - - // 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, - ); - } - } - // @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) { - system_register($item['type'], $item['name'], $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']; } + 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) { + system_register($item['type'], $item['name'], $item['filepath']); } return $lists[$type]; } + /** * Resets all system_list() caches. */ @@ -280,6 +125,7 @@ function system_list_reset() { state()->delete('system.theme.data'); } + /** * Registers an extension in runtime registries for execution. * @@ -297,53 +143,6 @@ function system_register($type, $name, $uri) { } /** - * Determines which modules require and are required by each module. - * - * @param $files - * The array of filesystem objects used to rebuild the cache. - * - * @return - * 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. - */ -function _module_build_dependencies($files) { - foreach ($files as $filename => $file) { - $graph[$file->name]['edges'] = array(); - if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) { - foreach ($file->info['dependencies'] as $dependency) { - $dependency_data = drupal_parse_dependency($dependency); - $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data; - } - } - } - $graph_object = new Graph($graph); - $graph = $graph_object->searchAndSort(); - foreach ($graph as $module => $data) { - $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array(); - $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array(); - $files[$module]->sort = $data['weight']; - } - return $files; -} - -/** - * Determines 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. - */ -function module_exists($module) { - $list = module_list(); - return isset($list[$module]); -} - -/** * Loads a module's installation hooks. * * @param $module @@ -401,15 +200,6 @@ function module_load_include($type, $module, $name = NULL) { return FALSE; } -/** - * Loads an include file for each enabled module. - */ -function module_load_all_includes($type, $name = NULL) { - $modules = module_list(); - foreach ($modules as $module) { - module_load_include($type, $module, $name); - } -} /** * Enables or installs a given list of modules. @@ -493,10 +283,11 @@ function module_enable($module_list, $enable_dependencies = TRUE) { $schema_store = drupal_container()->get('keyvalue')->get('system.schema'); $module_config = config('system.module'); $disabled_config = config('system.module.disabled'); - $module_filenames = drupal_container()->getParameter('container.modules'); + $extension_handler = drupal_container()->get('extension_handler'); foreach ($module_list as $module) { + $module_filenames = $extension_handler->getModuleList(); // Only process modules that are not already enabled. - $enabled = $module_config->get("enabled.$module") !== NULL; + $enabled = isset($module_filenames[$module]); if (!$enabled) { $weight = $disabled_config->get($module); if ($weight === NULL) { @@ -509,21 +300,35 @@ function module_enable($module_list, $enable_dependencies = TRUE) { $disabled_config ->clear($module) ->save(); + + $module_filenames[$module] = drupal_get_filename('module', $module); + + $sorted_modules = $module_config->get('enabled'); + $sorted_with_filenames = array(); + foreach (array_keys($sorted_modules) as $sorted_module) { + $filename = isset($module_filenames[$sorted_module]) ? $module_filenames[$sorted_module] : drupal_get_filename('module', $sorted_module); + $sorted_with_filenames[$sorted_module] = $filename; + } + + // Refresh the module list in the extension handler. + $extension_handler->setModuleList($module_filenames); + // Load the module's code. - drupal_load('module', $module); + $extension_handler->loadModule($module); module_load_install($module); // Refresh the module list to include it. - system_list_reset(); - module_implements_reset(); + $extension_handler->moduleImplementsReset(); + _system_update_bootstrap_status(); - $module_filenames[$module] = drupal_get_filename('module', $module); - // Update the kernel to include it. + + // Update the kernel to include the new module // @todo The if statement is here because install_begin_request() creates // a container without a kernel. It probably shouldn't. if ($kernel = drupal_container()->get('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE)) { - $kernel->updateModules(module_list(), $module_filenames); + $kernel->updateModules($sorted_modules, $sorted_with_filenames); } + // Refresh the schema to include it. drupal_get_schema(NULL, TRUE); // Update the theme registry to include it. @@ -631,8 +436,10 @@ function module_disable($module_list, $disable_dependents = TRUE) { $module_config = config('system.module'); $disabled_config = config('system.module.disabled'); + $extension_handler = drupal_container()->get('extension_handler'); foreach ($module_list as $module) { - if (module_exists($module)) { + $enabled = $extension_handler->getModuleList(); + if (isset($enabled[$module])) { module_load_install($module); module_invoke($module, 'disable'); $disabled_config @@ -641,22 +448,27 @@ function module_disable($module_list, $disable_dependents = TRUE) { $module_config ->clear("enabled.$module") ->save(); + unset($enabled[$module]); + $extension_handler->setModuleList($enabled); $invoke_modules[] = $module; watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO); } } if (!empty($invoke_modules)) { - // Refresh the module list to exclude the disabled modules. + // Refresh the system list to exclude the disabled modules. + // @todo This call is only needed so that the theme info gets rebuilt - see + // the todo in system_list_reset(). system_list_reset(); - module_implements_reset(); + // Refresh the module list to exclude the disabled modules. + drupal_container()->get('extension_handler')->moduleImplementsReset(); entity_info_cache_clear(); // 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 kernel to exclude the disabled modules. - drupal_container()->get('kernel')->updateModules(module_list()); + drupal_container()->get('kernel')->updateModules($module_config->get('enabled'), $enabled); // Update the theme registry to remove the newly-disabled module. drupal_theme_rebuild(); } @@ -783,7 +595,7 @@ function module_hook($module, $hook) { } // If the hook implementation does not exist, check whether it may live in an // optional include file registered via hook_hook_info(). - $hook_info = module_hook_info(); + $hook_info = drupal_container()->get('extension_handler')->moduleHookInfo(); if (isset($hook_info[$hook]['group'])) { module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']); if (function_exists($function)) { @@ -794,171 +606,6 @@ function module_hook($module, $hook) { } /** - * Determines which modules are implementing a hook. - * - * @param $hook - * The name of the hook (e.g. "help" or "menu"). - * - * @return - * An array with the names of the modules which are implementing this hook. - * - * @see module_implements_write_cache() - */ -function module_implements($hook) { - // Use the advanced drupal_static() pattern, since this is called very often. - static $drupal_static_fast; - if (!isset($drupal_static_fast)) { - $drupal_static_fast['implementations'] = &drupal_static(__FUNCTION__); - } - $implementations = &$drupal_static_fast['implementations']; - - // Fetch implementations from cache. - if (empty($implementations)) { - $implementations = cache('bootstrap')->get('module_implements'); - if ($implementations === FALSE) { - $implementations = array(); - } - else { - $implementations = $implementations->data; - } - } - - if (!isset($implementations[$hook])) { - // 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; - $hook_info = module_hook_info(); - $implementations[$hook] = array(); - foreach (module_list() as $module) { - $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']); - // Since module_hook() may needlessly try to load the include file again, - // function_exists() is used directly here. - if (function_exists($module . '_' . $hook)) { - $implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE; - } - } - // 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); - } - } - else { - 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"); - } - // It is possible that a module removed a hook implementation without the - // implementations cache being rebuilt yet, so we check whether the - // function exists on each request to avoid undefined function errors. - // Since module_hook() may needlessly try to load the include file again, - // function_exists() is used directly here. - if (!function_exists($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; - } - } - } - - return array_keys($implementations[$hook]); -} - -/** - * Regenerates the stored list of hook implementations. - */ -function module_implements_reset() { - // 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('bootstrap')->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. - drupal_static_reset('module_implements'); - cache('bootstrap')->set('module_implements', array()); - drupal_static_reset('module_hook_info'); - drupal_static_reset('drupal_alter'); - cache('bootstrap')->delete('hook_info'); -} - -/** - * Retrieves a list of hooks that are declared through hook_hook_info(). - * - * @return - * An associative array whose keys are hook names and whose values are an - * associative array containing a group name. The structure of the array - * is the same as the return value of hook_hook_info(). - * - * @see hook_hook_info() - */ -function module_hook_info() { - // When this function is indirectly invoked from bootstrap_invoke_all() prior - // to all modules being loaded, we do not want to cache an incomplete - // hook_hook_info() result, so instead return an empty array. This requires - // bootstrap hook implementations to reside in the .module file, which is - // optimal for performance anyway. - if (!module_load_all(NULL)) { - return array(); - } - $hook_info = &drupal_static(__FUNCTION__); - - if (!isset($hook_info)) { - $hook_info = array(); - $cache = cache('bootstrap')->get('hook_info'); - 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); - } - } - cache('bootstrap')->set('hook_info', $hook_info); - } - else { - $hook_info = $cache->data; - } - } - - return $hook_info; -} - -/** - * Writes the hook implementation cache. - * - * @see module_implements() - */ -function module_implements_write_cache() { - $implementations = &drupal_static('module_implements'); - // 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')) { - unset($implementations['#write_cache']); - cache('bootstrap')->set('module_implements', $implementations); - } -} - -/** * Invokes a hook in a particular module. * * @param $module @@ -981,39 +628,6 @@ function module_invoke($module, $hook) { } /** - * Invokes 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. - */ -function module_invoke_all($hook) { - $args = func_get_args(); - // Remove $hook from the arguments. - unset($args[0]); - $return = array(); - foreach (module_implements($hook) as $module) { - $function = $module . '_' . $hook; - if (function_exists($function)) { - $result = call_user_func_array($function, $args); - if (isset($result) && is_array($result)) { - $return = array_merge_recursive($return, $result); - } - elseif (isset($result)) { - $return[] = $result; - } - } - } - - return $return; -} - -/** * @} End of "defgroup hooks". */ @@ -1038,172 +652,6 @@ function drupal_required_modules() { } /** - * Passes alterable variables to specific hook_TYPE_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 - * A string describing the type of the alterable $data. 'form', 'links', - * '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. - * @param $data - * 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. - * @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 - * associative array as described above. - */ -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']; - - // 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; - } - - // 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. - if (!isset($functions[$cid])) { - $functions[$cid] = array(); - $hook = $type . '_alter'; - $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. module_implements() can only return ordered - // implementations of a single hook. To get the ordered implementations - // of multiple hooks, we mimic the module_implements() logic of first - // ordering by module_list(), and then calling - // drupal_alter('module_implements'). - if (array_diff($extra_modules, $modules)) { - // Merge the arrays and order by module_list(). - $modules = array_intersect(module_list(), array_merge($modules, $extra_modules)); - // Since module_implements() already took care of loading the necessary - // include files, we can safely pass FALSE for the array values. - $implementations = array_fill_keys($modules, FALSE); - // Let modules adjust the order solely based on the primary hook. This - // ensures the same module order regardless of whether this if block - // runs. Calling drupal_alter() recursively in this way does not result - // in an infinite loop, because this call is for a single $type, so we - // won't end up in this code block again. - drupal_alter('module_implements', $implementations, $hook); - $modules = array_keys($implementations); - } - 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; - } - } - } - } - // 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)) { - $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; - } - } - } - } - } - } - - foreach ($functions[$cid] as $function) { - $function($data, $context1, $context2); - } -} - -/** * Sets weight of a particular module. * * The weight of uninstalled modules cannot be changed. @@ -1216,11 +664,20 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { function module_set_weight($module, $weight) { // Update the module weight in the config file that contains it. $module_config = config('system.module'); + $extension_handler = drupal_container()->get('extension_handler'); + $enabled_modules = $extension_handler->getModuleList(); if ($module_config->get("enabled.$module") !== NULL) { $module_config ->set("enabled.$module", $weight) ->set('enabled', module_config_sort($module_config->get('enabled'))) ->save(); + $sorted_modules = $module_config->get('enabled'); + $sorted_with_filenames = array(); + foreach (array_keys($sorted_modules) as $m) { + $sorted_with_filenames[$m] = $enabled_modules[$m]; + } + // Refresh the module list in the extension handler. + $extension_handler->setModuleList($sorted_with_filenames); return; } $disabled_config = config('system.module.disabled'); diff --git a/core/includes/schema.inc b/core/includes/schema.inc index 7661dcb..e24bb80 100644 --- a/core/includes/schema.inc +++ b/core/includes/schema.inc @@ -79,13 +79,13 @@ function drupal_get_complete_schema($rebuild = FALSE) { // Load the .install files to get hook_schema. // On some databases this function may be called before bootstrap has // been completed, so we force the functions we need to load just in case. - if (function_exists('module_load_all_includes')) { + if (function_exists('system_list_reset')) { // This function can be called very early in the bootstrap process, so // we force the system_list() static cache to be refreshed to ensure // that it contains the complete list of modules before we go on to call // module_load_all_includes(). system_list_reset(); - module_load_all_includes('install'); + drupal_container()->get('extension_handler')->loadModuleIncludes('install'); } require_once DRUPAL_ROOT . '/core/includes/common.inc'; @@ -130,8 +130,7 @@ function drupal_get_schema_versions($module) { $updates = &drupal_static(__FUNCTION__, NULL); if (!isset($updates[$module])) { $updates = array(); - - foreach (module_list() as $loaded_module) { + foreach (drupal_container()->get('extension_handler')->getModuleList() as $loaded_module => $filename) { $updates[$loaded_module] = array(); } @@ -341,18 +340,18 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU * An array of fields. */ function drupal_schema_fields_sql($table, $prefix = NULL) { - $schema = drupal_get_schema($table); - $fields = array_keys($schema['fields']); - if ($prefix) { - $columns = array(); - foreach ($fields as $field) { - $columns[] = "$prefix.$field"; + if ($schema = drupal_get_schema($table)) { + $fields = array_keys($schema['fields']); + if ($prefix) { + $columns = array(); + foreach ($fields as $field) { + $columns[] = "$prefix.$field"; + } + return $columns; } - return $columns; - } - else { return $fields; } + return array(); } /** diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 35b9cd3..42fcd84 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -367,14 +367,14 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL, $registry = _theme_build_registry($theme, $base_theme, $theme_engine); // Only persist this registry if all modules are loaded. This assures a // complete set of theme hooks. - if (module_load_all(NULL)) { + if (drupal_container()->get('extension_handler')->allModulesLoaded()) { _theme_save_registry($theme, $registry); } } return $registry; } else { - return new ThemeRegistry('theme_registry:runtime:' . $theme->name, 'cache', array('theme_registry' => TRUE)); + return new ThemeRegistry('theme_registry:runtime:' . $theme->name, 'cache', array('theme_registry' => TRUE), drupal_container()->get('extension_handler')->allModulesLoaded()); } } @@ -439,7 +439,6 @@ function drupal_theme_rebuild() { * themes/bartik. * * @see theme() - * @see _theme_process_registry() * @see hook_theme() * @see list_themes() */ @@ -546,7 +545,7 @@ function _theme_process_registry(&$cache, $name, $type, $theme, $path) { // Add all modules so they can intervene with their own variable // processors. This allows them to provide variable processors even // if they are not the owner of the current hook. - $prefixes += module_list(); + $prefixes = array_merge($prefixes, array_keys(drupal_container()->get('extension_handler')->getModuleList())); } elseif ($type == 'theme_engine' || $type == 'base_theme_engine') { // Theme engines get an extra set that come before the normally @@ -641,7 +640,7 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) { _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module)); } // Only cache this registry if all modules are loaded. - if (module_load_all(NULL)) { + if (drupal_container()->get('extension_handler')->allModulesLoaded()) { cache()->set("theme_registry:build:modules", $cache, CacheBackendInterface::CACHE_PERMANENT, array('theme_registry' => TRUE)); } } @@ -956,7 +955,7 @@ function theme($hook, $variables = array()) { // If called before all modules are loaded, we do not necessarily have a full // theme registry to work with, and therefore cannot process the theme // request properly. See also _theme_load_registry(). - if (!module_load_all(NULL) && !defined('MAINTENANCE_MODE')) { + if (!drupal_container()->get('extension_handler')->allModulesLoaded() && !defined('MAINTENANCE_MODE')) { throw new Exception(t('theme() may not be called until all modules are loaded.')); } diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc index 4b3e80c..1627cd8 100644 --- a/core/includes/theme.maintenance.inc +++ b/core/includes/theme.maintenance.inc @@ -56,8 +56,8 @@ function _drupal_maintenance_theme() { // Ensure that system.module is loaded. if (!function_exists('_system_rebuild_theme_data')) { $module_list['system']['filename'] = 'core/modules/system/system.module'; - module_list(NULL, $module_list); - drupal_load('module', 'system'); + drupal_container()->get('extension_handler')->setModuleList($module_list); + drupal_container()->get('extension_handler')->loadModule('system'); } $themes = list_themes(); diff --git a/core/includes/update.inc b/core/includes/update.inc index b8e8ee6..8405b34 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -9,6 +9,7 @@ */ use Drupal\Component\Graph\Graph; +use Drupal\Core\ExtensionHandler; use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\ConfigException; use Drupal\Component\Uuid\Uuid; @@ -91,7 +92,7 @@ function update_prepare_d8_bootstrap() { include_once DRUPAL_ROOT . '/core/includes/install.inc'; include_once DRUPAL_ROOT . '/core/includes/schema.inc'; // Bootstrap to configuration. - drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + drupal_bootstrap(DRUPAL_BOOTSTRAP_KERNEL); // Check whether settings.php needs to be rewritten. $settings_exist = !empty($GLOBALS['config_directories']); @@ -125,9 +126,10 @@ function update_prepare_d8_bootstrap() { include_once DRUPAL_ROOT . '/core/includes/module.inc'; include_once DRUPAL_ROOT . '/core/includes/cache.inc'; - $module_list['system']['filename'] = 'core/modules/system/system.module'; - module_list(NULL, $module_list); - require_once DRUPAL_ROOT . '/' . $module_list['system']['filename']; + $extension_handler = drupal_container()->get('extension_handler'); + $extension_handler->setModuleList(array('system' => 'core/modules/system/system.module')); + + $extension_handler->loadModule('system'); // Ensure the configuration directories exist and are writable, or create // them. If the directories have not been specified in settings.php and // created manually already, and either directory cannot be created by the @@ -240,8 +242,8 @@ function update_prepare_d8_bootstrap() { // Populate a fixed module list (again, why did it get lost?) to avoid // errors due to the drupal_alter() in _system_rebuild_module_data(). - $module_list['system']['filename'] = 'core/modules/system/system.module'; - module_list(NULL, $module_list); + $module_list['system'] = 'core/modules/system/system.module'; + drupal_container()->get('extension_handler')->setModuleList($module_list); $module_data = _system_rebuild_module_data(); // Migrate each extension into configuration, varying by the extension's @@ -265,7 +267,14 @@ function update_prepare_d8_bootstrap() { } $schema_store->set($record->name, $record->schema_version); } - $module_config->set('enabled', module_config_sort($module_config->get('enabled')))->save(); + $sorted_modules = module_config_sort($module_config->get('enabled')); + $module_config->set('enabled', $sorted_modules)->save(); + $sorted_with_filenames = array(); + foreach (array_keys($sorted_modules) as $m) { + $sorted_with_filenames[$m] = drupal_get_filename('module', $m); + } + + drupal_container()->get('extension_handler')->setModuleList($sorted_with_filenames); $disabled_modules->save(); $theme_config->save(); $disabled_themes->save(); @@ -275,8 +284,6 @@ function update_prepare_d8_bootstrap() { update_prepare_stored_includes(); // Update the environment for the language bootstrap if needed. update_prepare_d8_language(); - // Prime the classloader. - system_list('module_enabled'); // Change language column to langcode in url_alias. if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) { diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index ff48b04..43bf208 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -99,6 +99,25 @@ public function build(ContainerBuilder $container) { $container->register('event_dispatcher', 'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher') ->addArgument(new Reference('service_container')); + + $container + ->register('cache.cache', 'Drupal\Core\Cache\CacheBackendInterface') + ->setFactoryClass('Drupal\Core\Cache\CacheFactory') + ->setFactoryMethod('get') + ->addArgument('cache'); + $container + ->register('cache.bootstrap', 'Drupal\Core\Cache\CacheBackendInterface') + ->setFactoryClass('Drupal\Core\Cache\CacheFactory') + ->setFactoryMethod('get') + ->addArgument('bootstrap'); + + // The ExtensionHandler manages enabled modules and provides the ability to + // invoke hooks in all enabled modules. + $container->register('extension_handler', 'Drupal\Core\ExtensionHandler') + ->addArgument('%container.modules%') + ->addArgument(new Reference('keyvalue')) + ->addArgument(new Reference('cache.bootstrap')); + $container->register('resolver', 'Drupal\Core\ControllerResolver') ->addArgument(new Reference('service_container')); $container->register('http_kernel', 'Drupal\Core\HttpKernel') @@ -130,8 +149,8 @@ public function build(ContainerBuilder $container) { $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder') ->addArgument(new Reference('router.dumper')) ->addArgument(new Reference('lock')) - ->addArgument(new Reference('event_dispatcher')); - + ->addArgument(new Reference('event_dispatcher')) + ->addArgument(new Reference('extension_handler')); $container->register('matcher', 'Drupal\Core\Routing\ChainMatcher'); $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher') @@ -213,6 +232,7 @@ public function build(ContainerBuilder $container) { ->setScope('request') ->addTag('event_subscriber'); $container->register('request_close_subscriber', 'Drupal\Core\EventSubscriber\RequestCloseSubscriber') + ->addArgument(new Reference('extension_handler')) ->addTag('event_subscriber'); $container->register('config_global_override_subscriber', 'Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber') ->addTag('event_subscriber'); diff --git a/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php index 8d5f87a..04250ab 100644 --- a/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php @@ -7,6 +7,7 @@ namespace Drupal\Core\EventSubscriber; +use Drupal\Core\ExtensionHandlerInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\PostResponseEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -16,6 +17,18 @@ */ class RequestCloseSubscriber implements EventSubscriberInterface { + /** + * @var ExtensionHandlerInterface + */ + protected $extensionHandler; + + /** + * Constructor. + */ + function __construct(ExtensionHandlerInterface $extension_handler) { + $this->extensionHandler = $extension_handler; + } + /** * Performs end of request tasks. * @@ -29,7 +42,14 @@ class RequestCloseSubscriber implements EventSubscriberInterface { */ public function onTerminate(PostResponseEvent $event) { module_invoke_all('exit'); - module_implements_write_cache(); + $request_method = $event->getRequest()->getMethod(); + // Check whether we need to write the module implementations 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 ($request_method == 'GET' || $request_method == 'HEAD') { + $this->extensionHandler->writeModuleImplementationsCache(); + } system_run_automated_cron(); } diff --git a/core/lib/Drupal/Core/ExtensionHandler.php b/core/lib/Drupal/Core/ExtensionHandler.php new file mode 100644 index 0000000..674b65d --- /dev/null +++ b/core/lib/Drupal/Core/ExtensionHandler.php @@ -0,0 +1,539 @@ +moduleList = $module_list; + $this->state = $key_value->get('state'); + $this->bootstrapCache = $bootstrapCache; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::loadModule(). + */ + public function loadModule($name) { + if (isset($this->loadedFiles[$name])) { + return TRUE; + } + + if (isset($this->moduleList[$name])) { + $filename = $this->moduleList[$name]; + include_once DRUPAL_ROOT . '/' . $filename; + $this->loadedFiles[$name] = TRUE; + return TRUE; + } + return FALSE; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::loadAllModules(). + */ + public function loadAllModules() { + if (!$this->loaded) { + foreach ($this->moduleList as $module => $filename) { + $this->loadModule($module); + } + $this->loaded = TRUE; + } + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::reloadModules(). + */ + public function reloadModules() { + $this->loaded = FALSE; + $this->loadAllModules(); + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::loadBootstrapModules(). + */ + public function loadBootstrapModules() { + if (!$this->loaded) { + foreach ($this->getBootstrapModules() as $module) { + $this->loadModule($module); + } + } + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::allModulesLoaded(). + */ + public function allModulesLoaded() { + return $this->loaded; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::getModuelList(). + */ + public function getModuleList() { + return $this->moduleList; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::setModuleList(). + */ + public function setModuleList(array $module_list = array()) { + $this->moduleList = $module_list; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::getBootstrapModules(). + */ + public function getBootstrapModules() { + if (isset($this->bootstrapModules)) { + return $this->bootstrapModules; + } + if ($cached = $this->bootstrapCache->get('bootstrap_modules')) { + $bootstrap_list = $cached->data; + } + else { + $bootstrap_list = $this->state->get('system.module.bootstrap') ?: array(); + $this->bootstrapCache->set('bootstrap_modules', $bootstrap_list); + } + + $this->bootstrapModules = array_keys($bootstrap_list); + return $this->bootstrapModules; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::buildModuleDependencies(). + */ + public function buildModuleDependencies($files) { + foreach ($files as $filename => $file) { + $graph[$file->name]['edges'] = array(); + if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) { + foreach ($file->info['dependencies'] as $dependency) { + $dependency_data = $this->parseDependency($dependency); + $graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data; + } + } + } + $graph_object = new Graph($graph); + $graph = $graph_object->searchAndSort(); + foreach ($graph as $module => $data) { + $files[$module]->required_by = isset($data['reverse_paths']) ? $data['reverse_paths'] : array(); + $files[$module]->requires = isset($data['paths']) ? $data['paths'] : array(); + $files[$module]->sort = $data['weight']; + } + return $files; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduleExists(). + */ + public function moduleExists($module) { + return isset($this->moduleList[$module]); + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::loadModuleIncludes(). + */ + public function loadModuleIncludes($type, $name = NULL) { + foreach ($this->moduleList as $module => $filename) { + module_load_include($type, $module, $name); + } + } + + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduleImplements(). + */ + public function moduleImplements($hook) { + // Fetch implementations from cache. + if (empty($this->implementations)) { + $implementations = $this->bootstrapCache->get('module_implements'); + if ($implementations === FALSE) { + $this->implementations = array(); + } + else { + $this->implementations = $implementations->data; + } + } + + if (!isset($this->implementations[$hook])) { + // 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. + $this->implementations['#write_cache'] = TRUE; + $hook_info = $this->moduleHookInfo(); + $this->implementations[$hook] = array(); + foreach ($this->moduleList as $module => $filename) { + $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']); + // Since module_hook() may needlessly try to load the include file again, + // function_exists() is used directly here. + if (function_exists($module . '_' . $hook)) { + $this->implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE; + } + } + // Allow modules to change the weight of specific implementations but avoid + // an infinite loop. + if ($hook != 'module_implements_alter') { + $this->alter('module_implements', $this->implementations[$hook], $hook); + } + } + else { + foreach ($this->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"); + } + // It is possible that a module removed a hook implementation without the + // implementations cache being rebuilt yet, so we check whether the + // function exists on each request to avoid undefined function errors. + // Since module_hook() may needlessly try to load the include file again, + // function_exists() is used directly here. + if (!function_exists($module . '_' . $hook)) { + // Clear out the stale implementation from the cache and force a cache + // refresh to forget about no longer existing hook implementations. + unset($this->implementations[$hook][$module]); + $this->implementations['#write_cache'] = TRUE; + } + } + } + return array_keys($this->implementations[$hook]); + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::getModuleHookImplementations(). + */ + public function getModuleHookImplementations() { + if (empty($this->implementations)) { + return array(); + } + return $this->implementations; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduleImplementsReset(). + */ + public function moduleImplementsReset() { + // 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 + // $this->bootstrapCache->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. + $this->implementations = NULL; + $this->bootstrapCache->set('module_implements', array()); + $this->hookInfo = NULL; + $this->alterFunctions = NULL; + $this->bootstrapCache->delete('hook_info'); + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduleHookInfo(). + */ + public function moduleHookInfo() { + // When this function is indirectly invoked from bootstrap_invoke_all() prior + // to all modules being loaded, we do not want to cache an incomplete + // hook_hookInfo() result, so instead return an empty array. This requires + // bootstrap hook implementations to reside in the .module file, which is + // optimal for performance anyway. + if (!$this->loaded) { + return array(); + } + if (!isset($this->hookInfo)) { + $this->hookInfo = array(); + $cache = $this->bootstrapCache->get('hook_info'); + if ($cache === FALSE) { + // Rebuild the cache and save it. + // We can't use $this->moduleInvokeAll() here or it would cause an infinite + // loop. + foreach ($this->moduleList as $module => $filename) { + $function = $module . '_hook_info'; + if (function_exists($function)) { + $result = $function(); + + if (isset($result) && is_array($result)) { + $this->hookInfo = array_merge_recursive($this->hookInfo, $result); + } + } + } + // We can't use $this->alter() for the same reason as above. + foreach ($this->moduleList as $module => $filename) { + $function = $module . '_hook_info_alter'; + if (function_exists($function)) { + $function($this->hookInfo); + } + } + $this->bootstrapCache->set('hook_info', $this->hookInfo); + } + else { + $this->hookInfo = $cache->data; + } + } + + return $this->hookInfo; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduleImplementsWriteCache(). + */ + public function writeModuleImplementationsCache() { + if (isset($this->implementations['#write_cache'])) { + unset($this->implementations['#write_cache']); + $this->bootstrapCache->set('module_implements', $this->implementations); + } + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduleInvokeAll(). + */ + public function moduleInvokeAll($hook, $args) { + $return = array(); + foreach ($this->moduleImplements($hook) as $module) { + $function = $module . '_' . $hook; + if (function_exists($function)) { + $result = call_user_func_array($function, $args); + if (isset($result) && is_array($result)) { + $return = array_merge_recursive($return, $result); + } + elseif (isset($result)) { + $return[] = $result; + } + } + } + + return $return; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::alter(). + */ + public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { + // 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; + } + + // Some alter hooks are invoked many times per page request, so store the + // list of functions to call, and on subsequent calls, iterate through them + // quickly. + if (!isset($this->alterFunctions[$cid])) { + $this->alterFunctions[$cid] = array(); + $hook = $type . '_alter'; + $modules = $this->moduleImplements($hook); + if (!isset($extra_types)) { + // For the more common case of a single hook, we do not need to call + // function_exists(), since $this->moduleImplements() returns only modules with + // implementations. + foreach ($modules as $module) { + $this->alterFunctions[$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, $this->moduleImplements($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. $this->moduleImplements() can only return ordered + // implementations of a single hook. To get the ordered implementations + // of multiple hooks, we mimic the $this->moduleImplements() logic of first + // ordering by $this->getModuleList(), and then calling + // $this->alter('module_implements'). + if (array_diff($extra_modules, $modules)) { + // Merge the arrays and order by getModuleList(). + $modules = array_intersect(array_keys($this->moduleList), array_merge($modules, $extra_modules)); + // Since $this->moduleImplements() already took care of loading the necessary + // include files, we can safely pass FALSE for the array values. + $implementations = array_fill_keys($modules, FALSE); + // Let modules adjust the order solely based on the primary hook. This + // ensures the same module order regardless of whether this if block + // runs. Calling $this->alter() recursively in this way does not result + // in an infinite loop, because this call is for a single $type, so we + // won't end up in this code block again. + $this->alter('module_implements', $implementations, $hook); + $modules = array_keys($implementations); + } + 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)) { + $this->alterFunctions[$cid][] = $function; + } + foreach ($extra_types as $extra_type) { + $function = $module . '_' . $extra_type . '_alter'; + if (function_exists($function)) { + $this->alterFunctions[$cid][] = $function; + } + } + } + } + // 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)) { + $this->alterFunctions[$cid][] = $function; + } + if (isset($extra_types)) { + foreach ($extra_types as $extra_type) { + $function = $theme_key . '_' . $extra_type . '_alter'; + if (function_exists($function)) { + $this->alterFunctions[$cid][] = $function; + } + } + } + } + } + } + + foreach ($this->alterFunctions[$cid] as $function) { + $function($data, $context1, $context2); + } + } + + /** + * Parses a dependency for comparison by drupal_check_incompatibility(). + * + * @param $dependency + * A dependency string, for example 'foo (>=8.x-4.5-beta5, 3.x)'. + * + * @return + * An associative array with three keys: + * - 'name' includes the name of the thing to depend on (e.g. 'foo'). + * - 'original_version' contains the original version string (which can be + * used in the UI for reporting incompatibilities). + * - 'versions' is a list of associative arrays, each containing the keys + * 'op' and 'version'. 'op' can be one of: '=', '==', '!=', '<>', '<', + * '<=', '>', or '>='. 'version' is one piece like '4.5-beta3'. + * Callers should pass this structure to drupal_check_incompatibility(). + * + * @see drupal_check_incompatibility() + */ + protected function parseDependency($dependency) { + // We use named subpatterns and support every op that version_compare + // supports. Also, op is optional and defaults to equals. + $p_op = '(?P!=|==|=|<|<=|>|>=|<>)?'; + // Core version is always optional: 8.x-2.x and 2.x is treated the same. + $p_core = '(?:' . preg_quote(DRUPAL_CORE_COMPATIBILITY) . '-)?'; + $p_major = '(?P\d+)'; + // By setting the minor version to x, branches can be matched. + $p_minor = '(?P(?:\d+|x)(?:-[A-Za-z]+\d+)?)'; + $value = array(); + $parts = explode('(', $dependency, 2); + $value['name'] = trim($parts[0]); + if (isset($parts[1])) { + $value['original_version'] = ' (' . $parts[1]; + foreach (explode(',', $parts[1]) as $version) { + if (preg_match("/^\s*$p_op\s*$p_core$p_major\.$p_minor/", $version, $matches)) { + $op = !empty($matches['operation']) ? $matches['operation'] : '='; + if ($matches['minor'] == 'x') { + // Drupal considers "2.x" to mean any version that begins with + // "2" (e.g. 2.0, 2.9 are all "2.x"). PHP's version_compare(), + // on the other hand, treats "x" as a string; so to + // version_compare(), "2.x" is considered less than 2.0. This + // means that >=2.x and <2.x are handled by version_compare() + // as we need, but > and <= are not. + if ($op == '>' || $op == '<=') { + $matches['major']++; + } + // Equivalence can be checked by adding two restrictions. + if ($op == '=' || $op == '==') { + $value['versions'][] = array('op' => '<', 'version' => ($matches['major'] + 1) . '.x'); + $op = '>='; + } + } + $value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']); + } + } + } + return $value; + } +} diff --git a/core/lib/Drupal/Core/ExtensionHandlerInterface.php b/core/lib/Drupal/Core/ExtensionHandlerInterface.php new file mode 100644 index 0000000..1ff835b --- /dev/null +++ b/core/lib/Drupal/Core/ExtensionHandlerInterface.php @@ -0,0 +1,200 @@ +moduleImplements() + */ + public function writeModuleImplementationsCache(); + + /** + * Invokes 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. + */ + public function moduleInvokeAll($hook, $args); + + /** + * Passes alterable variables to specific hook_TYPE_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', + * ); + * $this->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, + * ); + * $this->alter('mymodule_data', $data, $context); + * @endcode + * + * @param $type + * A string describing the type of the alterable $data. 'form', 'links', + * '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 $this->alter() to + * execute both hook_form_alter() and hook_form_FORM_ID_alter() + * implementations, it passes array('form', 'form_' . $form_id) for $type. + * @param $data + * 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. + * @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 + * associative array as described above. + */ + public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL); +} diff --git a/core/lib/Drupal/Core/ExtensionHandlerMinimal.php b/core/lib/Drupal/Core/ExtensionHandlerMinimal.php new file mode 100644 index 0000000..f62ae23 --- /dev/null +++ b/core/lib/Drupal/Core/ExtensionHandlerMinimal.php @@ -0,0 +1,141 @@ +moduleList = $module_list; + } + + /** + * Overrides Drupal\Core\ExtensionHandler::moduleImplements(). + */ + public function moduleImplements($hook) { + // Fetch implementations from cache. + if (empty($this->implementations)) { + $this->implementations = array(); + } + if (!isset($this->implementations[$hook])) { + // 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. + $this->implementations['#write_cache'] = TRUE; + $hook_info = $this->moduleHookInfo(); + $this->implementations[$hook] = array(); + foreach ($this->moduleList as $module => $filename) { + $include_file = isset($hook_info[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']); + // Since module_hook() may needlessly try to load the include file again, + // function_exists() is used directly here. + if (function_exists($module . '_' . $hook)) { + $this->implementations[$hook][$module] = $include_file ? $hook_info[$hook]['group'] : FALSE; + } + } + // Allow modules to change the weight of specific implementations but avoid + // an infinite loop. + if ($hook != 'module_implements_alter') { + $this->alter('module_implements', $this->implementations[$hook], $hook); + } + } + else { + foreach ($this->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"); + } + // It is possible that a module removed a hook implementation without the + // implementations cache being rebuilt yet, so we check whether the + // function exists on each request to avoid undefined function errors. + // Since module_hook() may needlessly try to load the include file again, + // function_exists() is used directly here. + if (!function_exists($module . '_' . $hook)) { + // Clear out the stale implementation from the cache and force a cache + // refresh to forget about no longer existing hook implementations. + unset($this->implementations[$hook][$module]); + $this->implementations['#write_cache'] = TRUE; + } + } + } + + return array_keys($this->implementations[$hook]); + } + + /** + * Overrides Drupal\Core\ExtensionHandler::moduleImplementsReset(). + */ + public function moduleImplementsReset() { + // 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('bootstrap')->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. + $this->implementations = NULL; + $this->hookInfo = NULL; + $this->alterFunctions = NULL; + } + + /** + * Overrides Drupal\Core\ExtensionHandler::moduleHookInfo(). + */ + public function moduleHookInfo() { + // When this function is indirectly invoked from bootstrap_invoke_all() prior + // to all modules being loaded, we do not want to cache an incomplete + // hook_hook_info() result, so instead return an empty array. This requires + // bootstrap hook implementations to reside in the .module file, which is + // optimal for performance anyway. + if (!$this->loaded) { + return array(); + } + + if (!isset($this->hookInfo)) { + $this->hookInfo = array(); + // We can't use $this->moduleInvokeAll() here or it would cause an infinite + // loop. + foreach ($this->moduleList as $module => $filename) { + $function = $module . '_hook_info'; + if (function_exists($function)) { + $result = $function(); + if (isset($result) && is_array($result)) { + $this->hookInfo = array_merge_recursive($this->hookInfo, $result); + } + } + } + // We can't use $this->alter() for the same reason as above. + foreach ($this->moduleList as $module => $filename) { + $function = $module . '_hook_info_alter'; + if (function_exists($function)) { + $function($this->hookInfo); + } + } + } + return $this->hookInfo; + } + + /** + * Overrides Drupal\Core\ExtensionHandler::moduleImplementsWriteCache(). + */ + public function writeModuleImplementationsCache() { + } +} diff --git a/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php index 6f7a86e..e74c4c6 100644 --- a/core/lib/Drupal/Core/Routing/RouteBuilder.php +++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php @@ -13,6 +13,7 @@ use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; +use Drupal\Core\ExtensionHandlerInterface; use Drupal\Core\Lock\LockBackendInterface; /** @@ -45,6 +46,13 @@ class RouteBuilder { protected $dispatcher; /** + * The extension handler for retieving the list of enabled modules. + * + * @var \Drupal\Core\ExtensionHandlerInterface + */ + protected $extensionHandler; + + /** * Construcs the RouteBuilder using the passed MatcherDumperInterface. * * @param \Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface $dumper @@ -54,10 +62,11 @@ class RouteBuilder { * @param \Symfony\Component\EventDispatcherEventDispatcherInterface * The event dispatcher to notify of routes. */ - public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher) { + public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher, ExtensionHandlerInterface $extension_handler) { $this->dumper = $dumper; $this->lock = $lock; $this->dispatcher = $dispatcher; + $this->extensionHandler = $extension_handler; } /** @@ -76,11 +85,9 @@ public function rebuild() { // We need to manually call each module so that we can know which module // a given item came from. - // @todo Use an injected Extension service rather than module_list(): - // http://drupal.org/node/1331486. - foreach (module_list() as $module) { + foreach ($this->extensionHandler->getModuleList() as $module => $filename) { $collection = new RouteCollection(); - $routing_file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . '/' . $module . '.routing.yml'; + $routing_file = DRUPAL_ROOT . '/' . dirname($filename) . '/' . $module . '.routing.yml'; if (file_exists($routing_file)) { $routes = $parser->parse(file_get_contents($routing_file)); if (!empty($routes)) { diff --git a/core/lib/Drupal/Core/Utility/ThemeRegistry.php b/core/lib/Drupal/Core/Utility/ThemeRegistry.php index bc9691c..eb2dd80 100644 --- a/core/lib/Drupal/Core/Utility/ThemeRegistry.php +++ b/core/lib/Drupal/Core/Utility/ThemeRegistry.php @@ -42,12 +42,14 @@ class ThemeRegistry extends CacheArray { * The bin to cache the array. * @param array $tags * (optional) The tags to specify for the cache item. + * @param bool $modules_loaded + * Whether all modules have already been loaded. */ - function __construct($cid, $bin, $tags) { + function __construct($cid, $bin, $tags, $modules_loaded = FALSE) { $this->cid = $cid; $this->bin = $bin; $this->tags = $tags; - $this->persistable = module_load_all(NULL) && $_SERVER['REQUEST_METHOD'] == 'GET'; + $this->persistable = $modules_loaded && $_SERVER['REQUEST_METHOD'] == 'GET'; $data = array(); if ($this->persistable && $cached = cache($this->bin)->get($this->cid)) { diff --git a/core/modules/breakpoint/breakpoint.install b/core/modules/breakpoint/breakpoint.install index 0e6bc30..9fd1054 100644 --- a/core/modules/breakpoint/breakpoint.install +++ b/core/modules/breakpoint/breakpoint.install @@ -18,6 +18,5 @@ function breakpoint_enable() { _breakpoint_theme_enabled(array_keys($themes)); // Import breakpoints from modules. - $modules = module_list(); - _breakpoint_modules_enabled(array_keys($modules)); + _breakpoint_modules_enabled(array_keys(drupal_container()->get('extension_handler')->getModuleList())); } diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php index ab03d41..4b40e7d 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php @@ -72,6 +72,7 @@ function testCommentEnable() { $edit = array(); $edit['modules[Core][comment][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); $this->resetAll(); $this->assertFalse(module_exists('comment'), 'Comment module disabled.'); @@ -80,11 +81,14 @@ function testCommentEnable() { $edit['modules[Core][book][enable]'] = 'book'; $edit['modules[Core][poll][enable]'] = 'poll'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); // Now enable the comment module. $edit = array(); $edit['modules[Core][comment][enable]'] = 'comment'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); + $this->resetAll(); $this->assertTrue(module_exists('comment'), 'Comment module enabled.'); diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php index 573ce93..813a3a3 100644 --- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php +++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php @@ -20,7 +20,7 @@ class EditTestBase extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('system', 'entity', 'field_test', 'field', 'number', 'text', 'edit', 'edit_test'); + public static $modules = array('system', 'entity', 'number', 'text', 'edit', 'edit_test'); /** * Sets the default field storage backend for fields created during tests. diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php index 8b0ce69..93e10c7 100644 --- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php +++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php @@ -14,6 +14,7 @@ */ class EntityDisplayTest extends DrupalUnitTestBase { + public static $install = array('system', 'entity', 'field'); public static $modules = array('entity_test'); public static function getInfo() { @@ -24,12 +25,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - - $this->enableModules(array('system', 'entity', 'field')); - } - /** * Tests basic CRUD operations on EntityDisplay objects. */ diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 01b0122..3059316 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -504,7 +504,7 @@ function field_modules_disabled($modules) { function field_sync_field_status() { // Refresh the 'active' and 'storage_active' columns according to the current // set of enabled modules. - $modules = module_list(); + $modules = array_keys(drupal_container()->get('extension_handler')->getModuleList()); foreach ($modules as $module_name) { field_associate_fields($module_name); } diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php index 7b0bef7..4390600 100644 --- a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php +++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php @@ -108,7 +108,7 @@ function testEnableForumField() { $edit['modules[Core][forum][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); - system_list_reset(); + $this->rebuildContainer(); $this->assertFalse(module_exists('forum'), 'Forum module is not enabled.'); // Attempt to re-enable the Forum module and ensure it does not try to @@ -117,7 +117,7 @@ function testEnableForumField() { $edit['modules[Core][forum][enable]'] = 'forum'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); - system_list_reset(); + $this->rebuildContainer(); $this->assertTrue(module_exists('forum'), 'Forum module is enabled.'); } diff --git a/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php index c4422aa..b22eb63 100644 --- a/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php +++ b/core/modules/jsonld/lib/Drupal/jsonld/Tests/RdfSchemaSerializationTest.php @@ -15,6 +15,8 @@ class RdfSchemaSerializationTest extends DrupalUnitTestBase { + public static $install = array('system'); + public static function getInfo() { return array( 'name' => 'Site schema JSON-LD serialization', @@ -27,9 +29,6 @@ public static function getInfo() { * Tests the serialization of site schemas. */ function testSchemaSerialization() { - // In order to use url() the url_alias table must be installed, so system - // is enabled. - $this->enableModules(array('system')); $entity_type = $bundle = 'entity_test'; diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php index 436b371..795d4d5 100644 --- a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php +++ b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php @@ -139,7 +139,7 @@ protected function languageNegotiationUpdate($op = 'enable') { $function = "module_{$op}"; $function($modules); // Reset hook implementation cache. - module_implements_reset(); + drupal_container()->get('extension_handler')->moduleImplementsReset(); } drupal_static_reset('language_types_info'); diff --git a/core/modules/language/lib/Drupal/language/Tests/Views/LanguageTestBase.php b/core/modules/language/lib/Drupal/language/Tests/Views/LanguageTestBase.php index e84844b..002072e 100644 --- a/core/modules/language/lib/Drupal/language/Tests/Views/LanguageTestBase.php +++ b/core/modules/language/lib/Drupal/language/Tests/Views/LanguageTestBase.php @@ -15,11 +15,11 @@ */ abstract class LanguageTestBase extends ViewUnitTestBase { + public static $install = array('system', 'language'); + protected function setUp() { parent::setUp(); - $this->enableModules(array('system', 'language')); - // Create another language beside English. $language = new Language(array('langcode' => 'xx-lolspeak', 'name' => 'Lolspeak')); language_save($language); diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php index a6c4ea9..c5de29e 100644 --- a/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php +++ b/core/modules/layout/lib/Drupal/layout/Plugin/Derivative/Layout.php @@ -10,6 +10,7 @@ use DirectoryIterator; use Drupal\Component\Plugin\Derivative\DerivativeInterface; use Drupal\Core\Config\FileStorage; +use Drupal\Core\ExtensionHandlerInterface; /** * Layout plugin derivative definition. @@ -58,11 +59,13 @@ public function getDerivativeDefinitions(array $base_plugin_definition) { $available_layout_providers = array(); // Add all modules as possible layout providers. - foreach (module_list() as $module) { + // @todo Change this class so that it gets the extension handler injected + // into it. + foreach (drupal_container()->get('extension_handler')->getModuleList() as $module => $filename) { $available_layout_providers[$module] = array( 'type' => 'module', 'provider' => $module, - 'dir' => drupal_get_path('module', $module), + 'dir' => dirname($filename), ); } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php index 96e9017..96856ec 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php @@ -50,18 +50,6 @@ */ public static $modules = array(); - /** - * Fixed module list being used by this test. - * - * @var array - * An associative array containing the required data for the $fixed_list - * argument of module_list(). - * - * @see UnitTestBase::setUp() - * @see UnitTestBase::enableModules() - */ - private $moduleList = array(); - private $moduleFiles; private $themeFiles; private $themeData; @@ -104,17 +92,27 @@ protected function setUp() { $this->kernel = new DrupalKernel('testing', TRUE, drupal_classloader(), FALSE); $this->kernel->boot(); - // Ensure that the module list is initially empty. - $this->moduleList = array(); // Collect and set a fixed module list. $class = get_class($this); $modules = array(); + $install = array(); while ($class) { + if (property_exists($class, 'install')) { + $install = array_merge($install, $class::$install); + } if (property_exists($class, 'modules')) { $modules = array_merge($modules, $class::$modules); } $class = get_parent_class($class); } + $system = array_search('system', $install); + if ($system !== FALSE) { + unset($install[$system]); + array_unshift($install, 'system'); + } + if ($install) { + $this->enableModules($install); + } $this->enableModules($modules, FALSE); } @@ -199,6 +197,9 @@ protected function installSchema($module, $table) { * enables the new modules in the system.module configuration only, but that * has no effect, since we are operating with a fixed module list. * + * Typically, the $modules static property should be used instead. + * Particularly, 'system' must not be passed to this function manually. + * * @param array $modules * A list of modules to enable. Dependencies are not resolved; i.e., * multiple modules have to be specified with dependent modules first. @@ -211,23 +212,18 @@ protected function installSchema($module, $table) { * implementation that is able to manage a fixed module list. */ protected function enableModules(array $modules, $install = TRUE) { - // Set the modules in the fixed module_list(). - $new_enabled = array(); + // Explicitly set the list of modules in the extension handler. + $enabled = $this->container->get('extension_handler')->getModuleList(); foreach ($modules as $module) { - $this->moduleList[$module]['filename'] = drupal_get_filename('module', $module); - $new_enabled[$module] = dirname($this->moduleList[$module]['filename']); - module_list(NULL, $this->moduleList); - + $enabled[$module] = drupal_get_filename('module', $module); // Call module_enable() to enable (install) the new module. if ($install) { module_enable(array($module), FALSE); } + $this->container->get('extension_handler')->setModuleList($enabled); } - // Otherwise, only ensure that the new modules are loaded. - if (!$install) { - module_load_all(FALSE, TRUE); - module_implements_reset(); - } + $this->container->get('extension_handler')->moduleImplementsReset(); + $this->container->get('extension_handler')->reloadModules(); } } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 0fb4e39..ceefb2b 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -1020,10 +1020,6 @@ protected function tearDown() { drupal_valid_test_ua($this->originalPrefix); } - // Reset module list and module load status. - module_list_reset(); - module_load_all(FALSE, TRUE); - // Restore original shutdown callbacks. $callbacks = &drupal_register_shutdown_function(); $callbacks = $this->originalShutdownCallbacks; diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php index 5813ffd..fe25549 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php @@ -39,7 +39,7 @@ function testSetUp() { // Verify that specified $modules have been loaded. $this->assertTrue(function_exists('entity_test_permission'), "$module.module was loaded."); // Verify that there is a fixed module list. - $this->assertIdentical(module_list(), array($module => $module)); + $this->assertIdentical(array_keys(drupal_container()->get('extension_handler')->getModuleList()), array($module)); $this->assertIdentical(module_implements('permission'), array($module)); // Verify that no modules have been installed. @@ -54,9 +54,9 @@ function testEnableModulesLoad() { // Verify that the module does not exist yet. $this->assertFalse(module_exists($module), "$module module not found."); - $list = module_list(); - $this->assertFalse(in_array($module, $list), "$module module in module_list() not found."); - $list = module_list('permission'); + $list = array_keys(drupal_container()->get('extension_handler')->getModuleList()); + $this->assertFalse(in_array($module, $list), "$module module not found in the extension handler's module list."); + $list = module_implements('permission'); $this->assertFalse(in_array($module, $list), "{$module}_permission() in module_implements() not found."); // Enable the module. @@ -64,9 +64,9 @@ function testEnableModulesLoad() { // Verify that the module exists. $this->assertTrue(module_exists($module), "$module module found."); - $list = module_list(); - $this->assertTrue(in_array($module, $list), "$module module in module_list() found."); - $list = module_list('permission'); + $list = array_keys(drupal_container()->get('extension_handler')->getModuleList()); + $this->assertTrue(in_array($module, $list), "$module module found in the extension handler's module list."); + $list = module_implements('permission'); $this->assertTrue(in_array($module, $list), "{$module}_permission() in module_implements() found."); } @@ -83,9 +83,9 @@ function testEnableModulesInstall() { // Verify that the module does not exist yet. $this->assertFalse(module_exists($module), "$module module not found."); - $list = module_list(); - $this->assertFalse(in_array($module, $list), "$module module in module_list() not found."); - $list = module_list('permission'); + $list = array_keys(drupal_container()->get('extension_handler')->getModuleList()); + $this->assertFalse(in_array($module, $list), "$module module not found in the extension handler's module list."); + $list = module_implements('permission'); $this->assertFalse(in_array($module, $list), "{$module}_permission() in module_implements() not found."); $this->assertFalse(db_table_exists($table), "'$table' database table not found."); @@ -97,9 +97,9 @@ function testEnableModulesInstall() { // Verify that the enabled module exists. $this->assertTrue(module_exists($module), "$module module found."); - $list = module_list(); - $this->assertTrue(in_array($module, $list), "$module module in module_list() found."); - $list = module_list('permission'); + $list = array_keys(drupal_container()->get('extension_handler')->getModuleList()); + $this->assertTrue(in_array($module, $list), "$module module found in the extension handler's module list."); + $list = module_implements('permission'); $this->assertTrue(in_array($module, $list), "{$module}_permission() in module_implements() found."); $this->assertTrue(db_table_exists($table), "'$table' database table found."); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php index 648aa42..d7bd225 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php @@ -56,9 +56,6 @@ protected function setUp() { $conf = array(); drupal_static_reset(); - // Enforce an empty module list. - module_list(NULL, array()); - $conf['file_public_path'] = $this->public_files_directory; // Change the database prefix. diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 4e3b101..bbc10ce 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -723,6 +723,7 @@ protected function setUp() { foreach ($variables as $name => $value) { $GLOBALS['conf'][$name] = $value; } + // Execute the non-interactive installer. require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; install_drupal($settings); @@ -830,8 +831,6 @@ protected function tearDown() { // Reload module list and implementations to ensure that test module hooks // aren't called after tests. system_list_reset(); - module_list_reset(); - module_implements_reset(); // Reset the Field API. field_cache_clear(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php index 8fa84fe..63edb99 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/DependencyTest.php @@ -36,7 +36,7 @@ function testEnableWithoutDependency() { $this->drupalPost(NULL, NULL, t('Continue')); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); - + $this->rebuildContainer(); $this->assertModules(array('translation_entity', 'language'), TRUE); // Assert that the language tables were enabled. @@ -65,6 +65,7 @@ function testMissingModules() { // Confirm. $this->drupalPost(NULL, NULL, t('Continue')); + $this->rebuildContainer(); // Verify that the module has been disabled. $this->assertModules(array('system_dependencies_test'), FALSE); } @@ -154,6 +155,7 @@ function testModuleEnableOrder() { $edit['modules[Core][poll][enable]'] = 'poll'; $edit['modules[Core][php][enable]'] = 'php'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); $this->assertModules(array('forum', 'poll', 'php', 'comment', 'history', 'taxonomy', 'options'), TRUE); // Check the actual order which is saved by module_test_modules_enabled(). @@ -169,14 +171,17 @@ function testUninstallDependents() { $edit = array('modules[Core][forum][enable]' => 'forum'); $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->drupalPost(NULL, array(), t('Continue')); + $this->rebuildContainer(); $this->assertModules(array('forum'), TRUE); // Disable forum and comment. Both should now be installed but disabled. $edit = array('modules[Core][forum][enable]' => FALSE); $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); $this->assertModules(array('forum'), FALSE); $edit = array('modules[Core][comment][enable]' => FALSE); $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); $this->assertModules(array('comment'), FALSE); // Check that the taxonomy module cannot be uninstalled. diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php index 638c065..ae829db 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/EnableDisableTest.php @@ -43,7 +43,7 @@ function testEnableDisable() { // Remove already enabled modules (via installation profile). // @todo Remove this after removing all dependencies from Testing profile. - foreach (module_list() as $dependency) { + foreach ($this->container->get('extension_handler')->getModuleList() as $dependency => $filename) { // Exclude required modules. Only installation profile "suggestions" can // be disabled and uninstalled. if (isset($modules[$dependency])) { @@ -95,10 +95,12 @@ function testEnableDisable() { $package = $module->info['package']; $edit['modules[' . $package . '][' . $name . '][enable]'] = $name; $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); // Handle the case where modules were installed along with this one and // where we therefore hit a confirmation screen. if (count($modules_to_enable) > 1) { $this->drupalPost(NULL, array(), t('Continue')); + $this->rebuildContainer(); } $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); @@ -169,6 +171,7 @@ function testEnableDisable() { $edit['modules[' . $module->info['package'] . '][' . $name . '][enable]'] = $name; } $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); } @@ -186,6 +189,7 @@ function assertSuccessfulDisableAndUninstall($module, $package = 'Core') { $edit = array(); $edit['modules[' . $package . '][' . $module . '][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); $this->assertModules(array($module), FALSE); @@ -204,6 +208,7 @@ function assertSuccessfulDisableAndUninstall($module, $package = 'Core') { $edit['uninstall[' . $module . ']'] = $module; $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall')); $this->drupalPost(NULL, NULL, t('Uninstall')); + $this->rebuildContainer(); $this->assertText(t('The selected modules have been uninstalled.'), 'Modules status has been updated.'); $this->assertModules(array($module), FALSE); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php index 85a4184..8787f9b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php @@ -25,7 +25,7 @@ public static function getInfo() { } /** - * The basic functionality of module_list(). + * The basic functionality of retrieving enabled modules. */ function testModuleList() { // Build a list of modules, sorted alphabetically. @@ -36,8 +36,8 @@ function testModuleList() { $module_list[] = 'standard'; sort($module_list); - // Compare this list to the one returned by module_list(). We expect them - // to match, since all default profile modules have a weight equal to 0 + // Compare this list to the one returned by the extension handler. We expect + // them to match, since all default profile modules have a weight equal to 0 // (except for block.module, which has a lower weight but comes first in // the alphabet anyway). $this->assertModuleList($module_list, t('Standard profile')); @@ -50,8 +50,7 @@ function testModuleList() { // Try to mess with the module weights. module_set_weight('contact', 20); - // Reset the module list. - system_list_reset(); + // Move contact to the end of the array. unset($module_list[array_search('contact', $module_list)]); $module_list[] = 'contact'; @@ -59,27 +58,26 @@ function testModuleList() { // Test the fixed list feature. $fixed_list = array( - 'system' => array('filename' => drupal_get_path('module', 'system')), - 'menu' => array('filename' => drupal_get_path('module', 'menu')), + 'system' => 'core/modules/system/system.module', + 'menu' => 'core/modules/menu/menu.module', ); - module_list(NULL, $fixed_list); + $this->container->get('extension_handler')->setModuleList($fixed_list); $new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list)); $this->assertModuleList($new_module_list, t('When using a fixed list')); - // Reset the module list. - module_list_reset(); - $this->assertModuleList($module_list, t('After reset')); } /** - * Assert that module_list() return the expected values. + * Assert that the extension handler returns the expected values. * * @param $expected_values * The expected values, sorted by weight and module name. */ protected function assertModuleList(Array $expected_values, $condition) { - $expected_values = array_combine($expected_values, $expected_values); - $this->assertEqual($expected_values, module_list(), format_string('@condition: module_list() returns correct results', array('@condition' => $condition))); + $expected_values = array_values(array_unique($expected_values)); + $enabled_modules = array_keys($this->container->get('extension_handler')->getModuleList()); + $enabled_modules = sort($enabled_modules); + $this->assertEqual($expected_values, $enabled_modules, format_string('@condition: extension handler returns correct results', array('@condition' => $condition))); } /** @@ -103,12 +101,13 @@ function testModuleImplements() { // already loaded when the cache is rebuilt. // For that activate the module_test which provides the file to load. module_enable(array('module_test')); - + $extension_handler = drupal_container()->get('extension_handler'); + $extension_handler->loadAllModules(); module_load_include('inc', 'module_test', 'module_test.file'); - $modules = module_implements('test_hook'); - $static = drupal_static('module_implements'); + $modules = $extension_handler->moduleImplements('test_hook'); $this->assertTrue(in_array('module_test', $modules), 'Hook found.'); - $this->assertEqual($static['test_hook']['module_test'], 'file', 'Include file detected.'); + $module_implementations = $extension_handler->getModuleHookImplementations(); + $this->assertEqual($module_implementations['test_hook']['module_test'], 'file', 'Include file detected.'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php index b7fd25c..1fc541f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php @@ -139,7 +139,6 @@ function assertNoModuleConfig($module) { * Expected module state. */ function assertModules(array $modules, $enabled) { - system_list_reset(); foreach ($modules as $module) { if ($enabled) { $message = 'Module "@module" is enabled.'; @@ -147,7 +146,7 @@ function assertModules(array $modules, $enabled) { else { $message = 'Module "@module" is not enabled.'; } - $this->assertEqual(module_exists($module), $enabled, format_string($message, array('@module' => $module))); + $this->assertEqual($this->container->get('extension_handler')->moduleExists($module), $enabled, format_string($message, array('@module' => $module))); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php b/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php index 2de5ad3..d83bdc3 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php @@ -55,8 +55,8 @@ function testMainContentFallback() { // Disable the block module. $edit['modules[Core][block][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); - system_list_reset(); $this->assertFalse(module_exists('block'), 'Block module disabled.'); // At this point, no region is filled and fallback should be triggered. @@ -89,8 +89,8 @@ function testMainContentFallback() { $edit = array(); $edit['modules[Core][block][enable]'] = 'block'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); + $this->rebuildContainer(); $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); - system_list_reset(); $this->assertTrue(module_exists('block'), 'Block module re-enabled.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/RegistryTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/RegistryTest.php index 7ea187f..1b683bb 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/RegistryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/RegistryTest.php @@ -40,7 +40,7 @@ function testRaceCondition() { // Directly instantiate the theme registry, this will cause a base cache // entry to be written in __construct(). - $registry = new ThemeRegistry($cid, 'cache', array('theme_registry' => TRUE)); + $registry = new ThemeRegistry($cid, 'cache', array('theme_registry' => TRUE), $this->container->get('extension_handler')->allModulesLoaded()); $this->assertTrue(cache()->get($cid), 'Cache entry was created.'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php index 5267300..1430f4d 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php @@ -175,9 +175,19 @@ function testRegistryRebuild() { $this->assertIdentical(theme('theme_test_foo', array('foo' => 'a')), 'a', 'The theme registry contains theme_test_foo.'); module_disable(array('theme_test'), FALSE); + // After enabling/disabling a module during a test, we need to rebuild the + // container and ensure the extension handler is loaded, otherwise theme() + // throws an exception. + $this->rebuildContainer(); + $this->container->get('extension_handler')->loadAllModules(); $this->assertIdentical(theme('theme_test_foo', array('foo' => 'b')), '', 'The theme registry does not contain theme_test_foo, because the module is disabled.'); module_enable(array('theme_test'), FALSE); + // After enabling/disabling a module during a test, we need to rebuild the + // container and ensure the extension handler is loaded, otherwise theme() + // throws an exception. + $this->rebuildContainer(); + $this->container->get('extension_handler')->loadAllModules(); $this->assertIdentical(theme('theme_test_foo', array('foo' => 'c')), 'c', 'The theme registry contains theme_test_foo again after re-enabling the module.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php index 8d4da51..2be9d12 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php @@ -82,7 +82,7 @@ public function testBasicMinimalUpgrade() { $this->assertFalse($result, 'No {menu_links} entry exists for user/autocomplete'); // Verify that all required modules are enabled. - $enabled = module_list(); + $enabled = $this->container->get('extension_handler')->getModuleList(); $required = array_filter(system_rebuild_module_data(), function ($data) { return !empty($data->info['required']); }); diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/ModulesDisabledUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/ModulesDisabledUpgradePathTest.php index c4c6f75..7034c01 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/ModulesDisabledUpgradePathTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/ModulesDisabledUpgradePathTest.php @@ -37,7 +37,7 @@ public function testDisabledUpgrade() { $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.'); // Get enabled modules. - $enabled = module_list(); + $enabled = drupal_container()->get('extension_handler')->getModuleList(); // Get all available modules. $available = system_rebuild_module_data(); // Filter out hidden test modules. 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 dd6703b..09f2b50 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php @@ -265,9 +265,8 @@ protected function performUpgrade($register_errors = TRUE) { // Reload module list for modules that are enabled in the test database // but not on the test client. - system_list_reset(); - module_implements_reset(); - module_load_all(FALSE, TRUE); + drupal_container()->get('extension_handler')->moduleImplementsReset(); + drupal_container()->get('extension_handler')->reloadModules(); // Rebuild the container and all caches. $this->rebuildContainer(); diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index c3c2ce4..21b1366 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -1204,7 +1204,7 @@ function system_modules_submit($form, &$form_state) { // Gets list of modules prior to install process, unsets $form_state['storage'] // so we don't get redirected back to the confirmation form. - $pre_install_list = module_list(); + $pre_install_list = drupal_container()->get('extension_handler')->getModuleList(); unset($form_state['storage']); // Reverse the 'enable' list, to order dependencies before dependents. @@ -1216,7 +1216,7 @@ function system_modules_submit($form, &$form_state) { // Gets module list after install process, flushes caches and displays a // message if there are changes. - $post_install_list = module_list(); + $post_install_list = drupal_container()->get('extension_handler')->getModuleList(); if ($pre_install_list != $post_install_list) { drupal_flush_all_caches(); drupal_set_message(t('The configuration options have been saved.')); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 8c7d72a..119d8b3 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -400,7 +400,7 @@ function system_requirements($phase) { ); // Check installed modules. - foreach (module_list() as $module) { + foreach (drupal_container()->get('extension_handler')->getModuleList() as $module => $filename) { $updates = drupal_get_schema_versions($module); if ($updates !== FALSE) { $default = drupal_get_installed_schema_version($module); diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 6d16229..d1dac40 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2741,7 +2741,7 @@ function system_get_info($type, $name = NULL) { $info = array(); if ($type == 'module') { $data = system_rebuild_module_data(); - foreach (module_list() as $module) { + foreach (drupal_container()->get('extension_handler')->getModuleList() as $module => $filename) { $info[$module] = $data[$module]->info; } } @@ -2884,7 +2884,7 @@ function system_rebuild_module_data() { $record->schema_version = SCHEMA_UNINSTALLED; $files[$module] = $record->filename; } - $modules = _module_build_dependencies($modules); + $modules = drupal_container()->get('extension_handler')->buildModuleDependencies($modules); $modules_cache = $modules; // Store filenames to allow system_list() and drupal_get_filename() to diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php index 43b807e..59327cf 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php @@ -22,7 +22,7 @@ class AreaTextTest extends ViewUnitTestBase { * @var array */ public static $testViews = array('test_view'); - + public static $install = array('system', 'filter'); public static function getInfo() { return array( 'name' => 'Area: Text', @@ -31,13 +31,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - - $this->enableModules(array('system')); - $this->enableModules(array('filter')); - } - public function testAreaText() { $view = views_get_view('test_view'); $view->setDisplay(); diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUrlTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUrlTest.php index 530d53b..e98577f 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUrlTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUrlTest.php @@ -14,6 +14,8 @@ */ class FieldUrlTest extends ViewUnitTestBase { + public static $install = array('system'); + /** * Views used by this test. * @@ -29,11 +31,6 @@ public static function getInfo() { ); } - public function setup() { - parent::setup(); - $this->enableModules(array('system')); - } - function viewsData() { $data = parent::viewsData(); $data['views_test_data']['name']['field']['id'] = 'url'; diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php index 05a42a4..96102ec 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterEqualityTest.php @@ -20,7 +20,7 @@ class FilterEqualityTest extends ViewUnitTestBase { * @var array */ public static $testViews = array('test_view'); - + public static $install = array('system', 'menu'); protected $column_map = array( 'views_test_data_name' => 'name', ); @@ -33,12 +33,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - - $this->enableModules(array('system')); - } - function viewsData() { $data = parent::viewsData(); $data['views_test_data']['name']['filter']['id'] = 'equality'; diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php index 6b6a8e8..adf33ac 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterInOperatorTest.php @@ -20,7 +20,7 @@ class FilterInOperatorTest extends ViewUnitTestBase { * @var array */ public static $testViews = array('test_view'); - + public static $install = array('system', 'menu'); protected $column_map = array( 'views_test_data_name' => 'name', 'views_test_data_age' => 'age', @@ -34,12 +34,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - - $this->enableModules(array('system')); - } - function viewsData() { $data = parent::viewsData(); $data['views_test_data']['age']['filter']['id'] = 'in_operator'; diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php index d497954..5985cd6 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterNumericTest.php @@ -20,7 +20,7 @@ class FilterNumericTest extends ViewUnitTestBase { * @var array */ public static $testViews = array('test_view'); - + public static $install = array('system', 'menu'); protected $column_map = array( 'views_test_data_name' => 'name', 'views_test_data_age' => 'age', @@ -34,12 +34,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - - $this->enableModules(array('system')); - } - function viewsData() { $data = parent::viewsData(); $data['views_test_data']['age']['filter']['allow empty'] = TRUE; diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php index 65c71e5..e7479b7 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FilterStringTest.php @@ -20,7 +20,7 @@ class FilterStringTest extends ViewUnitTestBase { * @var array */ public static $testViews = array('test_view'); - + public static $install = array('system', 'menu'); protected $column_map = array( 'views_test_data_name' => 'name', ); @@ -33,12 +33,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - - $this->enableModules(array('system')); - } - function viewsData() { $data = parent::viewsData(); $data['views_test_data']['name']['filter']['allow empty'] = TRUE; diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleMappingTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleMappingTest.php index c0db658..b837d58 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleMappingTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleMappingTest.php @@ -12,6 +12,8 @@ */ class StyleMappingTest extends StyleTestBase { + public static $install = array('system'); + /** * Views used by this test. * @@ -27,12 +29,6 @@ public static function getInfo() { ); } - public function setUp() { - parent::setUp(); - - $this->enableModules(array('system')); - } - /** * Verifies that the fields were mapped correctly. */ diff --git a/core/modules/views/lib/Drupal/views/Tests/TokenReplaceTest.php b/core/modules/views/lib/Drupal/views/Tests/TokenReplaceTest.php index c3c24b1..b573145 100644 --- a/core/modules/views/lib/Drupal/views/Tests/TokenReplaceTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/TokenReplaceTest.php @@ -12,6 +12,8 @@ */ class TokenReplaceTest extends ViewUnitTestBase { + public static $install = array('system'); + /** * Views used by this test. * @@ -27,12 +29,6 @@ public static function getInfo() { ); } - public function setUp() { - parent::setUp(); - - $this->enableModules(array('system')); - } - /** * Tests core token replacements generated from a view. */ diff --git a/core/scripts/dump-database-d6.sh b/core/scripts/dump-database-d6.sh index d39bb43..18453e6 100644 --- a/core/scripts/dump-database-d6.sh +++ b/core/scripts/dump-database-d6.sh @@ -44,7 +44,7 @@ ENDOFHEADER; -foreach (module_list() as $module) { +foreach (drupal_container()->get('extension_handler')->getModuleList() as $module => $filename) { $output .= " * - $module\n"; } $output .= " */\n\n"; diff --git a/core/scripts/dump-database-d7.sh b/core/scripts/dump-database-d7.sh index 3440fa2..8489520 100644 --- a/core/scripts/dump-database-d7.sh +++ b/core/scripts/dump-database-d7.sh @@ -45,7 +45,7 @@ ENDOFHEADER; -foreach (module_list() as $module) { +foreach (drupal_container()->get('extension_handler')->getModuleList() as $module => $filename) { $output .= " * - $module\n"; } $output .= " */\n\n"; diff --git a/core/update.php b/core/update.php index ff65c6e..2ea30f4 100644 --- a/core/update.php +++ b/core/update.php @@ -435,13 +435,14 @@ function update_check_requirements($skip_warnings = FALSE) { // Load module basics. include_once DRUPAL_ROOT . '/core/includes/module.inc'; - $module_list['system']['filename'] = 'core/modules/system/system.module'; - module_list(NULL, $module_list); - drupal_load('module', 'system'); + $module_list['system'] = 'core/modules/system/system.module'; + $extension_handler = drupal_container()->get('extension_handler'); + $extension_handler->setModuleList($module_list); + $extension_handler->loadModule('system'); - // Reset the module_implements() cache so that any new hook implementations + // Reset the module implementations cache so that any new hook implementations // in updated code are picked up. - module_implements_reset(); + $extension_handler->moduleImplementsReset(); // Set up $language, since the installer components require it. drupal_language_initialize();