diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php index 5449cde3da..99546ef1be 100644 --- a/core/lib/Drupal/Core/Template/Attribute.php +++ b/core/lib/Drupal/Core/Template/Attribute.php @@ -261,6 +261,20 @@ public function removeClass() { return $this; } + /** + * Gets the class attribute value if set. + * + * This method is implemented to take precedence over hasClass() for Twig 2.0. + * + * @return \Drupal\Core\Template\AttributeValueBase + * The class attribute value if set. + * + * @see twig_get_attribute() + */ + public function getClass() { + return $this->offsetGet('class'); + } + /** * Checks if the class array has the given CSS class. * diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php index 59f2005490..426234459b 100644 --- a/core/lib/Drupal/Core/Template/TwigEnvironment.php +++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php @@ -36,6 +36,11 @@ class TwigEnvironment extends \Twig_Environment { */ protected $templateClasses; + /** + * The template cache filename prefix. + * + * @var string + */ protected $twigCachePrefix = ''; /** @@ -55,7 +60,7 @@ class TwigEnvironment extends \Twig_Environment { * @param array $options * 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, array $options = []) { $this->state = $state; // Ensure that twig.engine is loaded, given that it is needed to render a @@ -73,11 +78,6 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension ]; // Ensure autoescaping is always on. $options['autoescape'] = 'html'; - - $policy = new TwigSandboxPolicy(); - $sandbox = new \Twig_Extension_Sandbox($policy, TRUE); - $this->addExtension($sandbox); - if ($options['cache'] === TRUE) { $current = $state->get(static::CACHE_PREFIX_METADATA_KEY, ['twig_extension_hash' => '']); if ($current['twig_extension_hash'] !== $twig_extension_hash || empty($current['twig_cache_prefix'])) { @@ -94,8 +94,11 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension $options['cache'] = new TwigPhpStorageCache($cache, $this->twigCachePrefix); } - $this->loader = $loader; - parent::__construct($this->loader, $options); + $this->setLoader($loader); + parent::__construct($this->getLoader(), $options); + $policy = new TwigSandboxPolicy(); + $sandbox = new \Twig_Extension_Sandbox($policy, TRUE); + $this->addExtension($sandbox); } /** @@ -106,7 +109,6 @@ public function __construct($root, CacheBackendInterface $cache, $twig_extension public function invalidate() { PhpStorageFactory::get('twig')->deleteAll(); $this->templateClasses = []; - $this->loadedTemplates = []; $this->state->delete(static::CACHE_PREFIX_METADATA_KEY); } @@ -138,7 +140,7 @@ public function getTemplateClass($name, $index = NULL) { // node.html.twig for the output of each node and the same compiled class. $cache_index = $name . (NULL === $index ? '' : '_' . $index); if (!isset($this->templateClasses[$cache_index])) { - $this->templateClasses[$cache_index] = $this->templateClassPrefix . hash('sha256', $this->loader->getCacheKey($name)) . (NULL === $index ? '' : '_' . $index); + $this->templateClasses[$cache_index] = parent::getTemplateClass($name, $index); } return $this->templateClasses[$cache_index]; } @@ -168,7 +170,7 @@ public function getTemplateClass($name, $index = NULL) { public function renderInline($template_string, array $context = []) { // Prefix all inline templates with a special comment. $template_string = '{# inline_template_start #}' . $template_string; - return Markup::create($this->loadTemplate($template_string, NULL)->render($context)); + return Markup::create($this->createTemplate($template_string)->render($context)); } } diff --git a/core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php b/core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php index 7937daa14d..bd23809e26 100644 --- a/core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php +++ b/core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php @@ -2,10 +2,21 @@ namespace Drupal\twig_loader_test\Loader; +use Twig\Source; + /** * A test Twig loader. */ -class TestLoader implements \Twig_LoaderInterface, \Twig_ExistsLoaderInterface { +class TestLoader implements \Twig_LoaderInterface, \Twig_ExistsLoaderInterface, \Twig_SourceContextLoaderInterface { + + /** + * {@inheritdoc} + */ + public function getSourceContext($name) { + $name = (string) $name; + $value = $this->getSource($name); + return new Source($value, $name); + } /** * {@inheritdoc} diff --git a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php index 1aaf40c4e0..bbc1253a3f 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php @@ -216,7 +216,7 @@ public function testTemplateInvalidation() { // 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); + $reflection = new \ReflectionClass(\Twig_Environment::class); $property_reflection = $reflection->getProperty('templateClassPrefix'); $property_reflection->setAccessible(TRUE); $property_reflection->setValue($environment, 'otherPrefix'); diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php index 6cfb562d25..f255e4c907 100644 --- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php +++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php @@ -331,7 +331,8 @@ public function testRenderVarWithGeneratedLink() { * @covers ::createAttribute */ public function testCreateAttribute() { - $loader = new StringLoader(); + $name = '__string_template_test_1__'; + $loader = new \Twig_Loader_Array([$name => "{% for iteration in iterations %}{% endfor %}"]); $twig = new \Twig_Environment($loader); $twig->addExtension($this->systemUnderTest); @@ -340,12 +341,15 @@ public function testCreateAttribute() { ['id' => 'puppies', 'data-value' => 'foo', 'data-lang' => 'en'], [], ]; - $result = $twig->render("{% for iteration in iterations %}{% endfor %}", ['iterations' => $iterations]); + $result = $twig->render($name, ['iterations' => $iterations]); $expected = '
'; $this->assertEquals($expected, $result); // Test default creation of empty attribute object and using its method. - $result = $twig->render(""); + $name = '__string_template_test_2__'; + $loader = new \Twig_Loader_Array([$name => ""]); + $twig->setLoader($loader); + $result = $twig->render($name); $expected = '
'; $this->assertEquals($expected, $result); }