diff -u b/core/includes/common.inc b/core/includes/common.inc --- b/core/includes/common.inc +++ b/core/includes/common.inc @@ -6853,6 +6853,9 @@ drupal_container()->get('router.builder')->rebuild(); menu_router_rebuild(); + // Wipe the DIC cache. + drupal_php_storage('service_container')->deleteAll(); + // Re-initialize the maintenance theme, if the current request attempted to // use it. Unlike regular usages of this function, the installer and update // scripts need to flush all caches during GET requests/page building. diff -u b/core/includes/file.inc b/core/includes/file.inc --- b/core/includes/file.inc +++ b/core/includes/file.inc @@ -1450,12 +1450,13 @@ $uri = "$dir/$filename"; $uri = file_stream_wrapper_uri_normalize($uri); if (is_dir($uri) && $options['recurse']) { - // Give priority to files in this folder by merging them in after any subdirectory files. + // Give priority to files in this folder by merging them in after + // any subdirectory files. $files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files); } elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) { - // Always use this match over anything already set in $files with the - // same $$options['key']. + // Always use this match over anything already set in $files with + // the same $options['key']. $file = new stdClass(); $file->uri = $uri; $file->filename = $filename; diff -u b/core/includes/install.core.inc b/core/includes/install.core.inc --- b/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1465,8 +1465,8 @@ // This will allow all freshly installed modules to be loaded. module_list_reset(); - // Instantiate a new kernel that will not compile the container. - $kernel = new DrupalKernel('prod', FALSE, NULL, FALSE); + // Instantiate the kernel. + $kernel = new DrupalKernel('prod', FALSE, NULL); $kernel->boot(); drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); } reverted: --- b/core/includes/module.inc +++ a/core/includes/module.inc @@ -119,7 +119,7 @@ * Builds a list of bootstrap modules and enabled modules and themes. * * @param $type + * The type of list to return: - * (optional) The type of list to return: * - module_enabled: All enabled modules. * - bootstrap: All enabled modules required for bootstrap. * - theme: All themes. @@ -138,13 +138,13 @@ * callers like system_list() to force-disable a possible configuration * storage controller cache or some other way to circumvent it/take it over. */ +function system_list($type) { -function system_list($type = NULL) { $lists = &drupal_static(__FUNCTION__); // For bootstrap modules, attempt to fetch the list from cache if possible. // if not fetch only the required information to fire bootstrap hooks // in case we are going to serve the page from cache. + if ($type == 'bootstrap') { - if ($type === 'bootstrap') { if (isset($lists['bootstrap'])) { return $lists['bootstrap']; } @@ -188,7 +188,7 @@ // Build a list of all enabled modules. $lists['module_enabled'][$name] = $name; // Build a list of filenames so drupal_get_filename can use it. + $lists['filepaths'][] = array( - $lists['filepaths'][$name] = array( 'type' => 'module', 'name' => $name, 'filepath' => $module_files[$name], @@ -215,7 +215,7 @@ $lists['theme'][$name] = $theme; // Build a list of filenames so drupal_get_filename can use it. if (isset($enabled_themes[$name])) { + $lists['filepaths'][] = array( - $lists['filepaths'][$name] = array( 'type' => 'theme', 'name' => $name, 'filepath' => $theme->filename, @@ -249,9 +249,6 @@ // Set the theme engine prefix. $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; } - // Store a hash of the enabled modules for use when compiling the DIC. - $lists['system_list_hash'] = hash('sha256', implode(',', array_keys($lists['filepaths']))); - cache('bootstrap')->set('system_list', $lists); } // To avoid a separate database lookup for the filepath, prime the @@ -262,7 +259,7 @@ } } + return $lists[$type]; - return $type ? $lists[$type] : $lists; } /** diff -u b/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php b/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php --- b/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php +++ b/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php @@ -78,2 +78,9 @@ } + + /** + * Implements Drupal\Component\PhpStorage\PhpStorageInterface::deleteAll(). + */ + public function deleteAll() { + return FALSE; + } } diff -u b/core/lib/Drupal/Component/PhpStorage/FileStorage.php b/core/lib/Drupal/Component/PhpStorage/FileStorage.php --- b/core/lib/Drupal/Component/PhpStorage/FileStorage.php +++ b/core/lib/Drupal/Component/PhpStorage/FileStorage.php @@ -80,2 +80,18 @@ } + + /** + * Implements Drupal\Component\PhpStorage\PhpStorageInterface::deleteAll(). + */ + function deleteAll() { + return file_unmanaged_delete_recursive($this->directory, array(__CLASS__, 'filePreDeleteCallback')); + } + + /** + * Ensures files and directories are deletable. + */ + public static function filePreDeleteCallback($path) { + if (file_exists($path)) { + chmod($path, 0700); + } + } } diff -u b/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php b/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php --- b/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php +++ b/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php @@ -75,2 +75,7 @@ public function delete($name); + + /** + * Removes all files in this bin. + */ + public function deleteAll(); } diff -u b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterMatchersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterMatchersPass.php --- b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterMatchersPass.php +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterMatchersPass.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\Core\DependencyInjection\Compiler\RegisterMatchersPass. + * Contains Drupal\Core\DependencyInjection\Compiler\RegisterMatchersPass. */ namespace Drupal\Core\DependencyInjection\Compiler; @@ -11,7 +11,17 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +/** + * Adds services tagged 'chained_matcher' to the 'matcher' service. + */ class RegisterMatchersPass implements CompilerPassInterface { + + /** + * Adds services tagged 'chained_matcher' to the 'matcher' service. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * The container to process. + */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('matcher')) { return; diff -u b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php --- b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterNestedMatchersPass.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\Core\DependencyInjection\Compiler\RegisterNestedMatchersPass. + * Contains Drupal\Core\DependencyInjection\Compiler\RegisterNestedMatchersPass. */ namespace Drupal\Core\DependencyInjection\Compiler; @@ -11,7 +11,17 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +/** + * Adds servies tagged 'nested_matcher' services to the tagged_matcher service. + */ class RegisterNestedMatchersPass implements CompilerPassInterface { + + /** + * Adds servies tagged 'nested_matcher' services to the tagged_matcher service. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * The container to process. + */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('nested_matcher')) { return; diff -u b/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php --- b/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -7,6 +7,7 @@ namespace Drupal\Core; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\CoreBundle; use Drupal\Component\PhpStorage\PhpStorageInterface; use Symfony\Component\HttpKernel\Kernel; @@ -32,47 +33,52 @@ * * @var array */ - protected $systemList; + protected $moduleList; /** - * Whether to use a compiled Container as opposed to a ContainerBuilder. + * Drupal cache object for getting or setting the class name of the compiled container. * - * @var boolean + * @var \Drupal\Core\Cache\CacheBackendInterface */ - protected $useCompiledContainer; + protected $compilationIndexCache; /** * PHP code storage object to use for the compiled container. * - * @var Drupal\Component\PhpStorage\PhpStorageInterface + * @var \Drupal\Component\PhpStorage\PhpStorageInterface */ protected $storage; /** - * @todo The first two constructor parameters for the Kernel class are for - * environment, e.g. 'prod', 'dev', and a boolean indicating whether it is - * in debug mode. Drupal does not currently make use of either of these, - * though that may change with http://drupal.org/node/1537198. + * Constructs a DrupalKernel object. * * @param string $environment + * String indicating the environment, e.g. 'prod' or 'dev'. Used by + * Symfony\Component\HttpKernel\Kernel::__construct(). Drupal does not use + * this value currently. Pass 'prod'. * @param bool $debug - * @param array $system_list - * The same data structure as system_list(). - * @param bool $use_compiled_container - * Whether to compile the container to disk or not. + * Boolean indicating whether we are in debug mode; Used by + * Symfony\Component\HttpKernel\Kernel::__construct(). Drupal does not use + * this value currently. Pass TRUE. + * @param array $module_list + * (optional) The array of enabled modules as returned by module_list(). + * @param Drupal\Core\Cache\CacheBackendInterface $compilation_index_cache + * (optional) If wanting to dump a compiled container to disk or use a + * previously compiled container, the cache object for the bin that stores + * the class name of the compiled container. */ - public function __construct($environment, $debug, array $system_list = NULL, $use_compiled_container = TRUE) { + public function __construct($environment, $debug, array $module_list = NULL, CacheBackendInterface $compilation_index_cache = NULL) { parent::__construct($environment, $debug); - $this->useCompiledContainer = $use_compiled_container; + $this->compilationIndexCache = $compilation_index_cache; $this->storage = drupal_php_storage('service_container'); - if (isset($system_list)) { - $this->systemList = $system_list; + if (isset($module_list)) { + $this->moduleList = $module_list; } else { // @todo This is a temporary measure which will no longer be necessary // once we have an ExtensionHandler for managing this list. See // http://drupal.org/node/1331486. - $this->systemList = system_list(); + $this->moduleList = module_list(); } } @@ -94,8 +100,7 @@ new CoreBundle(), ); - $modules = $this->systemList['module_enabled']; - foreach ($modules as $module) { + foreach ($this->moduleList as $module) { $camelized = ContainerBuilder::camelize($module); $class = "Drupal\\{$module}\\{$camelized}Bundle"; if (class_exists($class)) { @@ -108,42 +113,32 @@ /** * Initializes the service container. - * - * @todo We are compiling the container and dumping to a PHP file whose name - * is based on the list of enabled modules. A new file is created when this - * list changes but there is currently no garbage collection in place for - * the old files. See http://drupal.org/node/1759582. */ protected function initializeContainer() { $this->container = NULL; - if ($this->useCompiledContainer) { - // While the default Symfony class name only depends on the environment, for - // testing purposes we can't use that because there would be a collision as - // each test method creates a new kernel. On the other hand, the container - // only depends on the enabled modules (and only on those that provide - // bundles) so we base the name of the container class on the hash of the - // enabled modules. We can't directly use the hash though because PHP - // identifiers always start with a letter and hashes don't so we add a - // character to the beginning. This mechanism also avoids the problem of - // needing to rebuild the DIC at the right time on module enable: simply on - // the next request the hash will change and so the container will be - // rebuilt. - $class = 'c' . $this->systemList['system_list_hash'] . ucfirst($this->environment) . ($this->debug ? 'Debug' : ''); - $cache_file = $class . '.php'; - - // First, try to load. - if (!class_exists($class)) { - $this->storage->load($cache_file); - } - // If the load succeeded or the class already existed, use it. - if (class_exists($class)) { - $fully_qualified_classname = '\\' . $class; - $this->container = new $fully_qualified_classname; + if ($this->compilationIndexCache) { + // The name of the compiled container class is generated from the hash of + // its contents and cached. This enables multiple compiled containers + // (for example, for different states of which modules are enabled) to + // exist simultaneously on disk and in memory. + if ($cache = $this->compilationIndexCache->get(implode(':', array('service_container', $this->environment, $this->debug)))) { + $class = $cache->data; + $cache_file = $class . '.php'; + + // First, try to load. + if (!class_exists($class, FALSE)) { + $this->storage->load($cache_file); + } + // If the load succeeded or the class already existed, use it. + if (class_exists($class, FALSE)) { + $fully_qualified_class_name = '\\' . $class; + $this->container = new $fully_qualified_class_name; + } } } if (!isset($this->container)) { $this->container = $this->buildContainer(); - if ($this->useCompiledContainer && !$this->dumpDrupalContainer($cache_file, $this->container, $class, $this->getContainerBaseClass())) { + if ($this->compilationIndexCache && !$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'; @@ -193,29 +188,26 @@ * This method is based on the dumpContainer method in the parent class, but * that method is reliant on the Config component which we do not use here. * - * @param string $cache_file - * The full filename to write to. * @param ContainerBuilder $container * The service container. - * @param string $class - * The name of the class to generate. * @param string $baseClass * The name of the container's base class - * @param PhpStorageInterface $storage - * The PHP storage class. * * @return bool * TRUE if the container was successfully dumped to disk. */ - protected function dumpDrupalContainer($cache_file, ContainerBuilder $container, $class, $baseClass) { + protected function dumpDrupalContainer(ContainerBuilder $container, $baseClass) { if (!$this->storage->writeable()) { return FALSE; } // Cache the container. $dumper = new PhpDumper($container); - $content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass)); + $content = $dumper->dump(array('class' => 'DrupalServiceContainerStub', 'base_class' => $baseClass)); + $class = 'c' . hash('sha256', $content); + $content = str_replace('DrupalServiceContainerStub', $class, $content); + $this->compilationIndexCache->set(implode(':', array('service_container', $this->environment, $this->debug)), $class); - return $this->storage->save($cache_file, $content); + return $this->storage->save($class . '.php', $content); } /** diff -u b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php --- b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -18,8 +18,9 @@ class FinishResponseSubscriber implements EventSubscriberInterface { /** + * The LanguageManager object for retrieving the correct language code. + * * @var LanguageManager - * The LanguageManager object for retrieving the correct language code. */ protected $languageManager; @@ -27,6 +28,7 @@ * Constructs a FinishResponseSubscriber object. * * @param LanguageManager $language_manager + * The LanguageManager object for retrieving the correct language code. */ public function __construct(LanguageManager $language_manager) { $this->languageManager = $language_manager; diff -u b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php --- b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -846,7 +846,7 @@ // container in drupal_container(). Drupal\simpletest\TestBase::tearDown() // restores the original container. // @see Drupal\Core\DrupalKernel::initializeContainer() - $this->kernel = new DrupalKernel('testing', FALSE, NULL, FALSE); + $this->kernel = new DrupalKernel('testing', FALSE, NULL); // 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 -u b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php --- b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php @@ -2,17 +2,18 @@ /** * @file - * Definition of Drupal\system\Tests\DrupalKernel\DrupalKernelTest. + * Contains Drupal\system\Tests\DrupalKernel\DrupalKernelTest. */ namespace Drupal\system\Tests\DrupalKernel; +use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\DrupalKernel; use Drupal\simpletest\UnitTestBase; use ReflectionClass; /** - * Test compilation of the DIC. + * Tests compilation of the DIC. */ class DrupalKernelTest extends UnitTestBase { @@ -25,7 +26,7 @@ } /** - * Test DIC compilation. + * Tests DIC compilation. */ function testCompileDIC() { // Because we'll be instantiating a new kernel during this test, the @@ -38,20 +39,16 @@ 'class' => 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage', 'secret' => $GLOBALS['drupal_hash_salt'], ); + $cache = new MemoryBackend('test'); $module_enabled = array( 'system' => 'system', 'user' => 'user', ); - $module_enabled_hash = hash('sha256', implode(',', $module_enabled)); - $system_list = array( - 'module_enabled' => $module_enabled, - 'system_list_hash' => $module_enabled_hash, - ); - $kernel = new DrupalKernel('testing', FALSE, $system_list); + $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache); $kernel->boot(); // Instantiate it a second time and we should get the compiled Container // class. - $kernel = new DrupalKernel('testing', FALSE, $system_list); + $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache); $kernel->boot(); $container = $kernel->getContainer(); $refClass = new ReflectionClass($container); @@ -69,7 +66,7 @@ $conf['php_storage']['service_container'] = array( 'class' => 'Drupal\Component\PhpStorage\FileReadOnlyStorage', ); - $kernel = new DrupalKernel('testing', FALSE, $system_list); + $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache); $kernel->boot(); $container = $kernel->getContainer(); $refClass = new ReflectionClass($container); @@ -87,24 +84,19 @@ // Reset the container. drupal_container(NULL, TRUE); - // Add another module so that a different hash is used for the class name - // and we can test that the new module's bundle is registered to the new - // container. + // Add another module so that we can test that the new module's bundle is + // registered to the new container. $module_enabled = array( 'system' => 'system', 'user' => 'user', 'bundle_test' => 'bundle_test', ); - $module_enabled_hash = hash('sha256', implode(',', array_keys($module_enabled))); - $system_list = array( - 'module_enabled' => $module_enabled, - 'system_list_hash' => $module_enabled_hash, - ); - $kernel = new DrupalKernel('testing', FALSE, $system_list); + $cache->flush(); + $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache); $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, $system_list); + $kernel = new DrupalKernel('testing', FALSE, $module_enabled, $cache); $kernel->boot(); $container = $kernel->getContainer(); $refClass = new ReflectionClass($container); only in patch2: unchanged: --- a/index.php +++ b/index.php @@ -28,7 +28,7 @@ drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); // @todo Figure out how best to handle the Kernel constructor parameters. -$kernel = new DrupalKernel('prod', FALSE); +$kernel = new DrupalKernel('prod', FALSE, NULL, cache('bootstrap')); // Create a request object from the HTTPFoundation. $request = Request::createFromGlobals();