Skip to content
Snippets Groups Projects
Commit b11e9d9d authored by catch's avatar catch
Browse files

Issue #3032078 by jrglasgow, dagomar, Berdir, nikitagupta, catch,...

Issue #3032078 by jrglasgow, dagomar, Berdir, nikitagupta, catch, anmolgoyal74, technoveltyco, idebr, ndf, jonas139, nod_: Multiple webheads can cause infinite growth of Twig cache

(cherry picked from commit a59f4db3)
parent 9dd6583b
No related branches found
No related tags found
17 merge requests!8506Draft: Issue #3456536 by ibrahim tameme,!5646Issue #3350972 by nod_: [random test failure]...,!5600Issue #3350972 by nod_: [random test failure]...,!5343Issue #3305066 by quietone, Rename RedirectLeadingSlashesSubscriber,!3603#ISSUE 3346218 Add a different message on edit comment,!3555Issue #2473873: Views entity operations lack cacheability support, resulting in incorrect dropbuttons,!3494Issue #3327018 by Spokje, longwave, xjm, mondrake: Update PHPStan to 1.9.3 and...,!3410Issue #3340128: UserLoginForm::submitForm has some dead code,!3389Issue #3325184 by Spokje, andypost, xjm, smustgrave: $this->configFactory is...,!3381Issue #3332363: Refactor Claro's menus-and-lists stylesheet,!3307Issue #3326193: CKEditor 5 can grow past the viewport when there is a lot of content,!3236Issue #3332419: Refactor Claro's messages stylesheet,!3231Draft: Issue #3049525 by longwave, fougere, larowlan, kim.pepper, AaronBauman, Wim...,!3212Issue #3294003: Refactor Claro's entity-meta stylesheet,!3194Issue #3330981: Fix PHPStan L1 error "Relying on entity queries to check access by default is deprecated...",!3143Issue #3313342: [PHP 8.1] Deprecated function: strpos(): Passing null to parameter #1 LayoutBuilderUiCacheContext.php on line 28,!2972Issue #1845004: Replace custom password hashing library with PHP 5.5 password_hash()
......@@ -9,7 +9,7 @@
/**
* Adds the twig_extension_hash parameter to the container.
*
* Parameter twig_extension_hash is a hash of all extension mtimes for Twig
* Parameter twig_extension_hash is a crc32 hash of all extensions for Twig
* template invalidation.
*/
class TwigExtensionPass implements CompilerPassInterface {
......@@ -23,8 +23,8 @@ public function process(ContainerBuilder $container) {
$class_name = $container->getDefinition($service_id)->getClass();
$reflection = new \ReflectionClass($class_name);
// We use the class names as hash in order to invalidate on new extensions
// and mtime for every time we change an existing file.
$twig_extension_hash .= $class_name . filemtime($reflection->getFileName());
// and crc32 for every time we change an existing file.
$twig_extension_hash .= $class_name . hash_file('crc32', $reflection->getFileName());
}
$container->setParameter('twig_extension_hash', Crypt::hashBase64($twig_extension_hash));
......
......@@ -6,6 +6,7 @@
use Drupal\Component\Utility\Html;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Site\Settings;
use Drupal\Core\Template\TwigEnvironment;
use Drupal\Core\Template\TwigPhpStorageCache;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\DependencyInjection\Definition;
......@@ -229,4 +230,62 @@ public function testTemplateInvalidation() {
$this->assertEquals($template_after, $output);
}
/**
* Test twig file prefix change.
*/
public function testTwigFilePrefixChange() {
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
$environment = \Drupal::service('twig');
$cache_prefixes = [];
$cache_filenames = [];
// Assume this is the service container of webserver A.
$container_a = $this->container;
$template_name = 'core/modules/system/templates/container.html.twig';
// Request 1 handled by webserver A.
$cache_prefixes[] = \Drupal::state()->get(TwigEnvironment::CACHE_PREFIX_METADATA_KEY)['twig_cache_prefix'];
$cache_filenames[] = $environment->getCache()->generateKey($template_name, $environment->getTemplateClass($template_name));
// Assume this is the service container of webserver B.
// Assume that the files on the webserver B have a different mtime than
// webserver A.
touch('core/lib/Drupal/Core/Template/TwigExtension.php');
clearstatcache(TRUE, 'core/lib/Drupal/Core/Template/TwigExtension.php');
$container_b = \Drupal::service('kernel')->rebuildContainer();
// Request 2 handled by webserver B.
\Drupal::setContainer($container_b);
$environment = \Drupal::service('twig');
$cache_prefixes[] = \Drupal::state()->get(TwigEnvironment::CACHE_PREFIX_METADATA_KEY)['twig_cache_prefix'];
$cache_filenames[] = $environment->getCache()->generateKey($template_name, $environment->getTemplateClass($template_name));
// Request 3 handled by webserver A.
\Drupal::setContainer($container_a);
$container = \Drupal::getContainer();
// Emulate twig service reconstruct on new request.
$container->set('twig', NULL);
$environment = $container->get('twig');
$cache_prefixes[] = \Drupal::state()->get(TwigEnvironment::CACHE_PREFIX_METADATA_KEY)['twig_cache_prefix'];
$cache_filenames[] = $environment->getCache()->generateKey($template_name, $environment->getTemplateClass($template_name));
// Request 4 handled by webserver B.
\Drupal::setContainer($container_b);
$container = \Drupal::getContainer();
// Emulate twig service reconstruct on new request.
$container->set('twig', NULL);
$environment = $container->get('twig');
$cache_prefixes[] = \Drupal::state()->get(TwigEnvironment::CACHE_PREFIX_METADATA_KEY)['twig_cache_prefix'];
$cache_filenames[] = $environment->getCache()->generateKey($template_name, $environment->getTemplateClass($template_name));
// The cache prefix should not have been changed, as this is stored in
// state and thus shared between all (web)servers.
$this->assertEquals(count(array_unique($cache_prefixes)), 1);
// This also applies to twig's file cache resulting in an unlimited growth
// of the cache storage directory.
$this->assertEquals(count(array_unique($cache_filenames)), 1);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment