diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index a991ed1d414ee3c193d93bc69d16027c85a60ea7..4abbd409f37aafadbd65794885d4f5318cfed433 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -2264,6 +2264,13 @@ function _drupal_bootstrap_configuration() { // Load the procedural configuration system helper functions. require_once DRUPAL_ROOT . '/core/includes/config.inc'; + // Redirect the user to the installation script if Drupal has not been + // installed yet (i.e., if no $databases array has been defined in the + // settings.php file) and we are not already installing. + if (empty($GLOBALS['databases']) && !drupal_installation_attempted()) { + include_once DRUPAL_ROOT . '/core/includes/install.inc'; + install_goto('core/install.php'); + } } /** @@ -2359,15 +2366,6 @@ function _drupal_initialize_db_test_prefix() { * Initializes the database system by loading database.inc. */ function _drupal_bootstrap_database() { - // Redirect the user to the installation script if Drupal has not been - // installed yet (i.e., if no $databases array has been defined in the - // settings.php file) and we are not already installing. - if (empty($GLOBALS['databases']) && !drupal_installation_attempted()) { - include_once DRUPAL_ROOT . '/core/includes/install.inc'; - install_goto('core/install.php'); - } - - // Initialize the database system. Note that the connection // won't be initialized until it is actually requested. require_once DRUPAL_ROOT . '/core/includes/database.inc'; @@ -2442,10 +2440,9 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) { $container = $new_container; } if (!isset($container)) { - // Return a ContainerBuilder instance with the bare essentials needed for any - // full bootstrap regardless of whether there will be a DrupalKernel involved. - // This will get merged with the full Kernel-built Container on normal page - // requests. + // This is only ever used by the installer and by run-tests.sh. + // @todo Remove this entire section once these have been converted to use a + // kernel. $container = new ContainerBuilder(); // Register active configuration storage. @@ -2707,11 +2704,9 @@ 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 { + if (drupal_container()->isScopeActive('request')) { $language_manager = drupal_container()->get('language_manager', Container::NULL_ON_INVALID_REFERENCE); } - catch (DependencyInjectionRuntimeException $e) { - } if (isset($language_manager)) { return $language_manager->getLanguage($type); @@ -3448,54 +3443,6 @@ function drupal_check_memory_limit($required, $memory_limit = NULL) { return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required))); } -/** - * Instantiates and statically caches a storage controller for generated PHP code. - * - * By default, this returns an instance of the - * Drupal\Component\PhpStorage\MTimeProtectedFileStorage class. - * - * Classes implementing - * Drupal\Component\PhpStorage\PhpStorageInterface can be registered for a - * specific bin or as a default implementation. - * - * @param $name - * The name for which the storage controller should be returned. Defaults to - * 'default'. The name is also used as the storage bin if one is not - * specified in the configuration. - * - * @return Drupal\Component\PhpStorage\PhpStorageInterface - * An instantiated storage controller for the specified name. - * - * @see Drupal\Component\PhpStorage\PhpStorageInterface - */ -function drupal_php_storage($name = 'default') { - global $conf; - $storage_controllers = &drupal_static(__FUNCTION__); - if (!isset($storage_controllers[$name])) { - if (isset($conf['php_storage'][$name])) { - $configuration = $conf['php_storage'][$name]; - } - elseif (isset($conf['php_storage']['default'])) { - $configuration = $conf['php_storage']['default']; - } - else { - $configuration = array( - 'class' => 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage', - 'secret' => drupal_get_hash_salt(), - ); - } - $class = isset($configuration['class']) ? $configuration['class'] : 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage'; - if (!isset($configuration['bin'])) { - $configuration['bin'] = $name; - } - if (!isset($configuration['directory'])) { - $configuration['directory'] = DRUPAL_ROOT . '/' . variable_get('file_public_path', conf_path() . '/files') . '/php'; - } - $storage_controllers[$name] = new $class($configuration); - } - return $storage_controllers[$name]; -} - /** * @defgroup lock Locking mechanisms * @{ diff --git a/core/includes/common.inc b/core/includes/common.inc index 1eb3b945d0dec790766dd220d7c8263f92603c2e..0f30940bf67fa1339250b17b225bd0221e67e0d9 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3,6 +3,7 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Drupal\Component\PhpStorage\PhpStorageFactory; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Datetime\DrupalDateTime; @@ -6745,8 +6746,8 @@ function drupal_flush_all_caches() { menu_router_rebuild(); // Wipe the PHP Storage caches. - drupal_php_storage('service_container')->deleteAll(); - drupal_php_storage('twig')->deleteAll(); + PhpStorageFactory::get('service_container')->deleteAll(); + PhpStorageFactory::get('twig')->deleteAll(); // Re-initialize the maintenance theme, if the current request attempted to // use it. Unlike regular usages of this function, the installer and update diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index d5983b6d0faead9c7c0198094b4c65bbc4896f0e..24ceaf26542a100978749d64193c3cdd424916a5 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1502,7 +1502,7 @@ function install_bootstrap_full(&$install_state) { module_list_reset(); // Instantiate the kernel. - $kernel = new DrupalKernel('prod', FALSE, drupal_classloader()); + $kernel = new DrupalKernel('prod', FALSE, drupal_classloader(), FALSE); $kernel->boot(); drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); } diff --git a/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php b/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2952488301877cfe033d251f902d47d623a59555 --- /dev/null +++ b/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php @@ -0,0 +1,58 @@ + 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage', + 'secret' => $GLOBALS['drupal_hash_salt'], + ); + } + $class = isset($configuration['class']) ? $configuration['class'] : 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage'; + if (!isset($configuration['bin'])) { + $configuration['bin'] = $name; + } + if (!isset($configuration['directory'])) { + $path = isset($conf['file_public_path']) ? $conf['file_public_path'] : conf_path() . '/files'; + $configuration['directory'] = DRUPAL_ROOT . "/$path/php"; + } + return new $class($configuration); + } + +} diff --git a/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php b/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f5487018a1bb59c0800e8bbd27a2e9b15bc01b96 --- /dev/null +++ b/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php @@ -0,0 +1,31 @@ +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', 'Drupal\Core\Cache\CacheBackendInterface') + ->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)); + + // Register the service for the default database connection. + $container->register('database', 'Drupal\Core\Database\Connection') + ->setFactoryClass('Drupal\Core\Database\Database') + ->setFactoryMethod('getConnection') + ->addArgument('default'); + // Register the KeyValueStore factory. + $container + ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory') + ->addArgument(new Reference('service_container')); + $container + ->register('keyvalue.database', 'Drupal\Core\KeyValueStore\KeyValueDatabaseFactory') + ->addArgument(new Reference('database')); + + $container->register('path.alias_manager', 'Drupal\Core\Path\AliasManager') + ->addArgument(new Reference('database')) + ->addArgument(new Reference('keyvalue.database')); + + // Register the EntityManager. + $container->register('plugin.manager.entity', 'Drupal\Core\Entity\EntityManager'); + // The 'request' scope and service enable services to depend on the Request // object and get reconstructed when the request object changes (e.g., // during a subrequest). diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 25c0a1ace4a94b0b6c8ee099260da642409602d2..a5624d700435c8e7a939e179db0378a14556f07a 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -7,16 +7,15 @@ namespace Drupal\Core; -use Drupal\Core\Cache\CacheBackendInterface; -use Symfony\Component\ClassLoader\UniversalClassLoader; -use Drupal\Core\Config\FileStorage; +use Drupal\Component\PhpStorage\PhpStorageFactory; +use Drupal\Core\Config\BootstrapConfigStorageFactory; use Drupal\Core\CoreBundle; -use Drupal\Component\PhpStorage\PhpStorageInterface; -use Symfony\Component\HttpKernel\Kernel; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Symfony\Component\ClassLoader\UniversalClassLoader; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\HttpKernel\Kernel; /** * The DrupalKernel class is the core of Drupal itself. @@ -58,6 +57,20 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface { */ protected $moduleData = array(); + /** + * Holds a list of enabled modules and their paths. + * + * This is used to store module data as a container parameter so that it can + * be retrieved for registering namespaces when using a compiled container. + * When not using a compiled container, the namespaces get registered during + * the process of building the container. + * + * @var array + * An associative array whose keys are module names and whose values are + * module paths. + */ + protected $modulePaths = array(); + /** * PHP code storage object to use for the compiled container. * @@ -72,6 +85,13 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface { */ protected $classLoader; + /** + * Config storage object used for reading enabled modules configuration. + * + * @var \Drupal\Core\Config\StorageInterface + */ + protected $configStorage; + /** * The list of the classnames of the bundles in this kernel. * @@ -79,6 +99,20 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface { */ protected $bundleClasses; + /** + * Whether the container can be dumped. + * + * @var bool + */ + protected $allowDumping; + + /** + * Whether the container needs to be dumped once booting is complete. + * + * @var bool + */ + protected $containerNeedsDumping; + /** * Constructs a DrupalKernel object. * @@ -95,15 +129,14 @@ class DrupalKernel extends Kernel implements DrupalKernelInterface { * the load from storage fails and a container rebuild is required. In * this case, the loaded modules will be registered with this loader in * order to be able to find the module bundles. - * @param \Drupal\Component\PhpStorage\PhpStorageInterface $storage - * (optional) An object handling the load and save of the compiled - * container. If not specified, the container will neither be stored to - * disk nor read from there. + * @param bool $allow_dumping + * (optional) FALSE to stop the container from being written to or read + * from disk. Defaults to TRUE. */ - public function __construct($environment, $debug, UniversalClassLoader $class_loader, PhpStorageInterface $storage = NULL) { + public function __construct($environment, $debug, UniversalClassLoader $class_loader, $allow_dumping = TRUE) { parent::__construct($environment, $debug); - $this->storage = $storage; $this->classLoader = $class_loader; + $this->allowDumping = $allow_dumping; } /** @@ -125,28 +158,47 @@ public function boot() { } $this->initializeContainer(); $this->booted = TRUE; + // @todo Remove this once everything in the bootstrap has been converted to + // services in the DIC. + drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); + // Now that full bootstrap is complete, we can dump the container if it + // was just rebuilt. + if ($this->containerNeedsDumping && !$this->dumpDrupalContainer($this->container, $this->getContainerBaseClass())) { + watchdog('DrupalKernel', 'Container cannot be written to disk'); + } } /** * Returns an array of available bundles. + * + * @return array + * The available bundles. */ public function registerBundles() { + $this->configStorage = BootstrapConfigStorageFactory::get(); $bundles = array( new CoreBundle(), ); if (!isset($this->moduleList)) { - $storage = new FileStorage(config_get_config_directory()); - $module_list = $storage->read('system.module'); + $module_list = $this->configStorage->read('system.module'); $this->moduleList = isset($module_list['enabled']) ? $module_list['enabled'] : array(); } $namespaces = $this->classLoader->getNamespaces(); + // We will need to store module locations in a container parameter so that + // we can register all namespaces when using a compiled container. + $this->modulePaths = array(); foreach ($this->moduleList as $module => $weight) { // When installing new modules, the modules in the list passed to // updateModules() do not yet have their namespace registered. $namespace = 'Drupal\\' . $module; if (!isset($namespaces[$namespace]) && $this->moduleData($module)) { - $this->classLoader->registerNamespace($namespace, dirname(DRUPAL_ROOT . '/' . $this->moduleData($module)->uri) . '/lib'); + $path = dirname(DRUPAL_ROOT . '/' . $this->moduleData($module)->uri) . '/lib'; + $this->modulePaths[$module] = $path; + $this->classLoader->registerNamespace($namespace, $path); + } + else { + $this->modulePaths[$module] = $namespaces[$namespace]; } $camelized = ContainerBuilder::camelize($module); $class = "Drupal\\{$module}\\{$camelized}Bundle"; @@ -155,6 +207,13 @@ public function registerBundles() { $this->bundleClasses[] = $class; } } + // Add site specific or test bundles. + if (!empty($GLOBALS['conf']['container_bundles'])) { + foreach ($GLOBALS['conf']['container_bundles'] as $class) { + $bundles[] = new $class(); + $this->bundleClasses[] = $class; + } + } return $bundles; } @@ -177,10 +236,9 @@ protected function moduleData($module) { $profiles_scanner = new SystemListing(); $all_profiles = $profiles_scanner->scan('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles'); $profiles = array_keys(array_intersect_key($this->moduleList, $all_profiles)); - $storage = new FileStorage(config_get_config_directory()); // If a module is within a profile directory but specifies another // profile for testing, it needs to be found in the parent profile. - if (($parent_profile_config = $storage->read('simpletest.settings')) && isset($parent_profile_config['parent_profile']) && $parent_profile_config['parent_profile'] != $profiles[0]) { + if (($parent_profile_config = $this->configStorage->read('simpletest.settings')) && isset($parent_profile_config['parent_profile']) && $parent_profile_config['parent_profile'] != $profiles[0]) { // In case both profile directories contain the same extension, the // actual profile always has precedence. array_unshift($profiles, $parent_profile_config['parent_profile']); @@ -195,7 +253,7 @@ protected function moduleData($module) { /** * Implements Drupal\Core\DrupalKernelInterface::updateModules(). */ - public function updateModules(array $module_list, array $module_paths = array(), ContainerBuilder $base_container = NULL) { + public function updateModules(array $module_list, array $module_paths = array()) { $this->newModuleList = $module_list; foreach ($module_paths as $module => $path) { $this->moduleData[$module] = (object) array('uri' => $path); @@ -204,7 +262,6 @@ public function updateModules(array $module_list, array $module_paths = array(), // list will take effect when boot() is called. If we have already booted, // then reboot in order to refresh the bundle list and container. if ($this->booted) { - drupal_container($base_container, TRUE); $this->booted = FALSE; $this->boot(); } @@ -236,10 +293,10 @@ protected function initializeContainer() { $class = $this->getClassName(); $cache_file = $class . '.php'; - if ($this->storage) { + if ($this->allowDumping) { // First, try to load. if (!class_exists($class, FALSE)) { - $this->storage->load($cache_file); + $this->storage()->load($cache_file); } // If the load succeeded or the class already existed, use it. if (class_exists($class, FALSE)) { @@ -259,28 +316,33 @@ protected function initializeContainer() { // web frontend or during the installer -- changed the list of enabled // modules. if (isset($this->container)) { + // All namespaces must be registered before we attempt to use any service + // from the container. + $namespaces = $this->classLoader->getNamespaces(); + foreach ($this->container->getParameter('container.modules') as $module => $path) { + $namespace = 'Drupal\\' . $module; + if (!isset($namespaces[$namespace])) { + $this->classLoader->registerNamespace($namespace, $path); + } + } $module_list = $this->moduleList ?: $this->container->get('config.factory')->get('system.module')->load()->get('enabled'); - if (array_keys((array)$module_list) !== $this->container->getParameter('container.modules')) { + if (array_keys((array)$module_list) !== array_keys($this->container->getParameter('container.modules'))) { unset($this->container); } } if (!isset($this->container)) { $this->container = $this->buildContainer(); - if ($this->storage && !$this->dumpDrupalContainer($this->container, $this->getContainerBaseClass())) { - // We want to log this as an error but we cannot call watchdog() until - // the container has been fully built and set in drupal_container(). - $error = 'Container cannot be written to disk'; + if ($this->allowDumping) { + $this->containerNeedsDumping = TRUE; } } $this->container->set('kernel', $this); + // Set the class loader which was registered as a synthetic service. + $this->container->set('class_loader', $this->classLoader); drupal_container($this->container); - - if (isset($error)) { - watchdog('DrupalKernel', $error); - } } /** @@ -292,12 +354,9 @@ protected function buildContainer() { $this->initializeBundles(); $container = $this->getContainerBuilder(); $container->setParameter('container.bundles', $this->bundleClasses); - $container->setParameter('container.modules', array_keys($this->moduleList)); - - // Merge in the minimal bootstrap container. - if ($bootstrap_container = drupal_container()) { - $container->merge($bootstrap_container); - } + $container->setParameter('container.modules', $this->modulePaths); + // Register the class loader as a synthetic service. + $container->register('class_loader', 'Symfony\Component\ClassLoader\UniversalClassLoader')->setSynthetic(TRUE); foreach ($this->bundles as $bundle) { $bundle->build($container); } @@ -329,14 +388,14 @@ protected function getContainerBuilder() { * TRUE if the container was successfully dumped to disk. */ protected function dumpDrupalContainer(ContainerBuilder $container, $baseClass) { - if (!$this->storage->writeable()) { + if (!$this->storage()->writeable()) { return FALSE; } // Cache the container. $dumper = new PhpDumper($container); $class = $this->getClassName(); $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass)); - return $this->storage->save($class . '.php', $content); + return $this->storage()->save($class . '.php', $content); } /** @@ -352,4 +411,16 @@ protected function dumpDrupalContainer(ContainerBuilder $container, $baseClass) public function registerContainerConfiguration(LoaderInterface $loader) { } + /** + * Gets the PHP code storage object to use for the compiled container. + * + * @return \Drupal\Component\PhpStorage\PhpStorageInterface + */ + protected function storage() { + if (!isset($this->storage)) { + $this->storage = PhpStorageFactory::get('service_container'); + } + return $this->storage; + } + } diff --git a/core/lib/Drupal/Core/DrupalKernelInterface.php b/core/lib/Drupal/Core/DrupalKernelInterface.php index d4c8b8be52b52c2b084c5ae177e764b0ea24ec80..5f75fb2c68c1062fa26bc51ff8252e49133b2676 100644 --- a/core/lib/Drupal/Core/DrupalKernelInterface.php +++ b/core/lib/Drupal/Core/DrupalKernelInterface.php @@ -25,8 +25,8 @@ interface DrupalKernelInterface extends KernelInterface { * * @param array $module_list * The new list of modules. - * @param array $module_path + * @param array $module_paths * List of module paths, keyed by module name. */ - public function updateModules(array $module_list, array $module_path = array()); + public function updateModules(array $module_list, array $module_paths = array()); } diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php index bc7ab65578f70fcc2f20670a73550e6419628019..4e794e5dcb5443e2f21a4487144129498a10f699 100644 --- a/core/lib/Drupal/Core/Template/TwigEnvironment.php +++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Template; +use Drupal\Component\PhpStorage\PhpStorageFactory; + /** * A class that defines a Twig environment for Drupal. * @@ -24,9 +26,8 @@ class TwigEnvironment extends \Twig_Environment { * internally. */ public function __construct(\Twig_LoaderInterface $loader = NULL, $options = array()) { - // @todo Pass as arguments from the DIC? + // @todo Pass as arguments from the DIC. $this->cache_object = cache(); - $this->storage = drupal_php_storage('twig'); parent::__construct($loader, $options); } @@ -47,7 +48,7 @@ public function needsUpdate($cache_filename, $name) { public function updateCompiledTemplate($cache_filename, $name) { $source = $this->loader->getSource($name); $compiled_source = $this->compileSource($source, $name); - $this->storage->save($cache_filename, $compiled_source); + $this->storage()->save($cache_filename, $compiled_source); // Save the last modification time $cid = 'twig:' . $cache_filename; $this->cache_object->set($cid, REQUEST_TIME); @@ -83,9 +84,9 @@ public function loadTemplate($name, $index = NULL) { $this->updateCompiledTemplate($cache_filename, $name); } - if (!$this->storage->load($cache_filename)) { + if (!$this->storage()->load($cache_filename)) { $this->updateCompiledTemplate($cache_filename, $name); - $this->storage->load($cache_filename); + $this->storage()->load($cache_filename); } } } @@ -97,4 +98,16 @@ public function loadTemplate($name, $index = NULL) { return $this->loadedTemplates[$cls] = new $cls($this); } + /** + * Gets the PHP code storage object to use for the compiled Twig files. + * + * @return \Drupal\Component\PhpStorage\PhpStorageInterface + */ + protected function storage() { + if (!isset($this->storage)) { + $this->storage = PhpStorageFactory::get('service_container'); + } + return $this->storage; + } + } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php index 97a0aa237821ec1d6e2921d49e671047cdb470f5..a8edc813fcea31797e316622417507f8eeac2310 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php @@ -86,21 +86,18 @@ protected function setUp() { } parent::setUp(); - // Build a minimal, partially mocked environment for unit tests. - $this->setUpContainer(); + $this->containerBuild(drupal_container()); + // Make sure it survives kernel rebuilds. + $GLOBALS['conf']['container_bundles'][] = 'Drupal\simpletest\TestBundle'; state()->set('system.module.files', $this->moduleFiles); state()->set('system.theme.files', $this->themeFiles); state()->set('system.theme.data', $this->themeData); - // Back up the base container for enableModules(). - $this->baseContainer = clone $this->container; - // Bootstrap the kernel. $this->kernel = new DrupalKernel('testing', TRUE, drupal_classloader()); $this->kernel->boot(); - $this->container = drupal_container(); // Ensure that the module list is initially empty. $this->moduleList = array(); @@ -126,28 +123,18 @@ protected function setUp() { * @see DrupalUnitTestBase::setUp() * @see DrupalUnitTestBase::enableModules() */ - protected function setUpContainer() { + public function containerBuild($container) { global $conf; - $conf['lock_backend'] = 'Drupal\Core\Lock\NullLockBackend'; $conf['cache_classes'] = array('cache' => 'Drupal\Core\Cache\MemoryBackend'); - $this->container + $container ->register('config.storage', 'Drupal\Core\Config\FileStorage') ->addArgument($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); $conf['keyvalue_default'] = 'keyvalue.memory'; - $this->container + $container ->register('keyvalue.memory', 'Drupal\Core\KeyValueStore\KeyValueMemoryFactory'); } - /** - * Overrides TestBase::tearDown(). - */ - protected function tearDown() { - // Ensure that TestBase::tearDown() gets a working container. - $this->container = $this->baseContainer; - parent::tearDown(); - } - /** * Installs a specific table from a module schema definition. * @@ -212,15 +199,6 @@ protected function enableModules(array $modules, $install = TRUE) { // Call module_enable() to enable (install) the new module. if ($install) { - // module_enable() reboots DrupalKernel, but that builds an entirely new - // ContainerBuilder, retrieving a fresh base container from - // drupal_container(), which means that all of the service overrides - // from DrupalUnitTestBase::setUpContainer() are lost, in turn triggering - // invalid service reference errors; e.g., in TestBase::tearDown(). - // Since DrupalKernel also replaces the container in drupal_container() - // after (re-)booting, we have to re-inject a new copy of our initial - // base container that was built in setUpContainer(). - drupal_container(clone $this->baseContainer); module_enable(array($module), FALSE); } } @@ -229,8 +207,6 @@ protected function enableModules(array $modules, $install = TRUE) { module_load_all(FALSE, TRUE); module_implements_reset(); } - $kernel = $this->container->get('kernel'); - $kernel->updateModules($this->moduleList, $new_enabled, clone $this->baseContainer); } } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 89ccb8e7729f8397cbec5ae44daaacf389877842..60a9a7707b2e977158cf9ec8d67dc03c3976b74f 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -639,6 +639,7 @@ protected function verbose($message) { * methods during debugging. */ public function run(array $methods = array()) { + TestBundle::$currentTest = $this; $simpletest_config = config('simpletest.settings'); $class = get_class($this); @@ -710,6 +711,7 @@ public function run(array $methods = array()) { } } } + TestBundle::$currentTest = NULL; // Clear out the error messages and restore error handler. drupal_get_messages(); restore_error_handler(); @@ -912,7 +914,7 @@ protected function rebuildContainer() { // container in drupal_container(). Drupal\simpletest\TestBase::tearDown() // restores the original container. // @see Drupal\Core\DrupalKernel::initializeContainer() - $this->kernel = new DrupalKernel('testing', FALSE, drupal_classloader()); + $this->kernel = new DrupalKernel('testing', FALSE, drupal_classloader(), FALSE); // Booting the kernel is necessary to initialize the new DIC. While // normally the kernel gets booted on demand in // Symfony\Component\HttpKernel\handle(), this kernel needs manual booting diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBundle.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBundle.php new file mode 100644 index 0000000000000000000000000000000000000000..7e2fd2f8d9677d21a82e539135697107b8360063 --- /dev/null +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBundle.php @@ -0,0 +1,24 @@ +containerBuild($container); + } + } + +} diff --git a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php index 506fed25fa09c6c7ed8918f712c503d0e0e0dceb..648aa4251708984b25e9910cbac1aa1bb9a0405c 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php @@ -19,6 +19,11 @@ */ abstract class UnitTestBase extends TestBase { + /** + * @var array + */ + protected $configDirectories; + /** * Constructor for UnitTestBase. */ diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 1410f539a3990b7252fd5e867b1bb4c1cd8b0cdd..c0a59e0b00f10f3ef625c725632ccecea64fac95 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -713,11 +713,22 @@ protected function setUp() { // Reset the static batch to remove Simpletest's batch operations. $batch = &batch_get(); $batch = array(); - + $variables = array( + 'file_public_path' => $this->public_files_directory, + 'file_private_path' => $this->private_files_directory, + 'file_temporary_path' => $this->temp_files_directory, + 'locale_translate_file_directory' => $this->translation_files_directory, + ); + foreach ($variables as $name => $value) { + $GLOBALS['conf'][$name] = $value; + } // Execute the non-interactive installer. require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; install_drupal($settings); $this->rebuildContainer(); + foreach ($variables as $name => $value) { + variable_set($name, $value); + } // Restore the original Simpletest batch. $batch = &batch_get(); @@ -728,10 +739,6 @@ protected function setUp() { unset($conf['lock_backend']); // Set path variables. - variable_set('file_public_path', $this->public_files_directory); - variable_set('file_private_path', $this->private_files_directory); - variable_set('file_temporary_path', $this->temp_files_directory); - variable_set('locale_translate_file_directory', $this->translation_files_directory); // Set 'parent_profile' of simpletest to add the parent profile's // search path to the child site's search paths. diff --git a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php index f4dd2949d711519541ff01062d8c1881c7ab3104..c313d8259d1d4ea5a26371597f47b195b09b8b0c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php @@ -36,23 +36,24 @@ function testCompileDIC() { // test. $original_container = drupal_container(); $classloader = drupal_classloader(); - $configuration = array( + global $conf; + $conf['php_storage']['service_container']= array( 'bin' => 'service_container', + 'class' => 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage', 'directory' => DRUPAL_ROOT . '/' . variable_get('file_public_path', conf_path() . '/files') . '/php', 'secret' => $GLOBALS['drupal_hash_salt'], ); // @todo: write a memory based storage backend for testing. - $php_storage = new MTimeProtectedFastFileStorage($configuration); $module_enabled = array( 'system' => 'system', 'user' => 'user', ); - $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage); + $kernel = new DrupalKernel('testing', FALSE, $classloader); $kernel->updateModules($module_enabled); $kernel->boot(); // Instantiate it a second time and we should get the compiled Container // class. - $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage); + $kernel = new DrupalKernel('testing', FALSE, $classloader); $kernel->updateModules($module_enabled); $kernel->boot(); $container = $kernel->getContainer(); @@ -67,8 +68,8 @@ function testCompileDIC() { // Now use the read-only storage implementation, simulating a "production" // environment. - $php_storage = new FileReadOnlyStorage($configuration); - $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage); + $conf['php_storage']['service_container']['class'] = 'Drupal\Component\PhpStorage\FileReadOnlyStorage'; + $kernel = new DrupalKernel('testing', FALSE, $classloader); $kernel->updateModules($module_enabled); $kernel->boot(); $container = $kernel->getContainer(); @@ -77,6 +78,10 @@ function testCompileDIC() { $refClass->getParentClass()->getName() == 'Symfony\Component\DependencyInjection\Container' && !$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder'); $this->assertTrue($is_compiled_container); + // Test that our synthetic services are there. + $classloader = $container->get('class_loader'); + $refClass = new ReflectionClass($classloader); + $this->assertTrue($refClass->hasMethod('getNamespaces'), 'Container has a classloader'); // We make this assertion here purely to show that the new container below // is functioning correctly, i.e. we get a brand new ContainerBuilder @@ -90,12 +95,12 @@ function testCompileDIC() { // Add another module so that we can test that the new module's bundle is // registered to the new container. $module_enabled['bundle_test'] = 'bundle_test'; - $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage); + $kernel = new DrupalKernel('testing', FALSE, $classloader); $kernel->updateModules($module_enabled); $kernel->boot(); // Instantiate it a second time and we should still get a ContainerBuilder // class because we are using the read-only PHP storage. - $kernel = new DrupalKernel('testing', FALSE, $classloader, $php_storage); + $kernel = new DrupalKernel('testing', FALSE, $classloader); $kernel->updateModules($module_enabled); $kernel->boot(); $container = $kernel->getContainer(); @@ -104,6 +109,10 @@ function testCompileDIC() { $this->assertTrue($is_container_builder); // Assert that the new module's bundle was registered to the new container. $this->assertTrue($container->has('bundle_test_class')); + // Test that our synthetic services are there. + $classloader = $container->get('class_loader'); + $refClass = new ReflectionClass($classloader); + $this->assertTrue($refClass->hasMethod('getNamespaces'), 'Container has a classloader'); // Restore the original container. drupal_container($original_container); 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 df59ea08d8aae98d7a992c536f9676896deeb85e..ea2026d0522d969d90bd7ed0d9060ade3d62d173 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ClassLoaderTest.php @@ -37,10 +37,16 @@ function testClassLoading() { module_disable(array('module_autoload_test'), FALSE); $this->resetAll(); - // Check twice to test an unprimed and primed system_list() cache. + // The first request after a module has been disabled will result in that + // module's namespace getting registered because the kernel registers all + // namespaces in the existing 'container.modules' parameter before checking + // whether the list of modules has changed and rebuilding the container. + // @todo Fix the behavior so that the namespace is not registered even on the + // first request after disabling the module and revert this test to having + // the assertion inside the loop. See http://drupal.org/node/1846376 for ($i=0; $i<2; $i++) { $this->drupalGet('module-test/class-loading'); - $this->assertNoText($expected, 'Autoloader does not load classes from a disabled module.'); } + $this->assertNoText($expected, 'Autoloader does not load classes from a disabled module.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/PhpStorage/FileStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/PhpStorage/FileStorageTest.php index 6280cc40163c2a471320c967d8dfdae3a8f2e9e9..d1de46eec79089afaff3d18280b19183ce7a10a7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/PhpStorage/FileStorageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/PhpStorage/FileStorageTest.php @@ -39,7 +39,7 @@ function setUp() { * Tests basic load/save/delete operations. */ function testCRUD() { - $php = drupal_php_storage('simpletest'); + $php = $this->storageFactory->get('simpletest'); $this->assertIdentical(get_class($php), 'Drupal\Component\PhpStorage\FileStorage'); $this->assertCRUD($php); } @@ -48,7 +48,7 @@ function testCRUD() { * Tests writing with one class and reading with another. */ function testReadOnly() { - $php = drupal_php_storage('simpletest'); + $php = $this->storageFactory->get('simpletest'); $name = $this->randomName() . '/' . $this->randomName() . '.php'; // Find a global that doesn't exist. @@ -60,7 +60,7 @@ function testReadOnly() { $code = "save($name, $code); $this->assertIdentical($success, TRUE); - $php_read = drupal_php_storage('readonly'); + $php_read = $this->storageFactory->get('readonly'); $php_read->load($name); $this->assertTrue($GLOBALS[$random]); diff --git a/core/modules/system/lib/Drupal/system/Tests/PhpStorage/MTimeProtectedFileStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/PhpStorage/MTimeProtectedFileStorageTest.php index a0649cb7302b301130737b099313de7b9a08b737..ae54c2fa152dc01ad17dbac0de915b632f3f9445 100644 --- a/core/modules/system/lib/Drupal/system/Tests/PhpStorage/MTimeProtectedFileStorageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/PhpStorage/MTimeProtectedFileStorageTest.php @@ -7,6 +7,8 @@ namespace Drupal\system\Tests\PhpStorage; +use Drupal\Component\PhpStorage\PhpStorageFactory; + /** * Tests the directory mtime based PHP loader implementation. */ @@ -45,7 +47,7 @@ function setUp() { * Tests basic load/save/delete operations. */ function testCRUD() { - $php = drupal_php_storage('simpletest'); + $php = $this->storageFactory->get('simpletest'); $this->assertIdentical(get_class($php), $this->storageClass); $this->assertCRUD($php); } @@ -57,7 +59,7 @@ function testCRUD() { * mtime too. */ function testSecurity() { - $php = drupal_php_storage('simpletest'); + $php = $this->storageFactory->get('simpletest'); $name = 'simpletest.php'; $php->save($name, 'storageFactory->get('simpletest'); $GLOBALS['hacked'] = FALSE; $untrusted_code = "storageFactory = new PhpStorageFactory; + } + /** * Assert that a PHP storage controller's load/save/delete operations work. */ diff --git a/core/modules/system/tests/http.php b/core/modules/system/tests/http.php index 8594648c7b2ffce071c6dc14b51906ca5899b960..f53bea6401cec91a7fbad6e3589f4950bdc4ad29 100644 --- a/core/modules/system/tests/http.php +++ b/core/modules/system/tests/http.php @@ -36,6 +36,6 @@ drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); -$kernel = new DrupalKernel('prod', FALSE, drupal_classloader()); +$kernel = new DrupalKernel('prod', FALSE, drupal_classloader(), FALSE); $response = $kernel->handle($request)->prepare($request)->send(); $kernel->terminate($request, $response); diff --git a/core/modules/system/tests/https.php b/core/modules/system/tests/https.php index 54d8878c92a490c873e7d94c95386b973308830b..e186f0c5704a8e0ce1e0219409a4437d4a0873af 100644 --- a/core/modules/system/tests/https.php +++ b/core/modules/system/tests/https.php @@ -35,6 +35,6 @@ drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); -$kernel = new DrupalKernel('prod', FALSE, drupal_classloader()); +$kernel = new DrupalKernel('prod', FALSE, drupal_classloader(), FALSE); $response = $kernel->handle($request)->prepare($request)->send(); $kernel->terminate($request, $response); diff --git a/index.php b/index.php index 4d562900e7ce39e5f36b7c6817f1d4a3807c4898..082276950bfa24f56d659d96a35450ff1daaf0f7 100644 --- a/index.php +++ b/index.php @@ -25,10 +25,10 @@ // @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, drupal_classloader(), drupal_php_storage('service_container')); +$kernel = new DrupalKernel('prod', FALSE, drupal_classloader()); // Create a request object from the HTTPFoundation. $request = Request::createFromGlobals();