diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index f4e4bcf..b47ab73 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -5,6 +5,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\ExtensionHandler; use Drupal\Core\InstallerExtensionHandler; +use Drupal\Core\KeyValueStore\KeyValueFactory; use Symfony\Component\ClassLoader\UniversalClassLoader; use Symfony\Component\ClassLoader\ApcUniversalClassLoader; use Symfony\Component\DependencyInjection\Container; @@ -844,117 +845,6 @@ function drupal_settings_initialize() { } /** - * Returns and optionally sets the filename for a system resource. - * - * The filename, whether provided, cached, or retrieved from the database, is - * only returned if the file exists. - * - * This function plays a key role in allowing Drupal's resources (modules - * and themes) to be located in different places depending on a site's - * configuration. For example, a module 'foo' may legally be be located - * in any of these three places: - * - * core/modules/foo/foo.module - * modules/foo/foo.module - * sites/example.com/modules/foo/foo.module - * - * Calling drupal_get_filename('module', 'foo') will give you one of - * the above, depending on where the module is located. - * - * @param $type - * The type of the item (i.e. theme, theme_engine, module, profile). - * @param $name - * The name of the item for which the filename is requested. - * @param $filename - * The filename of the item if it is to be set explicitly rather - * than by consulting the database. - * - * @return - * The filename of the requested item. - */ -function drupal_get_filename($type, $name, $filename = NULL) { - // The location of files will not change during the request, so do not use - // drupal_static(). - static $files = array(), $dirs = array(); - - // Profiles are converted into modules in system_rebuild_module_data(). - // @todo Remove false-exposure of profiles as modules. - $original_type = $type; - if ($type == 'profile') { - $type = 'module'; - } - if (!isset($files[$type])) { - $files[$type] = array(); - } - - if (!empty($filename) && file_exists($filename)) { - $files[$type][$name] = $filename; - } - elseif (isset($files[$type][$name])) { - // nothing - } - else { - // Verify that we have an keyvalue service before using it. This is required - // because this function is called during installation. - // @todo Inject database connection into KeyValueStore\DatabaseStorage. - if (drupal_container()->hasDefinition('keyvalue') && function_exists('db_query')) { - try { - $file_list = state()->get('system.' . $type . '.files'); - if ($file_list && isset($file_list[$name]) && file_exists(DRUPAL_ROOT . '/' . $file_list[$name])) { - $files[$type][$name] = $file_list[$name]; - } - } - catch (Exception $e) { - // The keyvalue service raised an exception because the backend might - // be down. We have a fallback for this case so we hide the error - // completely. - } - } - // Fallback to searching the filesystem if the database could not find the - // file or the file returned by the database is not found. - if (!isset($files[$type][$name])) { - // We have consistent directory naming: modules, themes... - $dir = $type . 's'; - if ($type == 'theme_engine') { - $dir = 'themes/engines'; - $extension = 'engine'; - } - elseif ($type == 'theme') { - $extension = 'info'; - } - // Profiles are converted into modules in system_rebuild_module_data(). - // @todo Remove false-exposure of profiles as modules. - elseif ($original_type == 'profile') { - $dir = 'profiles'; - $extension = 'profile'; - } - else { - $extension = $type; - } - - if (!isset($dirs[$dir][$extension])) { - $dirs[$dir][$extension] = TRUE; - if (!function_exists('drupal_system_listing')) { - require_once DRUPAL_ROOT . '/core/includes/common.inc'; - } - // Scan the appropriate directories for all files with the requested - // extension, not just the file we are currently looking for. This - // prevents unnecessary scans from being repeated when this function is - // called more than once in the same page request. - $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0); - foreach ($matches as $matched_name => $file) { - $files[$type][$matched_name] = $file->uri; - } - } - } - } - - if (isset($files[$type][$name])) { - return $files[$type][$name]; - } -} - -/** * Loads the persistent variable table. * * The variable table is composed of values that have been saved in the table @@ -1132,40 +1022,6 @@ function bootstrap_invoke_all($hook) { } /** - * Includes a file with the provided type and name. - * - * This prevents including a theme, engine, module, etc., more than once. - * - * @param $type - * The type of item to load (i.e. theme, theme_engine, module). - * @param $name - * The name of the item to load. - * - * @return - * TRUE if the item is loaded or has already been loaded. - */ -function drupal_load($type, $name) { - // Once a file is included this can't be reversed during a request so do not - // use drupal_static() here. - static $files = array(); - - if (isset($files[$type][$name])) { - return TRUE; - } - - $filename = drupal_get_filename($type, $name); - - if ($filename) { - include_once DRUPAL_ROOT . '/' . $filename; - $files[$type][$name] = TRUE; - - return TRUE; - } - - return FALSE; -} - -/** * Sets an HTTP response header for the current page. * * Note: When sending a Content-Type header, always include a 'charset' type, @@ -2477,7 +2333,7 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) { } /** - * Returns an ExtensionHandlerInterface object. + * Retrieves the ExtensionHandler that manages the list of enabled modules. * * This is a temporary function that will instantiate a new ExtensionHandler if * there isn't one present in the container. This will only happen if the @@ -2485,6 +2341,8 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) { * @todo Remove this and convert all calls to it to * drupal_container()->get('extension_handler') once the installer is using a * Kernel. + * + * @return Drupal\Core\ExtensionHandlerInterface */ function drupal_extension_handler($installer = FALSE) { $extension_handler = &drupal_static(__FUNCTION__, NULL); @@ -2492,17 +2350,17 @@ function drupal_extension_handler($installer = FALSE) { return $extension_handler; } if ($installer) { - $extension_handler = new InstallerExtensionHandler(); + $extension_handler = new InstallerExtensionHandler(drupal_classloader()); } else { if (drupal_container()->has('extension_handler')) { $extension_handler = drupal_container()->get('extension_handler'); } else { + $key_value = new KeyValueFactory(); $cache = CacheFactory::get('cache'); $bootstrap_cache = CacheFactory::get('bootstrap'); - $connection = Database::getConnection(); - $extension_handler = new ExtensionHandler($connection, $cache, $bootstrap_cache); + $extension_handler = new ExtensionHandler($key_value, $cache, $bootstrap_cache, drupal_classloader()); } } @@ -2510,6 +2368,42 @@ function drupal_extension_handler($installer = FALSE) { } /** + * Returns and optionally sets the filename for a system resource. + * + * @see ExtensionHandler::getFilename(). + */ +function drupal_get_filename($type, $name, $filename = NULL) { + return drupal_extension_handler()->getFilename($type, $name, $filename); +} + +/** + * Includes a file with the provided type and name. + * + * @see ExtensionHandler::load(). + */ +function drupal_load($type, $name) { + return drupal_extension_handler()->load($type, $name); +} + +/** + * 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()->getEnabledModules($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, $loaded = FALSE) { + return drupal_extension_handler()->loadAll($bootstrap, $reset, $loaded); +} + +/** * Determines which modules are implementing a hook. * * @see ExtensionHandler::moduleImplements(). @@ -2549,24 +2443,6 @@ function module_exists($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, $loaded = FALSE) { - return drupal_extension_handler()->loadAll($bootstrap, $reset, $loaded); -} - -/** * Returns the state storage service. * * Use this to store machine-generated data, local to a specific environment diff --git a/core/includes/update.inc b/core/includes/update.inc index 7abcefb..7d141c0 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -264,7 +264,7 @@ function update_prepare_d8_bootstrap() { // Update the environment for the language bootstrap if needed. update_prepare_d8_language(); // Prime the classloader. - ExtensionHandler::systemListWarm($list); + drupal_extension_handler()->systemListWarm($list); // Change language column to langcode in url_alias. if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) { @@ -469,9 +469,6 @@ function update_module_enable(array $modules) { // updates since the module's inception are executed in a core upgrade. $schema_store->set($module, 0); - // 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'; 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 ed7dea8..c4358da 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -79,6 +79,10 @@ 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') @@ -90,10 +94,6 @@ 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') @@ -112,6 +112,7 @@ public function build(ContainerBuilder $container) { $matcher->add($nested, 5); $content_negotation = new \Drupal\Core\ContentNegotiation(); + $extension_handler = $container->get('extension_handler'); $dispatcher->addSubscriber(new \Symfony\Component\HttpKernel\EventListener\RouterListener($matcher)); $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\ViewSubscriber($content_negotation)); $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\AccessSubscriber()); @@ -120,7 +121,7 @@ public function build(ContainerBuilder $container) { $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\LegacyRequestSubscriber()); $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\LegacyControllerSubscriber()); $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\FinishResponseSubscriber()); - $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RequestCloseSubscriber()); + $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RequestCloseSubscriber($extension_handler)); $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber()); $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RouteProcessorSubscriber()); $container->set('content_negotiation', $content_negotation); diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 6d3d550..b4d41dd 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -9,12 +9,12 @@ 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 Drupal\Core\KeyValueStore\KeyValueFactory; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\HttpKernel\Kernel; /** * The DrupalKernel class is the core of Drupal itself. @@ -31,7 +31,7 @@ class DrupalKernel extends Kernel { /** * ExtensionHandler instance holding the list of enabled modules. */ - protected $extension_handler; + protected $extensionHandler; /** * The database connection used by the ExtensionHandler. @@ -57,8 +57,9 @@ public function boot() { // 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')); + $this->keyValue = new KeyValueFactory(); + $class_loader = drupal_classloader(); + $this->extensionHandler = new ExtensionHandler($this->keyValue, CacheFactory::get('cache'), CacheFactory::get('bootstrap'), $class_loader); parent::boot(); drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); } @@ -71,7 +72,7 @@ public function registerBundles() { new CoreBundle(), ); - $modules = array_keys($this->extension_handler->systemList('module_enabled')); + $modules = array_keys($this->extensionHandler->systemList('module_enabled')); foreach ($modules as $module) { $camelized = ContainerBuilder::camelize($module); $class = "Drupal\\{$module}\\{$camelized}Bundle"; @@ -103,14 +104,13 @@ protected function initializeContainer() { protected function buildContainer() { $container = $this->getContainerBuilder(); - // Add our ExtensionHandler and database connection objects as synthetic - // services. + // Add our ExtensionHandler and KeyValueFactory 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') + $container->set('extension_handler', $this->extensionHandler); + $container->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory') ->setSynthetic(TRUE); - $container->set('database', $this->connection); + $container->set('keyvalue', $this->keyValue); 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 51ee8fd..c487d1d 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; @@ -17,6 +18,18 @@ class RequestCloseSubscriber implements EventSubscriberInterface { /** + * @var ExtensionHandlerInterface + */ + protected $extensionHandler; + + /** + * Constructor. + */ + function __construct(ExtensionHandlerInterface $extension_handler) { + $this->extensionHandler = $extension_handler; + } + + /** * Performs end of request tasks. * * @todo The body of this function has just been copied almost verbatim from @@ -30,7 +43,7 @@ class RequestCloseSubscriber implements EventSubscriberInterface { public function onTerminate(PostResponseEvent $event) { module_invoke_all('exit'); drupal_cache_system_paths(); - drupal_extension_handler()->moduleImplementsWriteCache(); + $this->extensionHandler->writeModuleImplementationsCache(); system_run_automated_cron(); } diff --git a/core/lib/Drupal/Core/ExtensionHandler.php b/core/lib/Drupal/Core/ExtensionHandler.php index 1120ce7..6b9cb8f 100644 --- a/core/lib/Drupal/Core/ExtensionHandler.php +++ b/core/lib/Drupal/Core/ExtensionHandler.php @@ -9,9 +9,9 @@ use Drupal\Component\Graph\Graph; use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Database\Connection; -use Drupal\Core\Database\Database; +use Drupal\Core\KeyValueStore\KeyValueFactory; use Drupal\Core\ExtensionHandlerInterface; +use Symfony\Component\ClassLoader\UniversalClassLoader; class ExtensionHandler implements ExtensionHandlerInterface { @@ -33,6 +33,26 @@ class ExtensionHandler implements ExtensionHandlerInterface { protected $bootstrapCache; /** + * @var \Symfony\Component\ClassLoader\UniversalClassLoader + */ + protected $classLoader; + + /** + * Keeps track internally of loaded files. + */ + protected $loadedFiles; + + /** + * Keeps track internally of extension filenames. + */ + protected $filenames = array(); + + /** + * Keeps track internall of extension directories. + */ + protected $directories = array(); + + /** * Keeps track internally of enabled modules and themes. */ protected $lists; @@ -65,14 +85,119 @@ class ExtensionHandler implements ExtensionHandlerInterface { /** * Constructor. */ - public function __construct(Connection $connection, CacheBackendInterface $cache, CacheBackendInterface $bootstrapCache) { - $this->connection = $connection; + public function __construct(KeyValueFactory $key_value, CacheBackendInterface $cache, CacheBackendInterface $bootstrapCache, UniversalClassLoader $loader) { + $this->keyValue = $key_value; $this->cache = $cache; $this->bootstrapCache = $bootstrapCache; + $this->classLoader = $loader; } /** - * Implements Drupal\Core\ExtensionHandlerInterface::LoadAll(). + * Implements Drupal\Core\ExtensionHandlerInterface::getFilename(). + */ + public function getFilename($type, $name, $filename = NULL) { + + // Profiles are converted into modules in system_rebuild_module_data(). + // @todo Remove false-exposure of profiles as modules. + $original_type = $type; + if ($type == 'profile') { + $type = 'module'; + } + if (!isset($this->filenames[$type])) { + $this->filenames[$type] = array(); + } + + if (!empty($filename) && file_exists($filename)) { + $this->filenames[$type][$name] = $filename; + } + elseif (!isset($this->filenames[$type][$name])) { + $file_list = $this->getFileListFromState($type); + if ($file_list && isset($file_list[$name]) && file_exists(DRUPAL_ROOT . '/' . $file_list[$name])) { + $this->filenames[$type][$name] = $file_list[$name]; + } + // Fallback to searching the filesystem if the database could not find the + // file or the file returned by the database is not found. + if (!isset($this->filenames[$type][$name])) { + // We have consistent directory naming: modules, themes... + $dir = $type . 's'; + if ($type == 'theme_engine') { + $dir = 'themes/engines'; + $extension = 'engine'; + } + elseif ($type == 'theme') { + $extension = 'info'; + } + // Profiles are converted into modules in system_rebuild_module_data(). + // @todo Remove false-exposure of profiles as modules. + elseif ($original_type == 'profile') { + $dir = 'profiles'; + $extension = 'profile'; + } + else { + $extension = $type; + } + + if (!isset($this->directories[$dir][$extension])) { + $this->directories[$dir][$extension] = TRUE; + if (!function_exists('drupal_system_listing')) { + require_once DRUPAL_ROOT . '/core/includes/common.inc'; + } + // Scan the appropriate directories for all files with the requested + // extension, not just the file we are currently looking for. This + // prevents unnecessary scans from being repeated when this function is + // called more than once in the same page request. + $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0); + foreach ($matches as $matched_name => $file) { + $this->filenames[$type][$matched_name] = $file->uri; + } + } + } + } + + if (isset($this->filenames[$type][$name])) { + return $this->filenames[$type][$name]; + } + } + + /** + * Retrieves the file list from the key/value store's 'state' collection. + */ + protected function getFileListFromState($type) { + $file_list = array(); + try { + $file_list = $this->keyValue->get('state')->get('system.' . $type . '.files'); + } + catch (Exception $e) { + // The keyvalue service raised an exception because the backend might + // be down. We have a fallback for this case so we hide the error + // completely. + } + return $file_list; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::load(). + */ + public function load($type, $name) { + + if (isset($this->loadedFiles[$type][$name])) { + return TRUE; + } + + $filename = $this->getFilename($type, $name); + + if ($filename) { + include_once DRUPAL_ROOT . '/' . $filename; + $this->loadedFiles[$type][$name] = TRUE; + + return TRUE; + } + + return FALSE; + } + + /** + * Implements Drupal\Core\ExtensionHandlerInterface::loadAll(). */ public function loadAll($bootstrap = FALSE, $reset = FALSE, $loaded = FALSE) { if ($reset) { @@ -80,8 +205,8 @@ public function loadAll($bootstrap = FALSE, $reset = FALSE, $loaded = FALSE) { } if (isset($bootstrap) && !$this->loaded) { $type = $bootstrap ? 'bootstrap' : 'module_enabled'; - foreach ($this->moduleList($type) as $module) { - drupal_load('module', $module); + foreach ($this->getEnabledModules($type) as $module) { + $this->load('module', $module); } // $has_run will be TRUE if $bootstrap is FALSE. $this->loaded = !$bootstrap; @@ -92,7 +217,7 @@ public function loadAll($bootstrap = FALSE, $reset = FALSE, $loaded = FALSE) { /** * Implements Drupal\Core\ExtensionHandlerInterface::moduelList(). */ - public function moduleList($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) { + public function getEnabledModules($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. @@ -108,7 +233,7 @@ public function moduleList($type = 'module_enabled', array $fixed_list = NULL, $ if (isset($fixed_list)) { $this->moduleList = array(); foreach ($fixed_list as $name => $module) { - drupal_get_filename('module', $name, $module['filename']); + $this->getFilename('module', $name, $module['filename']); $this->moduleList[$name] = $name; } $list = $this->moduleList; @@ -123,7 +248,7 @@ public function moduleList($type = 'module_enabled', array $fixed_list = NULL, $ * Implements Drupal\Core\ExtensionHandlerInterface::moduleListReset(). */ public function moduleListReset() { - $this->moduleList(NULL, NULL, TRUE); + $this->moduleList = NULL; } /** @@ -141,14 +266,14 @@ public function systemList($type) { $bootstrap_list = $cached->data; } else { - $bootstrap_list = state()->get('system.module.bootstrap') ?: array(); + $bootstrap_list = $this->keyValue->get('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. + // $filenames internal 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 + $this->systemListWarm($bootstrap_list); + // We only return the module names here since getEnabledModules() doesn't need // the filename itself. $lists['bootstrap'] = array_keys($bootstrap_list); } @@ -169,11 +294,11 @@ public function systemList($type) { // 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'); + $module_files = $this->keyValue->get('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. + // Build a list of filenames so getFilename can use it. $lists['filepaths'][$name] = $module_files[$name]; } @@ -183,19 +308,22 @@ public function systemList($type) { // 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'); + $theme_data = $this->keyValue->get('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. + // Prevent a filesystem scan in self::load() and include it directly. // @see http://drupal.org/node/1067408 + if (!function_exists('drupal_system_listing')) { + require_once DRUPAL_ROOT . '/core/includes/common.inc'; + } 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. + // Build a list of filenames so getFilename can use it. if (isset($enabled_themes[$name])) { $lists['filepaths'][$name] = $theme->filename; } @@ -230,17 +358,17 @@ public function systemList($type) { $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']); + // $filenames internal cache with all enabled modules and themes. + $this->systemListWarm($lists['filepaths']); } return $lists[$type]; } - public static function systemListWarm($list) { + public function systemListWarm($list) { foreach ($list as $name => $filename) { - drupal_classloader_register($name, dirname($filename)); - drupal_get_filename('module', $name, $filename); + $this->classLoader->registerNamespace('Drupal\\' . $name, DRUPAL_ROOT . '/' . dirname($filename) . '/lib'); + $this->getFilename('module', $name, $filename); } } @@ -260,7 +388,7 @@ public function systemListReset() { // 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'); + $this->keyValue->get('state')->delete('system.theme.data'); } /** @@ -290,7 +418,7 @@ public function buildModuleDependencies($files) { * Implements Drupal\Core\ExtensionHandlerInterface::moduleExists(). */ public function moduleExists($module) { - $list = $this->moduleList(); + $list = $this->getEnabledModules(); return isset($list[$module]); } @@ -298,7 +426,7 @@ public function moduleExists($module) { * Implements Drupal\Core\ExtensionHandlerInterface::loadAllIncludes(). */ public function loadAllIncludes($type, $name = NULL) { - $modules = $this->moduleList(); + $modules = $this->getEnabledModules(); foreach ($modules as $module) { module_load_include($type, $module, $name); } @@ -326,7 +454,7 @@ public function moduleImplements($hook) { $this->implementations['#write_cache'] = TRUE; $hookInfo = $this->moduleHookInfo(); $this->implementations[$hook] = array(); - foreach ($this->moduleList() as $module) { + foreach ($this->getEnabledModules() 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. @@ -415,7 +543,7 @@ public function moduleHookInfo() { // 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) { + foreach ($this->getEnabledModules() as $module) { $function = $module . '_hook_info'; if (function_exists($function)) { $result = $function(); @@ -425,7 +553,7 @@ public function moduleHookInfo() { } } // We can't use $this->alter() for the same reason as above. - foreach ($this->moduleList() as $module) { + foreach ($this->getEnabledModules() as $module) { $function = $module . '_hook_info_alter'; if (function_exists($function)) { $function($this->hookInfo); @@ -444,7 +572,7 @@ public function moduleHookInfo() { /** * Implements Drupal\Core\ExtensionHandlerInterface::moduleImplementsWriteCache(). */ - public function moduleImplementsWriteCache() { + public function writeModuleImplementationsCache() { // 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. @@ -525,11 +653,11 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { // 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 + // ordering by $this->getEnabledModules(), 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)); + // Merge the arrays and order by getEnabledModules(). + $modules = array_intersect($this->getEnabledModules(), 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); @@ -587,4 +715,4 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $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 index ee476bd..6ffc7cc 100644 --- a/core/lib/Drupal/Core/ExtensionHandlerInterface.php +++ b/core/lib/Drupal/Core/ExtensionHandlerInterface.php @@ -10,6 +10,52 @@ interface ExtensionHandlerInterface { /** + * Returns and optionally sets the filename for a system resource. + * + * The filename, whether provided, cached, or retrieved from the database, is + * only returned if the file exists. + * + * This function plays a key role in allowing Drupal's resources (modules + * and themes) to be located in different places depending on a site's + * configuration. For example, a module 'foo' may legally be be located + * in any of these three places: + * + * core/modules/foo/foo.module + * modules/foo/foo.module + * sites/example.com/modules/foo/foo.module + * + * Calling getFilename('module', 'foo') will give you one of the above, + * depending on where the module is located. + * + * @param $type + * The type of the item (i.e. theme, theme_engine, module, profile). + * @param $name + * The name of the item for which the filename is requested. + * @param $filename + * The filename of the item if it is to be set explicitly rather + * than by consulting the database. + * + * @return + * The filename of the requested item. + */ + public function getFilename($type, $name, $filename = NULL); + + /** + * Includes a file with the provided type and name. + * + * This prevents including a theme, engine, module, etc., more than once. + * + * @param $type + * The type of item to load (i.e. theme, theme_engine, module). + * @param $name + * The name of the item to load. + * + * @return + * TRUE if the item is loaded or has already been loaded. + */ + public function load($type, $name); + + /** * Loads all the modules that have been enabled. * * @param $bootstrap @@ -58,12 +104,12 @@ public function loadAll($bootstrap = FALSE, $reset = FALSE); * * @see self::moduleListReset() */ - public function moduleList($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE); + public function getEnabledModules($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE); /** - * Reverts an enforced fixed list of self::moduleList(). + * Reverts an enforced fixed list of self::getEnabledModules(). * - * Subsequent calls to moduleList() will no longer use a fixed list. + * Subsequent calls to getEnabledModules() will no longer use a fixed list. */ public function moduleListReset(); @@ -82,7 +128,7 @@ public function moduleListReset(); * For $type 'theme', the array values are objects representing the * respective database row, with the 'info' property already unserialized. * - * @see self::moduleList() + * @see self::getEnabledModules() * @see list_themes() */ public function systemList($type); @@ -163,7 +209,7 @@ public function moduleHookInfo(); * * @see $this->moduleImplements() */ - public function moduleImplementsWriteCache(); + public function writeModuleImplementationsCache(); /** * Invokes a hook in all enabled modules that implement it. diff --git a/core/lib/Drupal/Core/InstallerExtensionHandler.php b/core/lib/Drupal/Core/InstallerExtensionHandler.php index 9576037..fd237ce 100644 --- a/core/lib/Drupal/Core/InstallerExtensionHandler.php +++ b/core/lib/Drupal/Core/InstallerExtensionHandler.php @@ -8,6 +8,7 @@ namespace Drupal\Core; use Drupal\Core\ExtensionHandler; +use Symfony\Component\ClassLoader\UniversalClassLoader; /** * Class representing and ExtensionHandler that the installer can use. @@ -18,43 +19,17 @@ class InstallerExtensionHandler extends ExtensionHandler { /** - * Array of enabled modules. - */ - protected $module_list; - - /** - * Keeps track internally of hook implementations. - */ - protected $implementations; - - /** - * Keeps track internally of hook info. - */ - protected $hook_info; - - - /** * Overrides Drupal\Core\ExtensionHandler::construct(). */ - public function __construct() { + public function __construct(UniversalClassLoader $loader) { + $this->classLoader = $loader; } /** - * Overrides Drupal\Core\ExtensionHandler::moduelList(). + * Retrieves the file list from the key/value store's 'state' collection. */ - public function moduleList($type = 'module_enabled', array $fixed_list = NULL, $reset = FALSE) { - // The list that will be be returned. Separate from $module_list in order - // to not duplicate the static cache of drupal_extension_handler()->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; + protected function getFileListFromState($type) { + return array(); } /** @@ -84,7 +59,8 @@ public function moduleImplements($hook) { $this->implementations['#write_cache'] = TRUE; $hook_info = $this->moduleHookInfo(); $this->implementations[$hook] = array(); - foreach ($this->moduleList() as $module) { + $enabled = $this->getEnabledModules(); + foreach ($enabled 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. @@ -137,8 +113,8 @@ public function moduleImplementsReset() { // 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; + $this->hookInfo = NULL; + $this->alterFunctions = NULL; } /** @@ -154,33 +130,33 @@ public function moduleHookInfo() { return array(); } - if (!isset($this->hook_info)) { - $this->hook_info = 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) { + foreach ($this->getEnabledModules() 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); + $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) { + foreach ($this->getEnabledModules() as $module) { $function = $module . '_hook_info_alter'; if (function_exists($function)) { - $function($this->hook_info); + $function($this->hookInfo); } } } - return $this->hook_info; + return $this->hookInfo; } /** * Overrides Drupal\Core\ExtensionHandler::moduleImplementsWriteCache(). */ - public function moduleImplementsWriteCache() { + public function writeModuleImplementationsCache() { } } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 90b3ae5..58014d4 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -781,7 +781,7 @@ protected function refreshVariables() { */ protected function tearDown() { - // Restore the original Modules object. + // Restore the original ExtensionHandler. drupal_container()->set('extension_handler', $this->originalExtensionHandler); // Destroy the testing kernel.