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

Issue #2572605 by Gábor Hojtsy, slasher13, Jo Fitzgerald, alexpott, andypost,...

Issue #2572605 by Gábor Hojtsy, slasher13, Jo Fitzgerald, alexpott, andypost, Cottser, skyredwang, mfernea, joelpittet, jibran, larowlan, aleevas, Wim Leers, lauriii, catch, Berdir: Make Drupal 8 optionally working with Twig 2, while keeping its dependency on Twig 1
parent 812c0bbf
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
......@@ -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.
*
......
......@@ -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));
}
}
......@@ -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}
......
......@@ -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');
......
......@@ -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 %}<div{{ create_attribute(iteration) }}></div>{% 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 %}<div{{ create_attribute(iteration) }}></div>{% endfor %}", ['iterations' => $iterations]);
$result = $twig->render($name, ['iterations' => $iterations]);
$expected = '<div class="kittens" data-toggle="modal" data-lang="es"></div><div id="puppies" data-value="foo" data-lang="en"></div><div></div>';
$this->assertEquals($expected, $result);
// Test default creation of empty attribute object and using its method.
$result = $twig->render("<div{{ create_attribute().addClass('meow') }}></div>");
$name = '__string_template_test_2__';
$loader = new \Twig_Loader_Array([$name => "<div{{ create_attribute().addClass('meow') }}></div>"]);
$twig->setLoader($loader);
$result = $twig->render($name);
$expected = '<div class="meow"></div>';
$this->assertEquals($expected, $result);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment