Unverified Commit ada497ac authored by alexpott's avatar alexpott

Issue #2752961 by dawehner, Wim Leers, Jo Fitzgerald, bradjones1, chr.fritsch,...

Issue #2752961 by dawehner, Wim Leers, Jo Fitzgerald, bradjones1, chr.fritsch, greg.1.anderson, anavarre, Berdir, moshe weitzman, bkosborne, alexpott, Crell, catch, msonnabaum, lauriii, joelpittet, deviantintegral, cilefen, effulgentsia, xjm: No reliable method exists for clearing the Twig cache
parent 0db1c040
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
use Drupal\Core\Render\Element\Link; use Drupal\Core\Render\Element\Link;
use Drupal\Core\Render\Markup; use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup; use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element; use Drupal\Core\Render\Element;
...@@ -1117,7 +1116,7 @@ function drupal_flush_all_caches() { ...@@ -1117,7 +1116,7 @@ function drupal_flush_all_caches() {
\Drupal::service('kernel')->invalidateContainer(); \Drupal::service('kernel')->invalidateContainer();
// Wipe the Twig PHP Storage cache. // Wipe the Twig PHP Storage cache.
PhpStorageFactory::get('twig')->deleteAll(); \Drupal::service('twig')->invalidate();
// Rebuild module and theme data. // Rebuild module and theme data.
$module_data = system_rebuild_module_data(); $module_data = system_rebuild_module_data();
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* Miscellaneous functions. * Miscellaneous functions.
*/ */
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\Cache;
use Drupal\Core\DrupalKernel; use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
...@@ -30,7 +29,7 @@ function drupal_rebuild($class_loader, Request $request) { ...@@ -30,7 +29,7 @@ function drupal_rebuild($class_loader, Request $request) {
restore_exception_handler(); restore_exception_handler();
// Force kernel to rebuild php cache. // Force kernel to rebuild php cache.
PhpStorageFactory::get('twig')->deleteAll(); \Drupal::service('twig')->invalidate();
// 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);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace Drupal\Core\Template; namespace Drupal\Core\Template;
use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Render\Markup; use Drupal\Core\Render\Markup;
use Drupal\Core\State\StateInterface; use Drupal\Core\State\StateInterface;
...@@ -16,6 +17,18 @@ ...@@ -16,6 +17,18 @@
*/ */
class TwigEnvironment extends \Twig_Environment { class TwigEnvironment extends \Twig_Environment {
/**
* Key name of the Twig cache prefix metadata key-value pair in State.
*/
const CACHE_PREFIX_METADATA_KEY = 'twig_extension_hash_prefix';
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/** /**
* Static cache of template classes. * Static cache of template classes.
* *
...@@ -43,6 +56,8 @@ class TwigEnvironment extends \Twig_Environment { ...@@ -43,6 +56,8 @@ class TwigEnvironment extends \Twig_Environment {
* The options for the Twig environment. * The options for the Twig environment.
*/ */
public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, StateInterface $state, \Twig_LoaderInterface $loader = NULL, $options = []) { public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, StateInterface $state, \Twig_LoaderInterface $loader = NULL, $options = []) {
$this->state = $state;
// Ensure that twig.engine is loaded, given that it is needed to render a // Ensure that twig.engine is loaded, given that it is needed to render a
// template because functions like TwigExtension::escapeFilter() are called. // template because functions like TwigExtension::escapeFilter() are called.
require_once $root . '/core/themes/engines/twig/twig.engine'; require_once $root . '/core/themes/engines/twig/twig.engine';
...@@ -63,7 +78,7 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension ...@@ -63,7 +78,7 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension
$this->addExtension($sandbox); $this->addExtension($sandbox);
if ($options['cache'] === TRUE) { if ($options['cache'] === TRUE) {
$current = $state->get('twig_extension_hash_prefix', ['twig_extension_hash' => '']); $current = $state->get(static::CACHE_PREFIX_METADATA_KEY, ['twig_extension_hash' => '']);
if ($current['twig_extension_hash'] !== $twig_extension_hash || empty($current['twig_cache_prefix'])) { if ($current['twig_extension_hash'] !== $twig_extension_hash || empty($current['twig_cache_prefix'])) {
$current = [ $current = [
'twig_extension_hash' => $twig_extension_hash, 'twig_extension_hash' => $twig_extension_hash,
...@@ -71,7 +86,7 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension ...@@ -71,7 +86,7 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension
'twig_cache_prefix' => uniqid(), 'twig_cache_prefix' => uniqid(),
]; ];
$state->set('twig_extension_hash_prefix', $current); $state->set(static::CACHE_PREFIX_METADATA_KEY, $current);
} }
$this->twigCachePrefix = $current['twig_cache_prefix']; $this->twigCachePrefix = $current['twig_cache_prefix'];
...@@ -82,6 +97,18 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension ...@@ -82,6 +97,18 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension
parent::__construct($this->loader, $options); parent::__construct($this->loader, $options);
} }
/**
* Invalidates all compiled Twig templates.
*
* @see \drupal_flush_all_caches
*/
public function invalidate() {
PhpStorageFactory::get('twig')->deleteAll();
$this->templateClasses = [];
$this->loadedTemplates = [];
$this->state->delete(static::CACHE_PREFIX_METADATA_KEY);
}
/** /**
* Get the cache prefixed used by \Drupal\Core\Template\TwigPhpStorageCache * Get the cache prefixed used by \Drupal\Core\Template\TwigPhpStorageCache
* *
......
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Html;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Site\Settings; use Drupal\Core\Site\Settings;
use Drupal\Core\Template\TwigPhpStorageCache; use Drupal\Core\Template\TwigPhpStorageCache;
use Drupal\KernelTests\KernelTestBase; use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\DependencyInjection\Definition;
/** /**
* Tests the twig environment. * Tests the twig environment.
...@@ -143,4 +145,52 @@ public function testCacheFilename() { ...@@ -143,4 +145,52 @@ public function testCacheFilename() {
$this->assertNotEqual($new_extension_filename, $original_filename); $this->assertNotEqual($new_extension_filename, $original_filename);
} }
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
parent::register($container);
$container->setDefinition('twig_loader__file_system', new Definition('Twig_Loader_Filesystem', [[sys_get_temp_dir()]]))
->addTag('twig.loader');
}
/**
* Test template invalidation.
*/
public function testTemplateInvalidation() {
$template_before = <<<TWIG
<div>Hello before</div>
TWIG;
$template_after = <<<TWIG
<div>Hello after</div>
TWIG;
$tempfile = tempnam(sys_get_temp_dir(), '__METHOD__') . '.html.twig';
file_put_contents($tempfile, $template_before);
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
$environment = \Drupal::service('twig');
$output = $environment->load(basename($tempfile))->render();
$this->assertEquals($template_before, $output);
file_put_contents($tempfile, $template_after);
$output = $environment->load(basename($tempfile))->render();
$this->assertEquals($template_before, $output);
$environment->invalidate();
// Manually change $templateClassPrefix to force a different template
// classname, as the other class is still loaded. This wouldn't be a problem
// on a real site where you reload the page.
$reflection = new \ReflectionClass($environment);
$property_reflection = $reflection->getProperty('templateClassPrefix');
$property_reflection->setAccessible(TRUE);
$property_reflection->setValue($environment, 'otherPrefix');
$output = $environment->load(basename($tempfile))->render();
$this->assertEquals($template_after, $output);
}
} }
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