diff --git a/core/core.services.yml b/core/core.services.yml index 796ece3..f35c607 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -840,7 +840,7 @@ services: - [setContext, ['@?router.request_context']] url_generator: class: Drupal\Core\Render\MetadataBubblingUrlGenerator - arguments: ['@url_generator.non_bubbling', '@renderer'] + arguments: ['@url_generator.non_bubbling', '@renderer', '@request_stack'] calls: - [setContext, ['@?router.request_context']] redirect.destination: diff --git a/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php b/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php index 6a5527b..d1327d1 100644 --- a/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php +++ b/core/lib/Drupal/Core/Render/MetadataBubblingUrlGenerator.php @@ -4,6 +4,7 @@ use Drupal\Core\GeneratedUrl; use Drupal\Core\Routing\UrlGeneratorInterface; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\RequestContext as SymfonyRequestContext; /** @@ -35,16 +36,27 @@ class MetadataBubblingUrlGenerator implements UrlGeneratorInterface { protected $renderer; /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + + + /** * Constructs a new bubbling URL generator service. * * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator * The non-bubbling URL generator. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer. + * @param \Symfony\Component\HttpFoundation\RequestStack + * The request stack. */ - public function __construct(UrlGeneratorInterface $url_generator, RendererInterface $renderer) { + public function __construct(UrlGeneratorInterface $url_generator, RendererInterface $renderer, RequestStack $request_stack) { $this->urlGenerator = $url_generator; $this->renderer = $renderer; + $this->requestStack = $request_stack; } /** @@ -80,8 +92,10 @@ protected function bubble(GeneratedUrl $generated_url, array $options = []) { // Bubbling metadata makes sense only if the code is executed inside a // render context. All code running outside controllers has no render // context by default, so URLs used there are not supposed to affect the - // response cacheability. - if ($this->renderer->hasRenderContext()) { + // response cacheability. Only do this for GET/HEAD requests since render + // caching does not apply to other responses. + $request = $this->requestStack->getCurrentRequest(); + if ($request->isMethodCacheable() && $this->renderer->hasRenderContext()) { $build = []; $generated_url->applyTo($build); $this->renderer->render($build); diff --git a/core/tests/Drupal/Tests/Core/Render/MetadataBubblingUrlGeneratorTest.php b/core/tests/Drupal/Tests/Core/Render/MetadataBubblingUrlGeneratorTest.php index 9524b0d9..2cbc13c 100644 --- a/core/tests/Drupal/Tests/Core/Render/MetadataBubblingUrlGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Render/MetadataBubblingUrlGeneratorTest.php @@ -5,6 +5,7 @@ use Drupal\Core\Render\MetadataBubblingUrlGenerator; use Drupal\Core\Url; use Drupal\Tests\Core\Routing\UrlGeneratorTest; +use Symfony\Component\HttpFoundation\Request; /** * Confirm that the MetadataBubblingUrlGenerator is functioning properly. @@ -33,7 +34,7 @@ protected function setUp() { ->method('hasRenderContext') ->willReturn(TRUE); - $this->generator = new MetadataBubblingUrlGenerator($this->generator, $this->renderer); + $this->generator = new MetadataBubblingUrlGenerator($this->generator, $this->renderer, $this->requestStack); } /** @@ -50,9 +51,13 @@ protected function setUp() { * * @dataProvider providerUrlBubbleableMetadataBubbling */ - public function testUrlBubbleableMetadataBubbling($collect_bubbleable_metadata, $invocations, array $options) { + public function testUrlBubbleableMetadataBubbling($collect_bubbleable_metadata, $invocations, $method, array $options) { $self = $this; + $request = Request::create('some/path'); + $request->setMethod($method); + $this->requestStack->push($request); + $this->renderer->expects($this->exactly($invocations)) ->method('render') ->willReturnCallback(function ($build) use ($self) { @@ -69,10 +74,14 @@ public function testUrlBubbleableMetadataBubbling($collect_bubbleable_metadata, */ public function providerUrlBubbleableMetadataBubbling() { return [ - // No bubbling when bubbleable metadata is collected. - [TRUE, 0, []], - // Bubbling when bubbleable metadata is not collected. - [FALSE, 1, []], + // No bubbling when bubbleable metadata is collected and safe method + [TRUE, 0, 'GET', []], + // Bubbling when bubbleable metadata is not collected and safe method + [FALSE, 1, 'GET', []], + // No bubbling when bubbleable metadata is collected and not safe method + [TRUE, 0, 'POST', []], + // No bubbling when bubbleable metadata is not collected and not safe method + [FALSE, 0, 'POST', []], ]; }