.../EarlyRenderingControllerWrapperSubscriber.php | 3 +- .../Tests/Common/EarlyRenderingControllerTest.php | 72 +++++++++++++++ .../early_rendering_controller_test.info.yml | 6 ++ .../early_rendering_controller_test.routing.yml | 55 ++++++++++++ .../src/AttachmentsTestResponse.php | 19 ++++ .../src/CacheableTestResponse.php | 18 ++++ .../src/EarlyRenderingTestController.php | 100 +++++++++++++++++++++ 7 files changed, 272 insertions(+), 1 deletion(-) diff --git a/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php index ad2737e..8abb662 100644 --- a/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php @@ -8,6 +8,7 @@ namespace Drupal\Core\EventSubscriber; use Drupal\Core\Cache\CacheableDependencyInterface; +use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Render\AttachmentsInterface; use Drupal\Core\Render\BubbleableMetadata; @@ -142,7 +143,7 @@ protected function wrapControllerExcecutionInRenderContext($controller, array $a // attachments or cacheability, then throw an exception: early rendering // is not permitted in that case. It is the developer's responsibility // to not use early rendering. - elseif ($response instanceof AttachmentsInterface || $response instanceof CacheableDependencyInterface) { + elseif ($response instanceof AttachmentsInterface || $response instanceof CacheableResponseInterface) { throw new \LogicException(sprintf('Early rendering is not permitted for controllers returning anything else than render arrays. Response class: %s.', get_class($response))); } else { diff --git a/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php b/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php new file mode 100644 index 0000000..d9d11d7 --- /dev/null +++ b/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php @@ -0,0 +1,72 @@ +drupalGet(Url::fromRoute('early_rendering_controller_test.render_array')); + $this->assertResponse(200, 'Hello world!'); + $this->assertCacheTag('foo'); + $this->drupalGet(Url::fromRoute('early_rendering_controller_test.render_array.early')); + $this->assertResponse(200, 'Hello world!'); + $this->assertCacheTag('foo'); + + // Basic Response object: non-early & early. + $this->drupalGet(Url::fromRoute('early_rendering_controller_test.response')); + $this->assertResponse(200, 'Hello world!'); + $this->assertNoCacheTag('foo'); + $this->drupalGet(Url::fromRoute('early_rendering_controller_test.response.early')); + $this->assertResponse(200, 'Hello world!'); + $this->assertNoCacheTag('foo'); + + // Response object with attachments: non-early & early. + $this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments')); + $this->assertResponse(200, 'Hello world!'); + $this->assertNoCacheTag('foo'); + $this->config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save(); + $this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments.early')); + $this->assertResponse(500, 'Early rendering is not permitted for controllers returning anything else than render arrays. Response class: Drupal\early_rendering_controller_test\AttachmentsTestResponse.'); + + // Cacheable Response object: non-early & early. + $this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response')); + $this->assertResponse(200, 'Hello world!'); + $this->assertNoCacheTag('foo'); + $this->config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save(); + $this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response.early')); + $this->assertResponse(500, 'Early rendering is not permitted for controllers returning anything else than render arrays. Response class: Drupal\early_rendering_controller_test\AttachmentsTestResponse.'); + + // The exceptions are expected. Do not interpret them as a test failure. + // Not using File API; a potential error must trigger a PHP warning. + unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log'); + } + +} diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.info.yml b/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.info.yml new file mode 100644 index 0000000..44ab452 --- /dev/null +++ b/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.info.yml @@ -0,0 +1,6 @@ +name: 'Early rendering controller test' +type: module +description: 'Support module for EarlyRenderingControllerTest.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.routing.yml b/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.routing.yml new file mode 100644 index 0000000..98d70c3 --- /dev/null +++ b/core/modules/system/tests/modules/early_rendering_controller_test/early_rendering_controller_test.routing.yml @@ -0,0 +1,55 @@ +# Controller returning a render array. +early_rendering_controller_test.render_array: + path: '/early-rendering-controller-test/render-array' + defaults: + _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::renderArray' + requirements: + _access: 'TRUE' +early_rendering_controller_test.render_array.early: + path: '/early-rendering-controller-test/render-array/early' + defaults: + _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::renderArrayEarly' + requirements: + _access: 'TRUE' + +# Controller returning a basic Response object. +early_rendering_controller_test.response: + path: '/early-rendering-controller-test/response' + defaults: + _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::response' + requirements: + _access: 'TRUE' +early_rendering_controller_test.response.early: + path: '/early-rendering-controller-test/response/early' + defaults: + _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::responseEarly' + requirements: + _access: 'TRUE' + +# Controller returning a Response object with attachments. +early_rendering_controller_test.response-with-attachments: + path: '/early-rendering-controller-test/response-with-attachments' + defaults: + _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::responseWithAttachments' + requirements: + _access: 'TRUE' +early_rendering_controller_test.response-with-attachments.early: + path: '/early-rendering-controller-test/response-with-attachments/early' + defaults: + _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::responseWithAttachmentsEarly' + requirements: + _access: 'TRUE' + +# Controller returning a cacheable Response object. +early_rendering_controller_test.cacheable-response: + path: '/early-rendering-controller-test/cacheable-response' + defaults: + _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::cacheableResponse' + requirements: + _access: 'TRUE' +early_rendering_controller_test.cacheable-response.early: + path: '/early-rendering-controller-test/cacheable-response/early' + defaults: + _controller: '\Drupal\early_rendering_controller_test\EarlyRenderingTestController::cacheableResponseEarly' + requirements: + _access: 'TRUE' diff --git a/core/modules/system/tests/modules/early_rendering_controller_test/src/AttachmentsTestResponse.php b/core/modules/system/tests/modules/early_rendering_controller_test/src/AttachmentsTestResponse.php new file mode 100644 index 0000000..f8d040e --- /dev/null +++ b/core/modules/system/tests/modules/early_rendering_controller_test/src/AttachmentsTestResponse.php @@ -0,0 +1,19 @@ +renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('renderer') + ); + } + + protected function earlyRenderContent() { + return [ + '#markup' => 'Hello world!', + '#cache' => [ + 'tags' => [ + 'foo', + ], + ], + ]; + } + + public function renderArray() { + return [ + '#pre_render' => [function () { + $elements = $this->earlyRenderContent(); + return $elements; + }], + ]; + } + + public function renderArrayEarly() { + $render_array = $this->earlyRenderContent(); + return [ + '#markup' => $this->renderer->render($render_array), + ]; + } + + public function response() { + return new Response('Hello world!'); + } + + public function responseEarly() { + return new Response($this->renderer->render($render_array)); + } + + public function responseWithAttachments() { + return new AttachmentsTestResponse('Hello world!'); + } + + public function responseWithAttachmentsEarly() { + $render_array = $this->earlyRenderContent(); + return new AttachmentsTestResponse($this->renderer->render($render_array)); + } + + public function cacheableResponse() { + return new CacheableTestResponse('Hello world!'); + } + + public function cacheableResponseEarly() { + $render_array = $this->earlyRenderContent(); + return new CacheableTestResponse($this->renderer->render($render_array)); + } + +}