Issue #2493665 by Fabianx, dawehner, msonnabaum, catch: Add centralized container invalidation method

8.0.x
Alex Pott 2015-07-07 17:35:44 +01:00
parent c78d806eb3
commit 5a528e59df
10 changed files with 99 additions and 28 deletions

View File

@ -1295,8 +1295,10 @@ function drupal_flush_all_caches() {
// Reset all static caches.
drupal_static_reset();
// Wipe the PHP Storage caches.
PhpStorageFactory::get('service_container')->deleteAll();
// Invalidate the container.
\Drupal::service('kernel')->invalidateContainer();
// Wipe the Twig PHP Storage cache.
PhpStorageFactory::get('twig')->deleteAll();
// Rebuild module and theme data.

View File

@ -29,13 +29,17 @@ function drupal_rebuild(ClassLoader $class_loader, Request $request) {
restore_error_handler();
restore_exception_handler();
// Force kernel to rebuild container.
PhpStorageFactory::get('service_container')->deleteAll();
// Force kernel to rebuild php cache.
PhpStorageFactory::get('twig')->deleteAll();
// Bootstrap up to where caches exist and clear them.
$kernel = new DrupalKernel('prod', $class_loader);
$kernel->setSitePath(DrupalKernel::findSitePath($request));
// Invalidate the container.
$kernel->invalidateContainer();
// Prepare a NULL request.
$kernel->prepareLegacyRequest($request);
foreach (Cache::getBins() as $bin) {

View File

@ -126,6 +126,13 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
*/
protected $allowDumping;
/**
* Whether the container needs to be rebuilt the next time it is initialized.
*
* @var bool
*/
protected $containerNeedsRebuild = FALSE;
/**
* Whether the container needs to be dumped once booting is complete.
*
@ -695,11 +702,13 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
}
// If we haven't yet booted, we don't need to do anything: the new module
// list will take effect when boot() is called. If we have already booted,
// then rebuild the container in order to refresh the serviceProvider list
// and container.
// list will take effect when boot() is called. However we set a
// flag that the container needs a rebuild, so that a potentially cached
// container is not used. If we have already booted, then rebuild the
// container in order to refresh the serviceProvider list and container.
$this->containerNeedsRebuild = TRUE;
if ($this->booted) {
$this->initializeContainer(TRUE);
$this->initializeContainer();
}
}
@ -738,11 +747,9 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
/**
* Initializes the service container.
*
* @param bool $rebuild
* Force a container rebuild.
* @return \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected function initializeContainer($rebuild = FALSE) {
protected function initializeContainer() {
$this->containerNeedsDumping = FALSE;
$session_started = FALSE;
if (isset($this->container)) {
@ -764,7 +771,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
// If the module list hasn't already been set in updateModules and we are
// not forcing a rebuild, then try and load the container from the disk.
if (empty($this->moduleList) && !$rebuild) {
if (empty($this->moduleList) && !$this->containerNeedsRebuild) {
$fully_qualified_class_name = '\\' . $this->getClassNamespace() . '\\' . $this->getClassName();
// First, try to load from storage.
@ -781,6 +788,9 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
$container = $this->compileContainer();
}
// The container was rebuilt successfully.
$this->containerNeedsRebuild = FALSE;
$this->attachSynthetic($container);
$this->container = $container;
@ -998,15 +1008,33 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
}
/**
* Force a container rebuild.
*
* @return \Symfony\Component\DependencyInjection\ContainerInterface
* {@inheritdoc}
*/
public function rebuildContainer() {
// Empty module properties and for them to be reloaded from scratch.
$this->moduleList = NULL;
$this->moduleData = array();
return $this->initializeContainer(TRUE);
$this->containerNeedsRebuild = TRUE;
return $this->initializeContainer();
}
/**
* {@inheritdoc}
*/
public function invalidateContainer() {
// An invalidated container needs a rebuild.
$this->containerNeedsRebuild = TRUE;
// If we have not yet booted, settings or bootstrap services might not yet
// be available. In that case the container will not be loaded from cache
// due to the above setting when the Kernel is booted.
if (!$this->booted) {
return;
}
// Also wipe the PHP Storage caches, so that the container is rebuilt
// for the next request.
$this->storage()->deleteAll();
}
/**

View File

@ -96,6 +96,18 @@ interface DrupalKernelInterface extends HttpKernelInterface {
*/
public function updateModules(array $module_list, array $module_filenames = array());
/**
* Force a container rebuild.
*
* @return \Symfony\Component\DependencyInjection\ContainerInterface
*/
public function rebuildContainer();
/**
* Invalidate the service container for the next request.
*/
public function invalidateContainer();
/**
* Prepare the kernel for handling a request without handling the request.
*

View File

@ -16,13 +16,11 @@ class InstallerKernel extends DrupalKernel {
/**
* {@inheritdoc}
*
* @param bool $rebuild
* Force a container rebuild. Unlike the parent method, this defaults to
* TRUE.
*/
protected function initializeContainer($rebuild = TRUE) {
$container = parent::initializeContainer($rebuild);
protected function initializeContainer() {
// Always force a container rebuild.
$this->containerNeedsRebuild = TRUE;
$container = parent::initializeContainer();
return $container;
}

View File

@ -8,7 +8,6 @@
namespace Drupal\language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\Language;
@ -105,7 +104,7 @@ class ConfigurableLanguageManager extends LanguageManager implements Configurabl
* {@inheritdoc}
*/
public static function rebuildServices() {
PhpStorageFactory::get('service_container')->deleteAll();
\Drupal::service('kernel')->invalidateContainer();
}
/**

View File

@ -9,9 +9,9 @@ namespace Drupal\language\EventSubscriber;
use Drupal\Core\Language\LanguageDefault;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
use Drupal\language\ConfigurableLanguageManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
@ -62,9 +62,8 @@ class ConfigSubscriber implements EventSubscriberInterface {
$this->languageManager->reset();
language_negotiation_url_prefixes_update();
}
// Trigger a container rebuild on the next request by deleting compiled
// from PHP storage.
PhpStorageFactory::get('service_container')->deleteAll();
// Trigger a container rebuild on the next request by invalidating it.
ConfigurableLanguageManager::rebuildServices();
}
}

View File

@ -35,4 +35,26 @@ class ContainerRebuildWebTest extends WebTestBase {
$this->assertHeader('container_rebuild_indicator', 'new-identifier');
}
/**
* Tests container invalidation.
*/
public function testContainerInvalidation() {
// Ensure that parameter is not set.
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', FALSE);
// Ensure that after setting the parameter, without a container rebuild the
// parameter is still not set.
$this->writeSettings(['settings' => ['container_rebuild_test_parameter' => (object) ['value' => 'rebuild_me_please', 'required' => TRUE]]]);
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', FALSE);
// Ensure that after container invalidation the parameter is set.
\Drupal::service('kernel')->invalidateContainer();
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', 'rebuild_me_please');
}
}

View File

@ -26,5 +26,9 @@ class ServiceProviderTestServiceProvider implements ServiceModifierInterface {
if ($indicator = Settings::get('deployment_identifier')) {
$container->setParameter('container_rebuild_indicator', $indicator);
}
if ($parameter = Settings::get('container_rebuild_test_parameter')) {
$container->setParameter('container_rebuild_test_parameter', $parameter);
}
}
}

View File

@ -51,6 +51,9 @@ class TestClass implements EventSubscriberInterface, DestructableInterface, Cont
if ($this->container->hasParameter('container_rebuild_indicator')) {
$event->getResponse()->headers->set('container_rebuild_indicator', $this->container->getParameter('container_rebuild_indicator'));
}
if ($this->container->hasParameter('container_rebuild_test_parameter')) {
$event->getResponse()->headers->set('container_rebuild_test_parameter', $this->container->getParameter('container_rebuild_test_parameter'));
}
}
/**