Commit 45e61d20 authored by alexpott's avatar alexpott

Issue #2213357 by sun: Use a proper kernel in early installer.

parent 33e3823e
......@@ -183,6 +183,9 @@ services:
default_plugin_manager:
abstract: true
arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
module_handler:
class: Drupal\Core\Extension\CachedModuleHandler
arguments: ['%container.modules%', '@state', '@cache.bootstrap']
theme_handler:
class: Drupal\Core\Extension\ThemeHandler
arguments: ['@config.factory', '@module_handler', '@cache.cache', '@info_parser', '@config.installer', '@router.builder']
......
This diff is collapsed.
......@@ -625,19 +625,16 @@ function drupal_install_system($install_state) {
// Create tables.
drupal_install_schema('system');
if (!\Drupal::hasService('kernel')) {
// Immediately boot a kernel to have real services ready. If there's already
// an initialized request object in the pre-kernel container, persist it in
// the post-kernel container.
if (\Drupal::getContainer()->initialized('request')) {
$request = \Drupal::request();
}
$kernel = new DrupalKernel('install', drupal_classloader(), FALSE);
$kernel->boot();
if (isset($request)) {
\Drupal::getContainer()->set('request', $request);
}
// Immediately boot a new kernel into the regular production environment.
$request = \Drupal::hasRequest() ? \Drupal::request() : FALSE;
unset($GLOBALS['conf']['container_service_providers']['InstallerServiceProvider']);
$kernel = new DrupalKernel('prod', drupal_classloader(), FALSE);
$kernel->boot();
if ($request) {
$kernel->getContainer()->enterScope('request');
$kernel->getContainer()->set('request', $request, 'request');
}
$system_path = drupal_get_path('module', 'system');
......
......@@ -37,7 +37,7 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
*
* @var array
*/
protected $providerOrders;
protected $providerOrders = array();
/**
* Sorted list of registered providers.
......
......@@ -12,7 +12,13 @@
/**
* Storage controller used by the Drupal installer.
*
* @see install_begin_request()
* This storage performs a full filesystem scan to discover all available
* extensions and reads from all default config directories that exist.
*
* This special implementation MUST NOT be used anywhere else than the early
* installer environment.
*
* @see \Drupal\Core\DependencyInjection\InstallServiceProvider
*/
class InstallStorage extends FileStorage {
......
......@@ -51,7 +51,6 @@ public function register(ContainerBuilder $container) {
// during a subrequest).
$container->addScope(new Scope('request'));
$this->registerTwig($container);
$this->registerModuleHandler($container);
$this->registerUuid($container);
// Add the compiler pass that lets service providers modify existing
......@@ -85,27 +84,6 @@ public function register(ContainerBuilder $container) {
$container->addCompilerPass(new RegisterTwigExtensionsPass());
}
/**
* Registers the module handler.
*
* As this is different during install, it needs to stay in PHP.
*/
protected function registerModuleHandler(ContainerBuilder $container) {
// The ModuleHandler manages enabled modules and provides the ability to
// invoke hooks in all enabled modules.
if ($container->getParameter('kernel.environment') == 'install') {
// During installation we use the non-cached version.
$container->register('module_handler', 'Drupal\Core\Extension\ModuleHandler')
->addArgument('%container.modules%');
}
else {
$container->register('module_handler', 'Drupal\Core\Extension\CachedModuleHandler')
->addArgument('%container.modules%')
->addArgument(new Reference('state'))
->addArgument(new Reference('cache.bootstrap'));
}
}
/**
* Registers Twig services.
*
......
......@@ -9,6 +9,7 @@
use Drupal\Component\PhpStorage\PhpStorageFactory;
use Drupal\Core\Config\BootstrapConfigStorageFactory;
use Drupal\Core\Config\NullStorage;
use Drupal\Core\CoreServiceProvider;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
......@@ -205,7 +206,6 @@ public function getContainer() {
* {@inheritdoc}
*/
public function discoverServiceProviders() {
$this->configStorage = BootstrapConfigStorageFactory::get();
$serviceProviders = array(
'CoreServiceProvider' => new CoreServiceProvider(),
);
......@@ -217,7 +217,7 @@ public function discoverServiceProviders() {
// Ensure we know what modules are enabled and that their namespaces are
// registered.
if (!isset($this->moduleList)) {
$module_list = $this->configStorage->read('system.module');
$module_list = $this->getConfigStorage()->read('system.module');
$this->moduleList = isset($module_list['enabled']) ? $module_list['enabled'] : array();
}
$module_filenames = $this->getModuleFileNames();
......@@ -305,7 +305,7 @@ protected function moduleData($module) {
// If a module is within a profile directory but specifies another
// profile for testing, it needs to be found in the parent profile.
$settings = $this->configStorage->read('simpletest.settings');
$settings = $this->getConfigStorage()->read('simpletest.settings');
$parent_profile = !empty($settings['parent_profile']) ? $settings['parent_profile'] : NULL;
if ($parent_profile && !isset($profiles[$parent_profile])) {
// In case both profile directories contain the same extension, the
......@@ -374,8 +374,7 @@ protected function initializeContainer() {
$this->containerNeedsDumping = FALSE;
$persist = $this->getServicesToPersist();
// The request service requires custom persisting logic, since it is also
// potentially scoped. During Drupal installation, there is a request
// service without a request scope.
// potentially scoped.
$request_scope = FALSE;
if (isset($this->container)) {
if ($this->container->isScopeActive('request')) {
......@@ -525,10 +524,11 @@ protected function buildContainer() {
// avoids the circular dependencies that would created by
// \Drupal\language\LanguageServiceProvider::alter() and allows the default
// language to not be English in the installer.
$system = BootstrapConfigStorageFactory::get()->read('system.site');
$default_language_values = Language::$defaultValues;
if ($default_language_values['id'] != $system['langcode']) {
$default_language_values = array('id' => $system['langcode'], 'default' => TRUE);
if ($system = $this->getConfigStorage()->read('system.site')) {
if ($default_language_values['id'] != $system['langcode']) {
$default_language_values = array('id' => $system['langcode'], 'default' => TRUE);
}
}
$container->setParameter('language.default_values', $default_language_values);
......@@ -647,6 +647,25 @@ protected function storage() {
return $this->storage;
}
/**
* Returns the active configuration storage to use during building the container.
*
* @return \Drupal\Core\Config\StorageInterface
*/
protected function getConfigStorage() {
if (!isset($this->configStorage)) {
// The active configuration storage may not exist yet; e.g., in the early
// installer. Catch the exception thrown by config_get_config_directory().
try {
$this->configStorage = BootstrapConfigStorageFactory::get();
}
catch (\Exception $e) {
$this->configStorage = new NullStorage();
}
}
return $this->configStorage;
}
/**
* Returns the file name for each enabled module.
*/
......
<?php
/**
* @file
* Contains \Drupal\Core\Installer\InstallerRouteBuilder.
*/
namespace Drupal\Core\Installer;
use Drupal\Core\Routing\RouteBuilder;
/**
* Manages the router in the installer.
*/
class InstallerRouteBuilder extends RouteBuilder {
/**
* {@inheritdoc}
*
* Overridden to return no routes.
*
* @todo Convert installer steps into routes; add an installer.routing.yml.
*/
protected function getRouteDefinitions() {
return array();
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Installer\InstallerServiceProvider.
*/
namespace Drupal\Core\Installer;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
/**
* Service provider for the early installer environment.
*
* This class is manually added by install_begin_request() via
* $conf['container_service_providers'] and required to prevent various services
* from trying to retrieve data from storages that do not exist yet.
*/
class InstallerServiceProvider implements ServiceProviderInterface, ServiceModifierInterface {
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
// Inject the special configuration storage for the installer.
// This special implementation MUST NOT be used anywhere else than the early
// installer environment.
$container->register('config.storage', 'Drupal\Core\Config\InstallStorage');
// Replace services with in-memory implementations.
foreach (array('bootstrap', 'config', 'cache', 'menu', 'page', 'path') as $bin) {
$container
->register("cache.$bin", 'Drupal\Core\Cache\MemoryBackend')
->addArgument($bin);
}
$container
->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueMemoryFactory');
$container
->register('keyvalue.expirable', 'Drupal\Core\KeyValueStore\KeyValueNullExpirableFactory');
// Replace services with no-op implementations.
$container
->register('lock', 'Drupal\Core\Lock\NullLockBackend');
$container
->register('url_generator', 'Drupal\Core\Routing\NullGenerator');
$container
->register('router.dumper', 'Drupal\Core\Routing\NullMatcherDumper');
// Replace the route builder with an empty implementation.
// @todo Convert installer steps into routes; add an installer.routing.yml.
$definition = $container->getDefinition('router.builder');
$definition->setClass('Drupal\Core\Installer\InstallerRouteBuilder');
// Remove dependencies on Drupal's default session handling.
$container->removeDefinition('authentication.cookie');
}
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
// Disable configuration overrides.
// ConfigFactory would to try to load language overrides and InstallStorage
// throws an exception upon trying to load a non-existing file.
$container->get('config.factory')->setOverrideState(FALSE);
// No service may persist when the early installer kernel is rebooted into
// the production environment.
// @todo The DrupalKernel reboot performed by drupal_install_system() is
// actually not a "regular" reboot (like ModuleHandler::install()), so
// services are not actually persisted.
foreach ($container->findTaggedServiceIds('persist') as $id => $tags) {
$definition = $container->getDefinition($id);
$definition->clearTag('persist');
}
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Routing\NullMatcherDumper.
*/
namespace Drupal\Core\Routing;
use Symfony\Component\Routing\RouteCollection;
/**
* Does not dump Route information.
*/
class NullMatcherDumper implements MatcherDumperInterface {
/**
* The routes to be dumped.
*
* @var \Symfony\Component\Routing\RouteCollection
*/
protected $routes;
/**
* {@inheritdoc}
*/
public function addRoutes(RouteCollection $routes) {
if (empty($this->routes)) {
$this->routes = $routes;
}
else {
$this->routes->addCollection($routes);
}
}
/**
* Dumps a set of routes to the router table in the database.
*
* Available options:
* - provider: The route grouping that is being dumped. All existing
* routes with this provider will be deleted on dump.
* - base_class: The base class name.
*
* @param array $options
* An array of options.
*/
public function dump(array $options = array()) {
// The dumper is reused for multiple providers, so reset the queued routes.
$this->routes = NULL;
}
/**
* Gets the routes to match.
*
* @return \Symfony\Component\Routing\RouteCollection
* A RouteCollection instance representing all routes currently in the
* dumper.
*/
public function getRoutes() {
return $this->routes;
}
}
......@@ -103,9 +103,7 @@ public function rebuild() {
return FALSE;
}
$yaml_discovery = $this->getYamlDiscovery();
foreach ($yaml_discovery->findAll() as $provider => $routes) {
foreach ($this->getRouteDefinitions() as $provider => $routes) {
$collection = new RouteCollection();
// The top-level 'routes_callback' is a list of methods in controller
......@@ -180,16 +178,16 @@ public function setRebuildNeeded() {
}
/**
* Returns the YAML discovery for getting all the .routing.yml files.
* Retrieves all defined routes from .routing.yml files.
*
* @return \Drupal\Component\Discovery\YamlDiscovery
* The yaml discovery.
* @return array
* The defined routes, keyed by provider.
*/
protected function getYamlDiscovery() {
protected function getRouteDefinitions() {
if (!isset($this->yamlDiscovery)) {
$this->yamlDiscovery = new YamlDiscovery('routing', $this->moduleHandler->getModuleDirectories());
}
return $this->yamlDiscovery;
return $this->yamlDiscovery->findAll();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment