diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 7149004..d72ab85 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1,7 +1,10 @@ get('extension_handler') once the installer is using a + * Kernel. + */ +function drupal_extension_handler($installer = FALSE) { + $extension_handler = &drupal_static(__FUNCTION__, NULL); + if (isset($extension_handler)) { + return $extension_handler; + } + if ($installer) { + $extension_handler = new InstallerExtensionHandler(); + } + else { + if (drupal_container()->has('extension_handler')) { + $extension_handler = drupal_container()->get('extension_handler'); + } + else { + $cache = CacheFactory::get('cache'); + $bootstrap_cache = CacheFactory::get('bootstrap'); + $connection = Database::getConnection(); + $extension_handler = new ExtensionHandler($connection, $cache, $bootstrap_cache); + } + } + + return $extension_handler; +} + +/** + * Determines which modules are implementing a hook. + * + * @see ExtensionHandler::moduleImplements(). + */ +function module_implements($hook) { + return drupal_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_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_extension_handler()->alter($type, $data, $context1, $context2); +} + +/** + * Determines whether a given module exists. + * + * @see ExtensionHandler::moduleExists(). + */ +function module_exists($module) { + return drupal_extension_handler()->moduleExists($module); +} + +/** + * Returns a list of currently active modules. + * + * @see ExtensionHandler::moduleList(). + */ +function module_list($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) { + return drupal_extension_handler()->moduleList($type, $fixed_list, $reset); +} + +/** + * Loads all the modules that have been enabled in the system table. + * + * @see ExtensionHandler::loadAll(). + */ +function module_load_all($bootstrap = FALSE, $reset = FALSE) { + return drupal_extension_handler()->loadAll($bootstrap, $reset); +} + +/** * Returns the state storage service. * * Use this to store machine-generated data, local to a specific environment @@ -2710,14 +2804,8 @@ function language($type, $reset = FALSE) { // When the language_manager service exists (is both defined and the 'request' // scope is active in the container), use it to get the language. Otherwise // return the default language. - try { - $language_manager = drupal_container()->get('language_manager', Container::NULL_ON_INVALID_REFERENCE); - } - catch (DependencyInjectionRuntimeException $e) { - } - - if (isset($language_manager)) { - return $language_manager->getLanguage($type); + if (drupal_container()->isScopeActive('request')) { + return drupal_container()->get('language_manager')->getLanguage($type); } else { if (!isset($languages[$type])) { diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index b1d3b50..94c393c 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -267,7 +267,8 @@ function install_begin_request(&$install_state) { conf_path(FALSE); drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); - + // Ensure that an InstallerExtensionHandler is used during installation. + drupal_extension_handler(TRUE); // A request object from the HTTPFoundation to tell us about the request. $request = Request::createFromGlobals(); @@ -1469,9 +1470,9 @@ 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'); // @todo The constructor parameters for the Kernel class are for environment, // e.g. 'prod', 'dev', and a boolean indicating whether it is in debug mode. // Drupal does not currently make use of either of these, though that may diff --git a/core/includes/install.inc b/core/includes/install.inc index db92987..648bfa6 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -424,9 +424,10 @@ function drupal_install_system() { ->save(); // Clear out module list and hook implementation statics. - system_list_reset(); - module_list_reset(); - module_implements_reset(); + $extension_handler = drupal_extension_handler(); + $extension_handler->systemListReset(); + $extension_handler->moduleListReset(); + $extension_handler->moduleImplementsReset(); config_install_default_config('module', 'system'); diff --git a/core/includes/module.inc b/core/includes/module.inc index 5a17873..587eec4 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -8,327 +8,6 @@ use Drupal\Component\Graph\Graph; /** - * 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) { - drupal_get_filename('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 - * The type of list to return: - * - module_enabled: All enabled modules. - * - bootstrap: All enabled modules required for bootstrap. - * - theme: All themes. - * - * @return - * An associative array of modules or themes, keyed by name. For $type - * 'bootstrap' and 'module_enabled', the array values equal the keys. - * 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() - * data. Consider to add a config($name, $cache = TRUE) argument to allow - * callers like system_list() to force-disable a possible configuration - * storage controller cache or some other way to circumvent it/take it over. - */ -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) { - drupal_classloader_register($name, dirname($filename)); - drupal_get_filename('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); - } - // 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 = config('system.module')->get('enabled'); - $module_files = state()->get('system.module.files'); - foreach ($enabled_modules as $name => $weight) { - // Build a list of all enabled modules. - $lists['module_enabled'][$name] = $name; - // Build a list of filenames so drupal_get_filename can use it. - $lists['filepaths'][] = array( - 'type' => 'module', - 'name' => $name, - 'filepath' => $module_files[$name], - ); - } - - // Build a list of themes. - $enabled_themes = 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']; - } - else { - // A plain theme is its own base theme. - $base_key = $key; - } - // Set the theme engine prefix. - $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; - } - cache('bootstrap')->set('system_list', $lists); - } - // To avoid a separate database lookup for the filepath, prime the - // drupal_get_filename() static cache with all enabled modules and themes. - foreach ($lists['filepaths'] as $item) { - drupal_get_filename($item['type'], $item['name'], $item['filepath']); - drupal_classloader_register($item['name'], dirname($item['filepath'])); - } - } - - return $lists[$type]; -} - -/** - * Resets all system_list() caches. - */ -function system_list_reset() { - drupal_static_reset('system_list'); - drupal_static_reset('system_rebuild_module_data'); - drupal_static_reset('list_themes'); - cache('bootstrap')->deleteMultiple(array('bootstrap_modules', 'system_list')); - cache()->delete('system_info'); - // Remove last known theme data state. - // This causes system_list() to call system_rebuild_theme_data() on its next - // invocation. When enabling a module that implements hook_system_info_alter() - // to inject a new (testing) theme or manipulate an existing theme, then that - // will cause system_list_reset() to be called, but theme data is not - // necessarily rebuilt afterwards. - // @todo Obsolete with proper installation status for themes. - state()->delete('system.theme.data'); -} - -/** - * 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 @@ -387,16 +66,6 @@ function module_load_include($type, $module, $name = NULL) { } /** - * 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. * * Definitions: @@ -498,8 +167,8 @@ function module_enable($module_list, $enable_dependencies = TRUE) { module_load_install($module); // Refresh the module list to include it. - system_list_reset(); - module_implements_reset(); + drupal_extension_handler()->systemListReset(); + drupal_extension_handler()->moduleImplementsReset(); _system_update_bootstrap_status(); // Refresh the schema to include it. drupal_get_schema(NULL, TRUE); @@ -625,8 +294,8 @@ function module_disable($module_list, $disable_dependents = TRUE) { if (!empty($invoke_modules)) { // Refresh the module list to exclude the disabled modules. - system_list_reset(); - module_implements_reset(); + drupal_extension_handler()->systemListReset(); + drupal_extension_handler()->moduleImplementsReset(); entity_info_cache_clear(); // Invoke hook_modules_disabled before disabling modules, // so we can still call module hooks to get information. @@ -761,7 +430,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_extension_handler()->moduleHookInfo(); if (isset($hook_info[$hook]['group'])) { module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']); if (function_exists($function)) { @@ -772,164 +441,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 what hooks are explicitly declared. - */ -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 @@ -952,39 +463,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". */ @@ -1009,172 +487,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. diff --git a/core/includes/schema.inc b/core/includes/schema.inc index f46739c..8aa3899 100644 --- a/core/includes/schema.inc +++ b/core/includes/schema.inc @@ -79,14 +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')) { - // 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'); - } + $extension_handler = drupal_extension_handler(); + // This function can be called very early in the bootstrap process, so + // we force the ExtensionHandler's system list to be refreshed to ensure + // that it contains the complete list of modules before we go on to call + // module_load_all_includes(). + $extension_handler->systemListReset(); + $extension_handler->loadAllIncludes('install'); require_once DRUPAL_ROOT . '/core/includes/common.inc'; // Invoke hook_schema for all modules. diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 3c4576a..d713f92 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -665,7 +665,7 @@ function list_themes($refresh = FALSE) { if ($refresh) { $list = array(); - system_list_reset(); + drupal_extension_handler()->systemListReset(); } if (empty($list)) { @@ -675,7 +675,7 @@ function list_themes($refresh = FALSE) { // Also check that the site is not in the middle of an install or update. if (!defined('MAINTENANCE_MODE')) { try { - $themes = system_list('theme'); + $themes = drupal_extension_handler()->systemList('theme'); } catch (Exception $e) { // If the database is not available, rebuild the theme data. diff --git a/core/includes/update.inc b/core/includes/update.inc index 6a7a1a3..99e7989 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; @@ -226,12 +227,14 @@ function update_prepare_d8_bootstrap() { ':schema_uninstalled' => SCHEMA_UNINSTALLED, )); $module_data = _system_rebuild_module_data(); + $list = array(); // Migrate each extension into configuration, varying by the extension's // status, and record its schema version. foreach ($result as $record) { if ($record->type == 'module') { if ($record->status && isset($module_data[$record->name])) { + $list[$record->name] = $module_data[$record->name]->filename; $module_config->set('enabled.' . $record->name, $record->weight); } else { @@ -240,6 +243,7 @@ function update_prepare_d8_bootstrap() { } elseif ($record->type == 'theme') { if ($record->status) { + $list[$record->name] = $module_data[$record->name]->filename; $theme_config->set('enabled.' . $record->name, 0); } else { @@ -259,7 +263,7 @@ function update_prepare_d8_bootstrap() { // Update the environment for the language bootstrap if needed. update_prepare_d8_language(); // Prime the classloader. - system_list('module_enabled'); + ExtensionHandler::systemListWarm($list); // Change language column to langcode in url_alias. if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) { @@ -451,10 +455,10 @@ function update_module_enable(array $modules) { // updates since the module's inception are executed in a core upgrade. $schema_store->set($module, 0); - // system_list_reset() is in module.inc but that would only be available + // drupal_extension_handler()->systemListReset() is in module.inc but that would only be available // once the variable bootstrap is done. require_once DRUPAL_ROOT . '/core/includes/module.inc'; - system_list_reset(); + drupal_extension_handler()->systemListReset(); // @todo: figure out what to do about hook_install() and hook_enable(). } } diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index f1e43d6..ed7dea8 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -35,6 +35,39 @@ public function build(ContainerBuilder $container) { $container->register('request', 'Symfony\Component\HttpFoundation\Request') ->setSynthetic(TRUE); + // Register the config services + // Register active configuration storage. + $container + ->register('config.cachedstorage.storage', 'Drupal\Core\Config\FileStorage') + ->addArgument(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY)); + // @todo Replace this with a cache.factory service plus 'config' argument. + $container + ->register('cache.config') + ->setFactoryClass('Drupal\Core\Cache\CacheFactory') + ->setFactoryMethod('get') + ->addArgument('config'); + + $container + ->register('config.storage', 'Drupal\Core\Config\CachedStorage') + ->addArgument(new Reference('config.cachedstorage.storage')) + ->addArgument(new Reference('cache.config')); + + // Register configuration object factory. + $container->register('config.subscriber.globalconf', 'Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber'); + $container->register('dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher') + ->addMethodCall('addSubscriber', array(new Reference('config.subscriber.globalconf'))); + $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory') + ->addArgument(new Reference('config.storage')) + ->addArgument(new Reference('dispatcher')); + + // Register staging configuration storage. + $container + ->register('config.storage.staging', 'Drupal\Core\Config\FileStorage') + ->addArgument(config_get_config_directory(CONFIG_STAGING_DIRECTORY)); + $container + ->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage') + ->addArgument('state'); + $container->register('dispatcher', 'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher') ->addArgument(new Reference('service_container')); $container->register('resolver', 'Drupal\Core\ControllerResolver') @@ -46,10 +79,6 @@ public function build(ContainerBuilder $container) { $container->register('language_manager', 'Drupal\Core\Language\LanguageManager') ->addArgument(new Reference('request')) ->setScope('request'); - $container->register('database', 'Drupal\Core\Database\Connection') - ->setFactoryClass('Drupal\Core\Database\Database') - ->setFactoryMethod('getConnection') - ->addArgument('default'); $container->register('database.slave', 'Drupal\Core\Database\Connection') ->setFactoryClass('Drupal\Core\Database\Database') ->setFactoryMethod('getConnection') @@ -61,6 +90,10 @@ public function build(ContainerBuilder $container) { ->addArgument(new Reference('database')) ->addArgument(new Reference('lock')); + $container + ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory') + ->addArgument(new Reference('database')); + $container->register('router.dumper', '\Drupal\Core\Routing\MatcherDumper') ->addArgument(new Reference('database')); $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder') diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 869d4ee..6d3d550 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -7,9 +7,12 @@ namespace Drupal\Core; +use Drupal\Core\Cache\CacheFactory; use Drupal\Core\CoreBundle; use Symfony\Component\HttpKernel\Kernel; +use Drupal\Core\Database\Database; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\ExtensionHandler; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; @@ -26,6 +29,16 @@ class DrupalKernel extends Kernel { /** + * ExtensionHandler instance holding the list of enabled modules. + */ + protected $extension_handler; + + /** + * The database connection used by the ExtensionHandler. + */ + protected $connection; + + /** * Overrides Kernel::init(). */ public function init() { @@ -36,6 +49,21 @@ public function init() { } /** + * Overrides Kernel::boot(). + */ + public function boot() { + // Instantiate an ExtensionHandler which the Kernel itself needs in order to + // find out which modules are enabled. In the buildContainer() method we + // register this and the database connection we pass to it as synthetic + // services to the container so that they do not need to be instantiated + // over again. + $this->connection = Database::getConnection(); + $this->extension_handler = new ExtensionHandler($this->connection, CacheFactory::get('cache'), CacheFactory::get('bootstrap')); + parent::boot(); + drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); + } + + /** * Returns an array of available bundles. */ public function registerBundles() { @@ -43,9 +71,7 @@ public function registerBundles() { new CoreBundle(), ); - // @todo Remove the necessity of calling system_list() to find out which - // bundles exist. See http://drupal.org/node/1331486 - $modules = array_keys(system_list('module_enabled')); + $modules = array_keys($this->extension_handler->systemList('module_enabled')); foreach ($modules as $module) { $camelized = ContainerBuilder::camelize($module); $class = "Drupal\\{$module}\\{$camelized}Bundle"; @@ -77,10 +103,15 @@ protected function initializeContainer() { protected function buildContainer() { $container = $this->getContainerBuilder(); - // Merge in the minimal bootstrap container. - if ($bootstrap_container = drupal_container()) { - $container->merge($bootstrap_container); - } + // Add our ExtensionHandler and database connection objects as synthetic + // services. + $container->register('extension_handler', 'Drupal\Core\ExtensionHandler') + ->setSynthetic(TRUE); + $container->set('extension_handler', $this->extension_handler); + $container->register('database', 'Drupal\Core\Database\Connection') + ->setSynthetic(TRUE); + $container->set('database', $this->connection); + foreach ($this->bundles as $bundle) { $bundle->build($container); } diff --git a/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php index 175ce79..51ee8fd 100644 --- a/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php @@ -30,7 +30,7 @@ class RequestCloseSubscriber implements EventSubscriberInterface { public function onTerminate(PostResponseEvent $event) { module_invoke_all('exit'); drupal_cache_system_paths(); - module_implements_write_cache(); + drupal_extension_handler()->moduleImplementsWriteCache(); 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..b789028 --- /dev/null +++ b/core/lib/Drupal/Core/ExtensionHandler.php @@ -0,0 +1,590 @@ +connection = $connection; + $this->cache = $cache; + $this->bootstrapCache = $bootstrapCache; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::LoadAll(). + */ + public function loadAll($bootstrap = FALSE, $reset = FALSE) { + if ($reset) { + $this->loaded = FALSE; + } + if (isset($bootstrap) && !$this->loaded) { + $type = $bootstrap ? 'bootstrap' : 'module_enabled'; + foreach ($this->moduleList($type) as $module) { + drupal_load('module', $module); + } + // $has_run will be TRUE if $bootstrap is FALSE. + $this->loaded = !$bootstrap; + } + return $this->loaded; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduelList(). + */ + public function moduleList($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) { + if ($reset) { + $this->moduleList = 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 $moduleList in order + // to not duplicate the static cache of drupal_extension_handler()->systemList(). + $list = $this->moduleList; + + if (isset($fixed_list)) { + $this->moduleList = array(); + foreach ($fixed_list as $name => $module) { + drupal_get_filename('module', $name, $module['filename']); + $this->moduleList[$name] = $name; + } + $list = $this->moduleList; + } + elseif (!isset($this->moduleList)) { + $list = $this->systemList($type); + } + return $list; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduleListReset(). + */ + public function moduleListReset() { + $this->moduleList(NULL, NULL, TRUE); + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::systemList(). + */ + public function systemList($type) { + // 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($this->lists['bootstrap'])) { + return $lists['bootstrap']; + } + if ($cached = $this->bootstrapCache->get('bootstrap_modules')) { + $bootstrap_list = $cached->data; + } + else { + $bootstrap_list = state()->get('system.module.bootstrap') ?: array(); + $this->bootstrapCache->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. + self::systemListWarm($bootstrap_list); + // We only return the module names here since moduleList() doesn't need + // the filename itself. + $lists['bootstrap'] = array_keys($bootstrap_list); + } + // Otherwise build the list for enabled modules and themes. + elseif (!isset($lists['module_enabled'])) { + if ($cached = $this->bootstrapCache->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 = config('system.module')->get('enabled'); + $module_files = state()->get('system.module.files'); + foreach ($enabled_modules as $name => $weight) { + // Build a list of all enabled modules. + $lists['module_enabled'][$name] = $name; + // Build a list of filenames so drupal_get_filename can use it. + $lists['filepaths'][$name] = $module_files[$name]; + } + + // Build a list of themes. + $enabled_themes = 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'][$name] = $theme->filename; + } + } + // @todo Move into list_themes(). Read info for a particular requested + // theme from state instead. + foreach ($lists['theme'] as $key => $theme) { + if (!empty($theme->info['base theme'])) { + // Make a list of the theme's base themes. + require_once DRUPAL_ROOT . '/core/includes/theme.inc'; + $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key); + // Don't proceed if there was a problem with the root base theme. + if (!current($lists['theme'][$key]->base_themes)) { + continue; + } + // Determine the root base theme. + $base_key = key($lists['theme'][$key]->base_themes); + // Add to the list of sub-themes for each of the theme's base themes. + foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) { + $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name']; + } + // Add the base theme's theme engine info. + $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_key]->info['engine']; + } + else { + // A plain theme is its own base theme. + $base_key = $key; + } + // Set the theme engine prefix. + $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; + } + $this->bootstrapCache->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. + self::systemListWarm($lists['filepaths']); + } + + return $lists[$type]; + } + + public static function systemListWarm($list) { + foreach ($list as $name => $filename) { + drupal_classloader_register($name, dirname($filename)); + drupal_get_filename('module', $name, $filename); + } + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::systemListReset(). + */ + public function systemListReset() { + $this->lists = NULL; + drupal_static_reset('system_rebuild_module_data'); + drupal_static_reset('list_themes'); + $this->bootstrapCache->deleteMultiple(array('bootstrap_modules', 'system_list')); + $this->cache->delete('system_info'); + // Remove last known theme data state. + // This causes system_list() to call system_rebuild_theme_data() on its next + // invocation. When enabling a module that implements hook_system_info_alter() + // to inject a new (testing) theme or manipulate an existing theme, then that + // will cause system_list_reset() to be called, but theme data is not + // necessarily rebuilt afterwards. + // @todo Obsolete with proper installation status for themes. + state()->delete('system.theme.data'); + } + + /** + * 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 = 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; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::moduleExists(). + */ + public function moduleExists($module) { + $list = $this->moduleList(); + return isset($list[$module]); + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::loadAllIncludes(). + */ + public function loadAllIncludes($type, $name = NULL) { + $modules = $this->moduleList(); + foreach ($modules as $module) { + 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; + $hookInfo = $this->moduleHookInfo(); + $this->implementations[$hook] = array(); + foreach ($this->moduleList() as $module) { + $include_file = isset($hookInfo[$hook]['group']) && module_load_include('inc', $module, $module . '.' . $hookInfo[$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 ? $hookInfo[$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::cachedHookImplementations(). + */ + public function cachedHookImplementations() { + 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->loadAll(NULL)) { + 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) { + $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) { + $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 moduleImplementsWriteCache() { + // 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($this->implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) { + 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 statically + // cache 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->moduleList(), and then calling + // $this->alter('module_implements'). + if (array_diff($extra_modules, $modules)) { + // Merge the arrays and order by moduleList(). + $modules = array_intersect($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); + } + } +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/ExtensionHandlerInterface.php b/core/lib/Drupal/Core/ExtensionHandlerInterface.php new file mode 100644 index 0000000..3c678a1 --- /dev/null +++ b/core/lib/Drupal/Core/ExtensionHandlerInterface.php @@ -0,0 +1,225 @@ +moduleImplements() + */ + public function moduleImplementsWriteCache(); + + /** + * 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); +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/InstallerExtensionHandler.php b/core/lib/Drupal/Core/InstallerExtensionHandler.php new file mode 100644 index 0000000..9576037 --- /dev/null +++ b/core/lib/Drupal/Core/InstallerExtensionHandler.php @@ -0,0 +1,186 @@ +systemList(). + $list = $this->module_list; + if (isset($fixed_list)) { + $this->module_list = array(); + foreach ($fixed_list as $name => $module) { + drupal_get_filename('module', $name, $module['filename']); + $this->module_list[$name] = $name; + } + $list = $this->module_list; + } + return $list; + } + + /** + * Overrides Drupal\Core\ExtensionHandler::systemList(). + */ + public function systemList($type) { + return array(); + } + + /** + * Overrides Drupal\Core\ExtensionHandler::systemListReset(). + */ + public function systemListReset() { + } + + /** + * 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) { + $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->hook_info = NULL; + $this->alter_functions = 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->loadAll(NULL)) { + return array(); + } + + if (!isset($this->hook_info)) { + $this->hook_info = array(); + // We can't use $this->moduleInvokeAll() here or it would cause an infinite + // loop. + foreach ($this->moduleList() as $module) { + $function = $module . '_hook_info'; + if (function_exists($function)) { + $result = $function(); + if (isset($result) && is_array($result)) { + $this->hook_info = array_merge_recursive($this->hook_info, $result); + } + } + } + // We can't use $this->alter() for the same reason as above. + foreach ($this->moduleList() as $module) { + $function = $module . '_hook_info_alter'; + if (function_exists($function)) { + $function($this->hook_info); + } + } + } + return $this->hook_info; + } + + /** + * Overrides Drupal\Core\ExtensionHandler::moduleImplementsWriteCache(). + */ + public function moduleImplementsWriteCache() { + } +} diff --git a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php index c837796..20e0c92 100644 --- a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php +++ b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php @@ -7,6 +7,7 @@ namespace Drupal\Core\KeyValueStore; +use Drupal\Core\Database\Database; use Drupal\Core\Database\Query\Merge; /** @@ -29,6 +30,11 @@ class DatabaseStorage extends StorageBase { protected $table; /** + * @var \Drupal\Core\Database\Connection + */ + protected $connection; + + /** * Overrides Drupal\Core\KeyValueStore\StorageBase::__construct(). * * @param string $collection @@ -39,6 +45,8 @@ class DatabaseStorage extends StorageBase { public function __construct($collection, $table = 'key_value') { parent::__construct($collection); $this->table = $table; + // Eventually, inject this. + $this->connection = Database::getConnection(); } /** @@ -47,7 +55,7 @@ public function __construct($collection, $table = 'key_value') { public function getMultiple(array $keys) { $values = array(); try { - $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE name IN (:keys) AND collection = :collection', array(':keys' => $keys, ':collection' => $this->collection))->fetchAllAssoc('name'); + $result = $this->connection->query('SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN (:keys) AND collection = :collection', array(':keys' => $keys, ':collection' => $this->collection))->fetchAllAssoc('name'); foreach ($keys as $key) { if (isset($result[$key])) { $values[$key] = unserialize($result[$key]->value); @@ -66,7 +74,7 @@ public function getMultiple(array $keys) { * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getAll(). */ public function getAll() { - $result = db_query('SELECT name, value FROM {' . db_escape_table($this->table) . '} WHERE collection = :collection', array(':collection' => $this->collection)); + $result = $this->connection->query('SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection', array(':collection' => $this->collection)); $values = array(); foreach ($result as $item) { @@ -81,7 +89,7 @@ public function getAll() { * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::set(). */ public function set($key, $value) { - db_merge($this->table) + $this->connection->merge($this->table) ->key(array( 'name' => $key, 'collection' => $this->collection, @@ -94,7 +102,7 @@ public function set($key, $value) { * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::setIfNotExists(). */ public function setIfNotExists($key, $value) { - $result = db_merge($this->table) + $result = $this->connection->merge($this->table) ->insertFields(array( 'collection' => $this->collection, 'name' => $key, @@ -112,7 +120,7 @@ public function setIfNotExists($key, $value) { public function deleteMultiple(array $keys) { // Delete in chunks when a large array is passed. do { - db_delete($this->table) + $this->connection->delete($this->table) ->condition('name', array_splice($keys, 0, 1000)) ->condition('collection', $this->collection) ->execute(); diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php index 7edb754..0a729d0 100644 --- a/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php +++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php @@ -6,6 +6,7 @@ */ namespace Drupal\Core\KeyValueStore; +use Drupal\Core\Database\Connection; /** * Defines the key/value store factory. diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php index a253e2b..995af53 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(); + drupal_extension_handler()->systemListReset(); $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(); + drupal_extension_handler()->systemListReset(); $this->assertTrue(module_exists('forum'), 'Forum module is enabled.'); } diff --git a/core/modules/language/language.module b/core/modules/language/language.module index c0c22b2..b6c615d 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -271,6 +271,7 @@ function language_save($language) { // Update URL Prefixes for all languages after the new default language is // propagated and the language_list() cache is flushed. + language_negotiation_include(); language_negotiation_url_prefixes_update(); return $language; diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php index 961a556..ee2999d 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_extension_handler()->moduleImplementsReset(); } drupal_static_reset('language_types_info'); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index c61015b..6544657 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -870,7 +870,7 @@ protected function tearDown() { drupal_static_reset(); // Reset module list and module load status. - module_list_reset(); + drupal_extension_handler()->moduleListReset(); module_load_all(FALSE, TRUE); // Restore original in-memory configuration. diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 24bf1f2..aaa3d6c 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -155,6 +155,11 @@ protected $kernel; /** + * The original extension handler for the parent site. + */ + protected $originalExtensionHandler; + + /** * Constructor for Drupal\simpletest\WebTestBase. */ function __construct($test_id = NULL) { @@ -601,6 +606,10 @@ protected function setUp() { // Backup the currently running Simpletest batch. $this->originalBatch = batch_get(); + // Store the original ExtensionHandler object so that it can be restored at + // tearDown. + $this->originalExtensionHandler = drupal_extension_handler(); + // Create the database prefix for this test. $this->prepareDatabasePrefix(); @@ -783,6 +792,9 @@ protected function refreshVariables() { * and reset the database prefix. */ protected function tearDown() { + // Restore the original Modules object. + drupal_container()->set('extension_handler', $this->originalExtensionHandler); + // Ensure that TestBase::changeDatabasePrefix() has run and TestBase::$setup // was not tricked into TRUE, since the following code would delete the // entire parent site otherwise. @@ -817,9 +829,10 @@ 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(); + $extension_handler = drupal_extension_handler(); + $extension_handler->systemListReset(); + $extension_handler->moduleListReset(); + $extension_handler->moduleImplementsReset(); // Reset the Field API. field_cache_clear(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php index df59ea0..6db9d43 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php @@ -29,7 +29,7 @@ function testClassLoading() { module_enable(array('module_test', 'module_autoload_test'), FALSE); $this->resetAll(); - // Check twice to test an unprimed and primed system_list() cache. + // Check twice to test an unprimed and primed drupal_extension_handler()->systemList() cache. for ($i=0; $i<2; $i++) { $this->drupalGet('module-test/class-loading'); $this->assertText($expected, 'Autoloader loads classes from an enabled module.'); @@ -37,7 +37,7 @@ function testClassLoading() { module_disable(array('module_autoload_test'), FALSE); $this->resetAll(); - // Check twice to test an unprimed and primed system_list() cache. + // Check twice to test an unprimed and primed drupal_extension_handler()->systemList() cache. for ($i=0; $i<2; $i++) { $this->drupalGet('module-test/class-loading'); $this->assertNoText($expected, 'Autoloader does not load classes from a disabled module.'); 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 f6e6929..2989644 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php @@ -51,7 +51,7 @@ function testModuleList() { // Try to mess with the module weights. module_set_weight('contact', 20); // Reset the module list. - system_list_reset(); + drupal_extension_handler()->systemListReset(); // Move contact to the end of the array. unset($module_list[array_search('contact', $module_list)]); $module_list[] = 'contact'; @@ -67,7 +67,7 @@ function testModuleList() { $this->assertModuleList($new_module_list, t('When using a fixed list')); // Reset the module list. - module_list_reset(); + drupal_extension_handler()->moduleListReset(); $this->assertModuleList($module_list, t('After reset')); } @@ -106,9 +106,9 @@ function testModuleImplements() { module_load_include('inc', 'module_test', 'module_test.file'); $modules = module_implements('test_hook'); - $static = drupal_static('module_implements'); $this->assertTrue(in_array('module_test', $modules), 'Hook found.'); - $this->assertEqual($static['test_hook']['module_test'], 'file', 'Include file detected.'); + $module_implementations = drupal_extension_handler()->cachedHookImplementations(); + $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 f23204b..0168350 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,7 @@ function assertNoModuleConfig($module) { * Expected module state. */ function assertModules(array $modules, $enabled) { - system_list_reset(); + drupal_extension_handler()->systemListReset(); foreach ($modules as $module) { if ($enabled) { $message = 'Module "@module" is enabled.'; diff --git a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php index 8d004ab..f8c2684 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php @@ -39,9 +39,9 @@ function testSystemInfoAlter() { $this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was returned by system_get_info().')); $seven_regions = system_region_list('seven'); $this->assertTrue(isset($seven_regions['test_region']), t('Altered theme info was returned by system_region_list().')); - $system_list_themes = system_list('theme'); + $system_list_themes = drupal_extension_handler()->systemList('theme'); $info = $system_list_themes['seven']->info; - $this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was returned by system_list().')); + $this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was returned by systemList().')); $list_themes = list_themes(); $this->assertTrue(isset($list_themes['seven']->info['regions']['test_region']), t('Altered theme info was returned by list_themes().')); @@ -53,9 +53,9 @@ function testSystemInfoAlter() { $this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was not returned by system_get_info().')); $seven_regions = system_region_list('seven'); $this->assertFalse(isset($seven_regions['test_region']), t('Altered theme info was not returned by system_region_list().')); - $system_list_themes = system_list('theme'); + $system_list_themes = drupal_extension_handler()->systemList('theme'); $info = $system_list_themes['seven']->info; - $this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was not returned by system_list().')); + $this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was not returned by systemList().')); $list_themes = list_themes(); $this->assertFalse(isset($list_themes['seven']->info['regions']['test_region']), t('Altered theme info was not returned by list_themes().')); } 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 e1fd6f6..7f2ebd6 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/MainContentFallbackTest.php @@ -56,7 +56,7 @@ function testMainContentFallback() { $edit['modules[Core][block][enable]'] = FALSE; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); - system_list_reset(); + drupal_extension_handler()->systemListReset(); $this->assertFalse(module_exists('block'), t('Block module disabled.')); // At this point, no region is filled and fallback should be triggered. @@ -90,7 +90,7 @@ function testMainContentFallback() { $edit['modules[Core][block][enable]'] = 'block'; $this->drupalPost('admin/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); - system_list_reset(); + drupal_extension_handler()->systemListReset(); $this->assertTrue(module_exists('block'), t('Block module re-enabled.')); } } 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 6598e06..dac1b7b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php @@ -241,8 +241,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(); + drupal_extension_handler()->systemListReset(); + drupal_extension_handler()->moduleImplementsReset(); module_load_all(FALSE, TRUE); // Rebuild caches. diff --git a/core/modules/system/system.module b/core/modules/system/system.module index bb3f2b4..664f0aa 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2684,7 +2684,7 @@ function system_get_info($type, $name = NULL) { } } else { - $list = system_list($type); + $list = drupal_extension_handler()->systemList($type); foreach ($list as $shortname => $item) { if (!empty($item->status)) { $info[$shortname] = $item->info; @@ -2831,7 +2831,8 @@ function system_rebuild_module_data() { $record->schema_version = SCHEMA_UNINSTALLED; $files[$module] = $record->filename; } - $modules = _module_build_dependencies($modules); + + $modules = drupal_extension_handler()->buildModuleDependencies($modules); $modules_cache = $modules; // Store filenames to allow system_list() and drupal_get_filename() to diff --git a/core/update.php b/core/update.php index 2273b40..145a39d 100644 --- a/core/update.php +++ b/core/update.php @@ -423,7 +423,7 @@ function update_check_requirements($skip_warnings = FALSE) { // Reset the module_implements() cache so that any new hook implementations // in updated code are picked up. - module_implements_reset(); + drupal_extension_handler()->moduleImplementsReset(); // Set up $language, since the installer components require it. drupal_language_initialize(); diff --git a/index.php b/index.php index 38177ab..03724d2 100644 --- a/index.php +++ b/index.php @@ -25,7 +25,7 @@ // @see Drupal\Core\EventSubscriber\PathSubscriber; // @see Drupal\Core\EventSubscriber\LegacyRequestSubscriber; require_once DRUPAL_ROOT . '/core/includes/bootstrap.inc'; -drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); // @todo Figure out how best to handle the Kernel constructor parameters. $kernel = new DrupalKernel('prod', FALSE);