diff --git a/core/core.services.yml b/core/core.services.yml index cb935793a6b0b17d9e7bcc90b970d4b006036b0d..5e6e2d9a80a9c36a3b82ae2f86e014b618c96f10 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1553,13 +1553,9 @@ services: - { name: service_collector, tag: 'twig.extension', call: addExtension } twig.extension: class: Drupal\Core\Template\TwigExtension - arguments: ['@renderer'] + arguments: ['@renderer', '@url_generator', '@theme.manager', '@date.formatter'] tags: - { name: twig.extension, priority: 100 } - calls: - - [setUrlGenerator, ['@url_generator']] - - [setThemeManager, ['@theme.manager']] - - [setDateFormatter, ['@date.formatter']] # @todo Figure out what to do about debugging functions. # @see https://www.drupal.org/node/1804998 twig.extension.debug: diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index 3fcbe2c86f37bde6687a0e1dfc838927b48e3093..7d67ff991c4cb6779ab7067dec151ffc95af4fa8 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -58,9 +58,18 @@ class TwigExtension extends \Twig_Extension { * * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer. + * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator + * The URL generator. + * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager + * The theme manager. + * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter + * The date formatter. */ - public function __construct(RendererInterface $renderer) { + public function __construct(RendererInterface $renderer, UrlGeneratorInterface $url_generator, ThemeManagerInterface $theme_manager, DateFormatterInterface $date_formatter) { $this->renderer = $renderer; + $this->urlGenerator = $url_generator; + $this->themeManager = $theme_manager; + $this->dateFormatter = $date_formatter; } /** @@ -72,7 +81,6 @@ public function __construct(RendererInterface $renderer) { * @return $this * * @deprecated in Drupal 8.0.x-dev, will be removed before Drupal 9.0.0. - * Use \Drupal\Core\Template\TwigExtension::setUrlGenerator(). */ public function setGenerators(UrlGeneratorInterface $url_generator) { return $this->setUrlGenerator($url_generator); @@ -85,6 +93,8 @@ public function setGenerators(UrlGeneratorInterface $url_generator) { * The URL generator. * * @return $this + * + * @deprecated in Drupal 8.3.x-dev, will be removed before Drupal 9.0.0. */ public function setUrlGenerator(UrlGeneratorInterface $url_generator) { $this->urlGenerator = $url_generator; @@ -98,6 +108,8 @@ public function setUrlGenerator(UrlGeneratorInterface $url_generator) { * The theme manager. * * @return $this + * + * @deprecated in Drupal 8.3.x-dev, will be removed before Drupal 9.0.0. */ public function setThemeManager(ThemeManagerInterface $theme_manager) { $this->themeManager = $theme_manager; @@ -111,6 +123,8 @@ public function setThemeManager(ThemeManagerInterface $theme_manager) { * The date formatter. * * @return $this + * + * @deprecated in Drupal 8.3.x-dev, will be removed before Drupal 9.0.0. */ public function setDateFormatter(DateFormatterInterface $date_formatter) { $this->dateFormatter = $date_formatter; 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 4afa3a78cbdd83774eeefba530789640898c6779..78d10202882fa72606819c0b7d7e1730749a8f7e 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 @@ -2,12 +2,11 @@ namespace Drupal\twig_extension_test\TwigExtension; -use Drupal\Core\Template\TwigExtension; /** * 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. diff --git a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php index 38373aa0673979b7e745206e40646c3bc188e9b1..396b2f9765ff9ce4ed5e68f936b9959e08e1c3a8 100644 --- a/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php +++ b/core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php @@ -1,16 +1,9 @@ <?php -/** - * @file - * Contains \Drupal\Tests\Core\Template\TwigExtensionTest. - */ - namespace Drupal\Tests\Core\Template; use Drupal\Core\GeneratedLink; use Drupal\Core\Render\RenderableInterface; -use Drupal\Core\Render\RendererInterface; -use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Template\Loader\StringLoader; use Drupal\Core\Template\TwigEnvironment; @@ -26,26 +19,74 @@ */ class TwigExtensionTest extends UnitTestCase { + /** + * The renderer. + * + * @var \Drupal\Core\Render\RendererInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $renderer; + + /** + * The url generator. + * + * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $urlGenerator; + + /** + * The theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $themeManager; + + /** + * The date formatter. + * + * @var \Drupal\Core\Datetime\DateFormatterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $dateFormatter; + + /** + * The system under test. + * + * @var \Drupal\Core\Template\TwigExtension + */ + protected $systemUnderTest; + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + + $this->renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); + $this->urlGenerator = $this->getMock('\Drupal\Core\Routing\UrlGeneratorInterface'); + $this->themeManager = $this->getMock('\Drupal\Core\Theme\ThemeManagerInterface'); + $this->dateFormatter = $this->getMock('\Drupal\Core\Datetime\DateFormatterInterface'); + + $this->systemUnderTest = new TwigExtension($this->renderer, $this->urlGenerator, $this->themeManager, $this->dateFormatter); + } + /** * Tests the escaping * * @dataProvider providerTestEscaping */ public function testEscaping($template, $expected) { - $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); $twig = new \Twig_Environment(NULL, array( 'debug' => TRUE, 'cache' => FALSE, 'autoescape' => 'html', - 'optimizations' => 0 + 'optimizations' => 0, )); - $twig->addExtension((new TwigExtension($renderer))->setUrlGenerator($this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'))); + $twig->addExtension($this->systemUnderTest); $nodes = $twig->parse($twig->tokenize($template)); $this->assertSame($expected, $nodes->getNode('body') - ->getNode(0) - ->getNode('expr') instanceof \Twig_Node_Expression_Filter); + ->getNode(0) + ->getNode('expr') instanceof \Twig_Node_Expression_Filter); } /** @@ -83,25 +124,19 @@ public function providerTestEscaping() { * Tests the active_theme function. */ public function testActiveTheme() { - $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); - $extension = new TwigExtension($renderer); - $theme_manager = $this->getMock('\Drupal\Core\Theme\ThemeManagerInterface'); $active_theme = $this->getMockBuilder('\Drupal\Core\Theme\ActiveTheme') ->disableOriginalConstructor() ->getMock(); - $active_theme - ->expects($this->once()) + $active_theme->expects($this->once()) ->method('getName') ->willReturn('test_theme'); - $theme_manager - ->expects($this->once()) + $this->themeManager->expects($this->once()) ->method('getActiveTheme') ->willReturn($active_theme); - $extension->setThemeManager($theme_manager); $loader = new \Twig_Loader_String(); $twig = new \Twig_Environment($loader); - $twig->addExtension($extension); + $twig->addExtension($this->systemUnderTest); $result = $twig->render('{{ active_theme() }}'); $this->assertEquals('test_theme', $result); } @@ -110,30 +145,21 @@ public function testActiveTheme() { * Tests the format_date filter. */ public function testFormatDate() { - $date_formatter = $this->getMockBuilder('\Drupal\Core\Datetime\DateFormatter') - ->disableOriginalConstructor() - ->getMock(); - $date_formatter->expects($this->exactly(2)) + $this->dateFormatter->expects($this->exactly(2)) ->method('format') ->willReturn('1978-11-19'); - $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); - $extension = new TwigExtension($renderer); - $extension->setDateFormatter($date_formatter); $loader = new StringLoader(); $twig = new \Twig_Environment($loader); - $twig->addExtension($extension); + $twig->addExtension($this->systemUnderTest); $result = $twig->render('{{ time|format_date("html_date") }}'); - $this->assertEquals($date_formatter->format('html_date'), $result); + $this->assertEquals($this->dateFormatter->format('html_date'), $result); } /** * Tests the active_theme_path function. */ public function testActiveThemePath() { - $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); - $extension = new TwigExtension($renderer); - $theme_manager = $this->getMock('\Drupal\Core\Theme\ThemeManagerInterface'); $active_theme = $this->getMockBuilder('\Drupal\Core\Theme\ActiveTheme') ->disableOriginalConstructor() ->getMock(); @@ -141,15 +167,13 @@ public function testActiveThemePath() { ->expects($this->once()) ->method('getPath') ->willReturn('foo/bar'); - $theme_manager - ->expects($this->once()) + $this->themeManager->expects($this->once()) ->method('getActiveTheme') ->willReturn($active_theme); - $extension->setThemeManager($theme_manager); $loader = new \Twig_Loader_String(); $twig = new \Twig_Environment($loader); - $twig->addExtension($extension); + $twig->addExtension($this->systemUnderTest); $result = $twig->render('{{ active_theme_path() }}'); $this->assertEquals('foo/bar', $result); } @@ -160,34 +184,32 @@ public function testActiveThemePath() { * @covers ::escapeFilter */ public function testSafeStringEscaping() { - $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface'); $twig = new \Twig_Environment(NULL, array( 'debug' => TRUE, 'cache' => FALSE, 'autoescape' => 'html', - 'optimizations' => 0 + 'optimizations' => 0, )); - $twig_extension = new TwigExtension($renderer); // By default, TwigExtension will attempt to cast objects to strings. // Ensure objects that implement MarkupInterface are unchanged. $safe_string = $this->getMock('\Drupal\Component\Render\MarkupInterface'); - $this->assertSame($safe_string, $twig_extension->escapeFilter($twig, $safe_string, 'html', 'UTF-8', TRUE)); + $this->assertSame($safe_string, $this->systemUnderTest->escapeFilter($twig, $safe_string, 'html', 'UTF-8', TRUE)); // Ensure objects that do not implement MarkupInterface are escaped. $string_object = new TwigExtensionTestString("<script>alert('here');</script>"); - $this->assertSame('<script>alert('here');</script>', $twig_extension->escapeFilter($twig, $string_object, 'html', 'UTF-8', TRUE)); + $this->assertSame('<script>alert('here');</script>', $this->systemUnderTest->escapeFilter($twig, $string_object, 'html', 'UTF-8', TRUE)); } /** * @covers ::safeJoin */ public function testSafeJoin() { - $renderer = $this->prophesize(RendererInterface::class); - $renderer->render(['#markup' => '<strong>will be rendered</strong>', '#printed' => FALSE])->willReturn('<strong>will be rendered</strong>'); - $renderer = $renderer->reveal(); + $this->renderer->expects($this->any()) + ->method('render') + ->with(['#markup' => '<strong>will be rendered</strong>', '#printed' => FALSE]) + ->willReturn('<strong>will be rendered</strong>'); - $twig_extension = new TwigExtension($renderer); $twig_environment = $this->prophesize(TwigEnvironment::class)->reveal(); // Simulate t(). @@ -198,9 +220,9 @@ public function testSafeJoin() { $items = [ '<em>will be escaped</em>', $markup, - ['#markup' => '<strong>will be rendered</strong>'] + ['#markup' => '<strong>will be rendered</strong>'], ]; - $result = $twig_extension->safeJoin($twig_environment, $items, '<br/>'); + $result = $this->systemUnderTest->safeJoin($twig_environment, $items, '<br/>'); $this->assertEquals('<em>will be escaped</em><br/><em>will be markup</em><br/><strong>will be rendered</strong>', $result); // Ensure safe_join Twig filter supports Traversable variables. @@ -209,12 +231,12 @@ public function testSafeJoin() { $markup, ['#markup' => '<strong>will be rendered</strong>'], ]); - $result = $twig_extension->safeJoin($twig_environment, $items, ', '); + $result = $this->systemUnderTest->safeJoin($twig_environment, $items, ', '); $this->assertEquals('<em>will be escaped</em>, <em>will be markup</em>, <strong>will be rendered</strong>', $result); // Ensure safe_join Twig filter supports empty variables. $items = NULL; - $result = $twig_extension->safeJoin($twig_environment, $items, '<br>'); + $result = $this->systemUnderTest->safeJoin($twig_environment, $items, '<br>'); $this->assertEmpty($result); } @@ -222,13 +244,12 @@ public function testSafeJoin() { * @dataProvider providerTestRenderVar */ public function testRenderVar($result, $input) { - $renderer = $this->prophesize(RendererInterface::class); - $renderer->render($result += ['#printed' => FALSE])->willReturn('Rendered output'); - - $renderer = $renderer->reveal(); - $twig_extension = new TwigExtension($renderer); + $this->renderer->expects($this->any()) + ->method('render') + ->with($result += ['#printed' => FALSE]) + ->willReturn('Rendered output'); - $this->assertEquals('Rendered output', $twig_extension->renderVar($input)); + $this->assertEquals('Rendered output', $this->systemUnderTest->renderVar($input)); } public function providerTestRenderVar() { @@ -247,7 +268,6 @@ public function providerTestRenderVar() { * @covers ::bubbleArgMetadata */ public function testEscapeWithGeneratedLink() { - $renderer = $this->prophesize(RendererInterface::class); $twig = new \Twig_Environment(NULL, [ 'debug' => TRUE, 'cache' => FALSE, @@ -256,22 +276,23 @@ public function testEscapeWithGeneratedLink() { ] ); - $twig_extension = new TwigExtension($renderer->reveal()); - $twig->addExtension($twig_extension->setUrlGenerator($this->prophesize(UrlGeneratorInterface::class)->reveal())); + $twig->addExtension($this->systemUnderTest); $link = new GeneratedLink(); $link->setGeneratedLink('<a href="http://example.com"></a>'); $link->addCacheTags(['foo']); $link->addAttachments(['library' => ['system/base']]); - $result = $twig_extension->escapeFilter($twig, $link, 'html', NULL, TRUE); - $renderer->render([ - "#cache" => [ - "contexts" => [], - "tags" => ["foo"], - "max-age" => -1 - ], - "#attached" => ['library' => ['system/base']], - ])->shouldHaveBeenCalled(); + $this->renderer->expects($this->atLeastOnce()) + ->method('render') + ->with([ + "#cache" => [ + "contexts" => [], + "tags" => ["foo"], + "max-age" => -1, + ], + "#attached" => ['library' => ['system/base']], + ]); + $result = $this->systemUnderTest->escapeFilter($twig, $link, 'html', NULL, TRUE); $this->assertEquals('<a href="http://example.com"></a>', $result); } @@ -280,22 +301,22 @@ public function testEscapeWithGeneratedLink() { * @covers ::bubbleArgMetadata */ public function testRenderVarWithGeneratedLink() { - $renderer = $this->prophesize(RendererInterface::class); - $twig_extension = new TwigExtension($renderer->reveal()); $link = new GeneratedLink(); $link->setGeneratedLink('<a href="http://example.com"></a>'); $link->addCacheTags(['foo']); $link->addAttachments(['library' => ['system/base']]); - $result = $twig_extension->renderVar($link); - $renderer->render([ - "#cache" => [ - "contexts" => [], - "tags" => ["foo"], - "max-age" => -1 - ], - "#attached" => ['library' => ['system/base']], - ])->shouldHaveBeenCalled(); + $this->renderer->expects($this->atLeastOnce()) + ->method('render') + ->with([ + "#cache" => [ + "contexts" => [], + "tags" => ["foo"], + "max-age" => -1, + ], + "#attached" => ['library' => ['system/base']], + ]); + $result = $this->systemUnderTest->renderVar($link); $this->assertEquals('<a href="http://example.com"></a>', $result); } @@ -305,11 +326,9 @@ public function testRenderVarWithGeneratedLink() { * @covers ::createAttribute */ public function testCreateAttribute() { - $renderer = $this->prophesize(RendererInterface::class); - $extension = new TwigExtension($renderer->reveal()); $loader = new StringLoader(); $twig = new \Twig_Environment($loader); - $twig->addExtension($extension); + $twig->addExtension($this->systemUnderTest); $iterations = [ ['class' => ['kittens'], 'data-toggle' => 'modal', 'data-lang' => 'es'],