diff --git a/core/lib/Drupal/Core/Template/Loader/ThemeRegistryLoader.php b/core/lib/Drupal/Core/Template/Loader/ThemeRegistryLoader.php index fe1ae25..4d6ba8d 100644 --- a/core/lib/Drupal/Core/Template/Loader/ThemeRegistryLoader.php +++ b/core/lib/Drupal/Core/Template/Loader/ThemeRegistryLoader.php @@ -38,6 +38,8 @@ public function __construct(Registry $theme_registry) { * * @param string $name * The name of the template to load. + * @param bool $throw + * Whether to throw an exception when an error occurs. * * @return string * The path to the template. @@ -45,7 +47,7 @@ public function __construct(Registry $theme_registry) { * @throws \Twig_Error_Loader * Thrown if a template matching $name cannot be found. */ - protected function findTemplate($name) { + protected function findTemplate($name, $throw = TRUE) { // Allow for loading based on the Drupal theme registry. $hook = str_replace('.html.twig', '', strtr($name, '-', '_')); $theme_registry = $this->themeRegistry->getRuntime(); @@ -63,7 +65,9 @@ protected function findTemplate($name) { } } - throw new \Twig_Error_Loader(sprintf('Unable to find template "%s" in the Drupal theme registry.', $name)); + if ($throw) { + throw new \Twig_Error_Loader(sprintf('Unable to find template "%s" in the Drupal theme registry.', $name)); + } } } diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php index 2deec5f..a7bd648 100644 --- a/core/lib/Drupal/Core/Template/TwigEnvironment.php +++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php @@ -28,6 +28,8 @@ class TwigEnvironment extends \Twig_Environment { */ protected $templateClasses; + protected $templateClassPrefix = '__TwigTemplate_'; + /** * Constructs a TwigEnvironment object and stores cache and storage * internally. @@ -44,10 +46,6 @@ class TwigEnvironment extends \Twig_Environment { * The options for the Twig environment. */ public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, \Twig_LoaderInterface $loader = NULL, $options = array()) { - // Ensure that twig.engine is loaded, given that it is needed to render a - // template because functions like TwigExtension::escapeFilter() are called. - require_once $root . '/core/themes/engines/twig/twig.engine'; - $this->templateClasses = array(); $options += array( diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index 735a434..5cd3626 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -145,7 +145,7 @@ public function getFilters() { new \Twig_SimpleFilter('safe_join', [$this, 'safeJoin'], ['needs_environment' => true, 'is_safe' => ['html']]), // Array filters. - new \Twig_SimpleFilter('without', 'twig_without'), + new \Twig_SimpleFilter('without', [$this, 'withoutFilter']), // CSS class and ID filters. new \Twig_SimpleFilter('clean_class', '\Drupal\Component\Utility\Html::getClass'), @@ -513,4 +513,37 @@ public function safeJoin(\Twig_Environment $env, $value, $glue = '') { }, $value)); } + /** + * Removes child elements from a copy of the original array. + * + * Creates a copy of the renderable array and removes child elements by key + * specified through filter's arguments. The copy can be printed without these + * elements. The original renderable array is still available and can be used + * to print child elements in their entirety in the twig template. + * + * @param array|object $element + * The parent renderable array to exclude the child items. + * @param string[] $args, ... + * The string keys of $element to prevent printing. + * + * @return array + * The filtered renderable array. + */ + public function withoutFilter($element) { + if ($element instanceof \ArrayAccess) { + $filtered_element = clone $element; + } + else { + $filtered_element = $element; + } + $args = func_get_args(); + unset($args[0]); + foreach ($args as $arg) { + if (isset($filtered_element[$arg])) { + unset($filtered_element[$arg]); + } + } + return $filtered_element; + } + } diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php index a7b723a..fe2753a 100644 --- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php +++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php @@ -25,7 +25,7 @@ class TwigNodeTrans extends \Twig_Node { /** * {@inheritdoc} */ - public function __construct(\Twig_NodeInterface $body, \Twig_NodeInterface $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) { + public function __construct(\Twig_Node $body, \Twig_Node $plural = NULL, \Twig_Node_Expression $count = NULL, \Twig_Node_Expression $options = NULL, $lineno, $tag = NULL) { parent::__construct(array( 'count' => $count, 'body' => $body, @@ -91,7 +91,7 @@ public function compile(\Twig_Compiler $compiler) { /** * Extracts the text and tokens for the "trans" tag. * - * @param \Twig_NodeInterface $body + * @param \Twig_Node $body * The node to compile. * * @return array @@ -101,7 +101,7 @@ public function compile(\Twig_Compiler $compiler) { * - array $tokens * The extracted tokens as new \Twig_Node_Expression_Name instances. */ - protected function compileString(\Twig_NodeInterface $body) { + protected function compileString(\Twig_Node $body) { if ($body instanceof \Twig_Node_Expression_Name || $body instanceof \Twig_Node_Expression_Constant || $body instanceof \Twig_Node_Expression_TempName) { return array($body, array()); } diff --git a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php index ac1e676..860cc56 100644 --- a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php +++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php @@ -16,19 +16,19 @@ * * @see twig_render */ -class TwigNodeVisitor implements \Twig_NodeVisitorInterface { +class TwigNodeVisitor extends \Twig_BaseNodeVisitor { /** * {@inheritdoc} */ - public function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) { + protected function doEnterNode(\Twig_Node $node, \Twig_Environment $env) { return $node; } /** * {@inheritdoc} */ - public function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) { + protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) { // We use this to inject a call to render_var -> TwigExtension->renderVar() // before anything is printed. if ($node instanceof \Twig_Node_Print) { diff --git a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php index c67f463..009cc9c 100644 --- a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php +++ b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php @@ -82,14 +82,14 @@ public function getTag() { /** * Ensure that any nodes that are parsed are only of allowed types. * - * @param \Twig_NodeInterface $body + * @param \Twig_Node $body * The expression to check. * @param integer $lineno * The source line. * * @throws \Twig_Error_Syntax */ - protected function checkTransString(\Twig_NodeInterface $body, $lineno) { + protected function checkTransString(\Twig_Node $body, $lineno) { foreach ($body as $node) { if ( $node instanceof \Twig_Node_Text diff --git a/core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php b/core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php index 1bab9bf..2683264 100644 --- a/core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php +++ b/core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php @@ -91,13 +91,18 @@ public function testCacheFilename() { // Note: Later we refetch the twig service in order to bypass its internal // static cache. $environment = \Drupal::service('twig'); + $template_path = 'core/modules/system/templates/container.html.twig'; - $original_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig'); + $cache = $environment->getCache(); + $class = $environment->getTemplateClass($template_path); + $original_filename = $cache->generateKey($template_path, $class); \Drupal::getContainer()->set('twig', NULL); \Drupal::service('module_installer')->install(['twig_extension_test']); $environment = \Drupal::service('twig'); - $new_extension_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig'); + $cache = $environment->getCache(); + $class = $environment->getTemplateClass($template_path); + $new_extension_filename = $cache->generateKey($template_path, $class); \Drupal::getContainer()->set('twig', NULL); $this->assertNotEqual($new_extension_filename, $original_filename); diff --git a/core/modules/system/src/Tests/Theme/TwigSettingsTest.php b/core/modules/system/src/Tests/Theme/TwigSettingsTest.php index f581fa62..74a517b 100644 --- a/core/modules/system/src/Tests/Theme/TwigSettingsTest.php +++ b/core/modules/system/src/Tests/Theme/TwigSettingsTest.php @@ -101,7 +101,11 @@ function testTwigCacheOverride() { // theme_test.template_test.html.twig. $info = $templates->get('theme_test_template_test'); $template_filename = $info['path'] . '/' . $info['template'] . $extension; - $cache_filename = $this->container->get('twig')->getCacheFilename($template_filename); + + $environment = $this->container->get('twig'); + $cache = $environment->getCache(); + $class = $environment->getTemplateClass($template_filename); + $cache_filename = $cache->generateKey($template_filename, $class); // Navigate to the page and make sure the template gets cached. $this->drupalGet('theme-test/template-test'); diff --git a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php index 3c2b851..c4c6025 100644 --- a/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php +++ b/core/modules/system/tests/modules/twig_extension_test/src/TwigExtension/TestExtension.php @@ -12,7 +12,7 @@ /** * A test Twig extension that adds a custom function and a custom filter. */ -class TestExtension extends TwigExtension { +class TestExtension extends \Twig_Extension { /** * Generates a list of all Twig functions that this extension defines. @@ -27,9 +27,9 @@ class TestExtension extends TwigExtension { * The value is a standard PHP callback that defines what the function does. */ public function getFunctions() { - return array( - 'testfunc' => new \Twig_Function_Function(array('Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFunction')), - ); + return [ + new \Twig_SimpleFunction('testfunc', [$this, 'testFunction']), + ]; } /** @@ -45,9 +45,9 @@ public function getFunctions() { * The value is a standard PHP callback that defines what the filter does. */ public function getFilters() { - return array( - 'testfilter' => new \Twig_Filter_Function(array('Drupal\twig_extension_test\TwigExtension\TestExtension', 'testFilter')), - ); + return [ + new \Twig_SimpleFilter('testfilter', [$this, 'testFilter']), + ]; } /** diff --git a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml index 491d1e8..8784c0f 100644 --- a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml +++ b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.services.yml @@ -1,6 +1,5 @@ services: twig_extension_test.twig.test_extension: - arguments: ['@renderer'] class: Drupal\twig_extension_test\TwigExtension\TestExtension tags: - { name: twig.extension } diff --git a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php index b4a192d..7060401 100644 --- a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php +++ b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php @@ -10,6 +10,7 @@ use Drupal\Core\Template\Attribute; use Drupal\Core\Template\AttributeArray; use Drupal\Core\Template\AttributeString; +use Drupal\Core\Template\Loader\StringLoader; use Drupal\Tests\UnitTestCase; /** @@ -253,7 +254,7 @@ public function testChainAddRemoveClasses() { * @covers ::addClass */ public function testTwigAddRemoveClasses($template, $expected, $seed_attributes = array()) { - $loader = new \Twig_Loader_String(); + $loader = new StringLoader(); $twig = new \Twig_Environment($loader); $data = array('attributes' => new Attribute($seed_attributes)); $result = $twig->render($template, $data); diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php index 3e78e91..0f68047 100644 --- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php +++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php @@ -5,13 +5,14 @@ * Contains \Drupal\Tests\Core\Template\TwigExtensionTest. */ -namespace Drupal\Tests\Core\Template; +namespace Drupal\Tests\Core\Template { use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Render\RenderableInterface; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Template\TwigEnvironment; use Drupal\Core\Template\TwigExtension; +use Drupal\Core\Template\Loader\StringLoader; use Drupal\Tests\UnitTestCase; /** @@ -30,7 +31,8 @@ class TwigExtensionTest extends UnitTestCase { */ public function testEscaping($template, $expected) { $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); - $twig = new \Twig_Environment(NULL, array( + $loader = new \Twig_Loader_Filesystem(); + $twig = new \Twig_Environment($loader, array( 'debug' => TRUE, 'cache' => FALSE, 'autoescape' => 'html', @@ -96,7 +98,7 @@ public function testActiveTheme() { ->willReturn($active_theme); $extension->setThemeManager($theme_manager); - $loader = new \Twig_Loader_String(); + $loader = new StringLoader(); $twig = new \Twig_Environment($loader); $twig->addExtension($extension); $result = $twig->render('{{ active_theme() }}'); @@ -110,7 +112,8 @@ public function testActiveTheme() { */ public function testSafeStringEscaping() { $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); - $twig = new \Twig_Environment(NULL, array( + $loader = new \Twig_Loader_Filesystem(); + $twig = new \Twig_Environment($loader, array( 'debug' => TRUE, 'cache' => FALSE, 'autoescape' => 'html', @@ -192,3 +195,16 @@ public function __toString() { } } + +} + +namespace { + if (!function_exists('t')) { + function t($string, array $args = []) { + return strtr($string, $args); + } + } + if (!function_exists('file_create_url')) { + function file_create_url() {} + } +} diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine index 977f094..70a8012 100644 --- a/core/themes/engines/twig/twig.engine +++ b/core/themes/engines/twig/twig.engine @@ -118,36 +118,3 @@ function twig_render_template($template_file, array $variables) { // This output has already been rendered and is therefore considered safe. return SafeString::create(implode('', $output)); } - -/** - * Removes child elements from a copy of the original array. - * - * Creates a copy of the renderable array and removes child elements by key - * specified through filter's arguments. The copy can be printed without these - * elements. The original renderable array is still available and can be used - * to print child elements in their entirety in the twig template. - * - * @param array|object $element - * The parent renderable array to exclude the child items. - * @param string[] $args, ... - * The string keys of $element to prevent printing. - * - * @return array - * The filtered renderable array. - */ -function twig_without($element) { - if ($element instanceof ArrayAccess) { - $filtered_element = clone $element; - } - else { - $filtered_element = $element; - } - $args = func_get_args(); - unset($args[0]); - foreach ($args as $arg) { - if (isset($filtered_element[$arg])) { - unset($filtered_element[$arg]); - } - } - return $filtered_element; -}