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. // Reset all static caches.
drupal_static_reset(); drupal_static_reset();
// Wipe the PHP Storage caches. // Invalidate the container.
PhpStorageFactory::get('service_container')->deleteAll(); \Drupal::service('kernel')->invalidateContainer();
// Wipe the Twig PHP Storage cache.
PhpStorageFactory::get('twig')->deleteAll(); PhpStorageFactory::get('twig')->deleteAll();
// Rebuild module and theme data. // Rebuild module and theme data.

View File

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

View File

@ -126,6 +126,13 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
*/ */
protected $allowDumping; 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. * 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 // 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, // list will take effect when boot() is called. However we set a
// then rebuild the container in order to refresh the serviceProvider list // flag that the container needs a rebuild, so that a potentially cached
// and container. // 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) { if ($this->booted) {
$this->initializeContainer(TRUE); $this->initializeContainer();
} }
} }
@ -738,11 +747,9 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
/** /**
* Initializes the service container. * Initializes the service container.
* *
* @param bool $rebuild
* Force a container rebuild.
* @return \Symfony\Component\DependencyInjection\ContainerInterface * @return \Symfony\Component\DependencyInjection\ContainerInterface
*/ */
protected function initializeContainer($rebuild = FALSE) { protected function initializeContainer() {
$this->containerNeedsDumping = FALSE; $this->containerNeedsDumping = FALSE;
$session_started = FALSE; $session_started = FALSE;
if (isset($this->container)) { 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 // 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. // 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(); $fully_qualified_class_name = '\\' . $this->getClassNamespace() . '\\' . $this->getClassName();
// First, try to load from storage. // First, try to load from storage.
@ -781,6 +788,9 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
$container = $this->compileContainer(); $container = $this->compileContainer();
} }
// The container was rebuilt successfully.
$this->containerNeedsRebuild = FALSE;
$this->attachSynthetic($container); $this->attachSynthetic($container);
$this->container = $container; $this->container = $container;
@ -998,15 +1008,33 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
} }
/** /**
* Force a container rebuild. * {@inheritdoc}
*
* @return \Symfony\Component\DependencyInjection\ContainerInterface
*/ */
public function rebuildContainer() { public function rebuildContainer() {
// Empty module properties and for them to be reloaded from scratch. // Empty module properties and for them to be reloaded from scratch.
$this->moduleList = NULL; $this->moduleList = NULL;
$this->moduleData = array(); $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()); 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. * Prepare the kernel for handling a request without handling the request.
* *

View File

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

View File

@ -8,7 +8,6 @@
namespace Drupal\language; namespace Drupal\language;
use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\Language; use Drupal\Core\Language\Language;
@ -105,7 +104,7 @@ class ConfigurableLanguageManager extends LanguageManager implements Configurabl
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function rebuildServices() { 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\LanguageDefault;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Config\ConfigCrudEvent; use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents; use Drupal\Core\Config\ConfigEvents;
use Drupal\language\ConfigurableLanguageManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/** /**
@ -62,9 +62,8 @@ class ConfigSubscriber implements EventSubscriberInterface {
$this->languageManager->reset(); $this->languageManager->reset();
language_negotiation_url_prefixes_update(); language_negotiation_url_prefixes_update();
} }
// Trigger a container rebuild on the next request by deleting compiled // Trigger a container rebuild on the next request by invalidating it.
// from PHP storage. ConfigurableLanguageManager::rebuildServices();
PhpStorageFactory::get('service_container')->deleteAll();
} }
} }

View File

@ -35,4 +35,26 @@ class ContainerRebuildWebTest extends WebTestBase {
$this->assertHeader('container_rebuild_indicator', 'new-identifier'); $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')) { if ($indicator = Settings::get('deployment_identifier')) {
$container->setParameter('container_rebuild_indicator', $indicator); $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')) { if ($this->container->hasParameter('container_rebuild_indicator')) {
$event->getResponse()->headers->set('container_rebuild_indicator', $this->container->getParameter('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'));
}
} }
/** /**