diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 1616f18..e2991e3 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -1940,6 +1940,23 @@ function install_check_translations($langcode, $server_pattern) { } } + // Installations on Windows can run into limitations with MAX_PATH if the + // Drupal root directory is too deep in the filesystem. Generally this shows + // up in cached Twig templates and other public files with long directory or + // file names. There is no definite root directory depth below which Drupal is + // guaranteed to function correctly on Windows, but we start warning when + // there are more than 100 characters in the Drupal root path. + if (substr(PHP_OS, 0, 3) == 'WIN') { + $depth = strlen(realpath(DRUPAL_ROOT)); + if ($depth > 100) { + $requirements['max_path_on_windows'] = [ + 'title' => t('Windows installation'), + 'description' => t('The path length to the Drupal root directory (@depth characters) is greater than 100 characters. This may cause problems when running on a Windows server. It is recommended to install Drupal on Windows in a shorter path.', ['@depth' => $depth]), + 'severity' => REQUIREMENT_WARNING, + ]; + } + } + return $requirements; } diff --git a/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php index b9f7cae..4f1a2ce 100644 --- a/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php +++ b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php @@ -4,6 +4,7 @@ use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\PhpStorage\PhpStorageFactory; +use Drupal\Component\Utility\Crypt; /** * Provides an alternate cache storage for Twig using PhpStorage. @@ -18,6 +19,11 @@ class TwigPhpStorageCache implements \Twig_CacheInterface { /** + * The maximum length for each part of the cache key suffix. + */ + const SUFFIX_SUBSTRING_LENGTH = 25; + + /** * The cache object used for auto-refresh via mtime. * * @var \Drupal\Core\Cache\CacheBackendInterface @@ -67,8 +73,6 @@ protected function storage() { * {@inheritdoc} */ public function generateKey($name, $className) { - $hash = hash('sha256', $className); - if (strpos($name, '{# inline_template_start #}') === 0) { // $name is an inline template, and can have characters that are not valid // for a filename. $hash is unique for each inline template so we just use @@ -79,8 +83,20 @@ public function generateKey($name, $className) { $name = basename($name); } - // The first part is what is invalidated. - return $this->templateCacheFilenamePrefix . '_' . $name . '_' . $hash; + // If the suffix is longer that 51 characters then we truncate it so the + // length of the filename can't grow beyond that. Windows only supports 255 + // characters in a path. If the files directory is in the usual place of + // 'sites/default/files' then the maximum relative path of a twig file is + // 153 characters. We use 138 of that as follows: + // - 30 (/sites/default/files/php/twig/) + // - 9 (prefix + _) + // - 51 (suffix) + // - 48 (/ + hmac_hash + .php) + $suffix = substr($name, 0, self::SUFFIX_SUBSTRING_LENGTH) . '_'; + $suffix .= substr(Crypt::hashBase64($className), 0, self::SUFFIX_SUBSTRING_LENGTH); + + // The cache prefix is what gets invalidated. + return $this->templateCacheFilenamePrefix . '_' . $suffix; } /** diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php index 59a49a4..d849106 100644 --- a/core/modules/system/system.post_update.php +++ b/core/modules/system/system.post_update.php @@ -57,3 +57,11 @@ function system_post_update_add_region_to_entity_displays() { array_map($entity_save, EntityViewDisplay::loadMultiple()); array_map($entity_save, EntityFormDisplay::loadMultiple()); } + + +/** + * Force Twig php file cache to be cleared. + */ +function system_post_update_clear_twig_cache() { + // Empty post-update hook. +} diff --git a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php index deaf033..ae2aec0 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php @@ -4,7 +4,9 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Site\Settings; +use Drupal\Core\Template\TwigPhpStorageCache; use Drupal\KernelTests\KernelTestBase; +use Drupal\Component\Utility\Crypt; /** * Tests the twig environment. @@ -87,7 +89,7 @@ public function testInlineTemplate() { $cache = $environment->getCache(); $class = $environment->getTemplateClass($name); - $expected = $hash . '_inline-template' . '_' . hash('sha256', $class); + $expected = $hash . '_inline-template_' . substr(Crypt::hashBase64($class), 0, TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH); $this->assertEqual($expected, $cache->generateKey($name, $class)); } @@ -116,6 +118,19 @@ public function testCacheFilename() { // static cache. $environment = \Drupal::service('twig'); + // A template basename greater than the constant + // TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH should get truncated. + $cache = $environment->getCache(); + $long_name = 'core/modules/system/templates/block--system-messages-block.html.twig'; + $this->assertGreaterThan(TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH, strlen(basename($long_name))); + $class = $environment->getTemplateClass($long_name); + $key = $cache->generateKey($long_name, $class); + $hash = $this->container->getParameter('twig_extension_hash'); + // The key should consist of the hash, two underscores, and two strings + // truncated to length TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH + $expected = strlen($hash) + 2 + 2 * TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH; + $this->assertEquals($expected, strlen($key)); + $original_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig'); \Drupal::getContainer()->set('twig', NULL);