diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index 7d67ff9..9a4fb29 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -280,6 +280,10 @@ public function getLink($text, $url, $attributes = []) { if (!$url instanceof Url) { $url = Url::fromUri($url); } + // The twig extension should not modify the original url object, this + // ensures consistent rendering. + // @see https://www.drupal.org/node/2842399 + $url = clone $url; if ($attributes) { if ($attributes instanceof Attribute) { $attributes = $attributes->toArray(); diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php index 0ec5280..ed022a6 100644 --- a/core/lib/Drupal/Core/Utility/LinkGenerator.php +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php @@ -76,6 +76,11 @@ public function generateFromLink(Link $link) { * @see system_page_attachments() */ public function generate($text, Url $url) { + // The link generator should not modify the original url object, this + // ensures consistent rendering. + // @see https://www.drupal.org/node/2842399 + $url = clone $url; + // Performance: avoid Url::toString() needing to retrieve the URL generator // service from the container. $url->setUrlGenerator($this->urlGenerator); diff --git a/core/modules/system/src/Tests/Theme/EngineTwigTest.php b/core/modules/system/src/Tests/Theme/EngineTwigTest.php index 51d092d..09dec39 100644 --- a/core/modules/system/src/Tests/Theme/EngineTwigTest.php +++ b/core/modules/system/src/Tests/Theme/EngineTwigTest.php @@ -79,14 +79,16 @@ public function testTwigLinkGenerator() { $generated_url = Url::fromRoute('user.register', [], ['absolute' => TRUE])->toString(TRUE)->getGeneratedUrl(); $expected = [ - 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['absolute' => TRUE])), - 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['absolute' => TRUE, 'attributes' => ['foo' => 'bar']])), - 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['foo' => 'bar', 'id' => 'kitten']])), - 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['id' => 'kitten']])), - 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['class' => ['llama', 'kitten', 'panda']]])), - 'link via the linkgenerator: ' . $link_generator->generate(Markup::create('register'), new Url('user.register', [], ['absolute' => TRUE])), - 'link via the linkgenerator: register', +// 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['absolute' => TRUE])), +// 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['absolute' => TRUE, 'attributes' => ['foo' => 'bar']])), +// 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['foo' => 'bar', 'id' => 'kitten']])), +// 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['id' => 'kitten']])), +// 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['class' => ['llama', 'kitten', 'panda']]])), +// 'link via the linkgenerator: ' . $link_generator->generate(Markup::create('register'), new Url('user.register', [], ['absolute' => TRUE])), +// 'link via the linkgenerator: register', + 'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['foo' => 'bar']])) . ' ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['foo' => 'bar']])), ]; + debug($expected); // Verify that link() has the ability to bubble cacheability metadata: // absolute URLs should bubble the 'url.site' cache context. (This only diff --git a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.link_generator.html.twig b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.link_generator.html.twig index a836aec..1b20b46 100644 --- a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.link_generator.html.twig +++ b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.link_generator.html.twig @@ -7,3 +7,4 @@
link via the linkgenerator: {{ link(title, test_url) }}
{% set title %}register{% endset %}
link via the linkgenerator: {{ link(title, test_url) }}
+
link via the linkgenerator: {{ link('register', test_url_attribute) }} {{ link('register', test_url_attribute) }}
diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php index f4dc2df..c3af2db 100644 --- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php @@ -538,6 +538,25 @@ public function testGenerateWithAlterHook() { } /** + * Tests whether rendering the same link twice works. + * + * This is a regression test for https://www.drupal.org/node/2842399. + */ + public function testGenerateTwice() { + $this->urlGenerator->expects($this->any()) + ->method('generateFromRoute') + ->will($this->returnValue((new GeneratedUrl())->setGeneratedUrl('/'))); + + $url = Url::fromRoute('', [], ['attributes' => ['class' => ['foo', 'bar']]]); + $url->setUrlGenerator($this->urlGenerator); + + $link = Link::fromTextAndUrl('text', $url); + $link->setLinkGenerator($this->linkGenerator); + $output = $link->toString() . $link->toString(); + $this->assertEquals('texttext', $output); + } + + /** * Checks that a link with certain properties exists in a given HTML snippet. * * @param array $properties