.../Core/Render/MainContent/HtmlRenderer.php | 3 --
.../EventSubscriber/DynamicPageCacheSubscriber.php | 45 ++++++++++++++++++++++
2 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
index b6eaf59..4a4ab59 100644
--- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
@@ -223,9 +223,6 @@ protected function prepare(array $main_content, Request $request, RouteMatchInte
// @todo Remove this once https://www.drupal.org/node/2359901 lands.
if (!empty($main_content)) {
$this->renderer->executeInRenderContext(new RenderContext(), function() use (&$main_content) {
- if (!isset($main_content['#cache'])) {
- throw new \LogicException('Must have cacheability metadata.');
- }
if (isset($main_content['#cache']['keys'])) {
// Retain #title, otherwise, dynamically generated titles would be
// missing for controllers whose entire returned render array is
diff --git a/core/modules/dynamic_page_cache/src/EventSubscriber/DynamicPageCacheSubscriber.php b/core/modules/dynamic_page_cache/src/EventSubscriber/DynamicPageCacheSubscriber.php
index 103b303..792d0cd 100644
--- a/core/modules/dynamic_page_cache/src/EventSubscriber/DynamicPageCacheSubscriber.php
+++ b/core/modules/dynamic_page_cache/src/EventSubscriber/DynamicPageCacheSubscriber.php
@@ -9,7 +9,9 @@
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Cache\CacheableResponseInterface;
+use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\PageCache\RequestPolicyInterface;
use Drupal\Core\PageCache\ResponsePolicyInterface;
use Drupal\Core\Render\Element;
@@ -17,6 +19,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
@@ -149,6 +152,44 @@ public function onRouteMatch(GetResponseEvent $event) {
}
/**
+ * Complain if a controller returns a render array without #cache.
+ *
+ * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event
+ * The event to process.
+ *
+ * @throws \LogicException
+ */
+ public function onViewRenderArray(GetResponseForControllerResultEvent $event) {
+ $request = $event->getRequest();
+ $result = $event->getControllerResult();
+
+ // Don't complain if Dynamic Page Cache's request subscriber did not fire,
+ // because that means it is impossible to have a Dynamic Page Cache hit,
+ // and hence complaining is unnecessary.
+ // @see ::onResponse()
+ if (!$request->attributes->has(self::ATTRIBUTE_REQUEST_POLICY_RESULT)) {
+ return;
+ }
+
+ // Don't complain if the Dynamic Page Cache request & response policies are
+ // not met, because that means Dynamic Page Cache won't cache this response
+ // and hence complaining is unnecessary.
+ // @see onRouteMatch()
+ if ($request->attributes->get(self::ATTRIBUTE_REQUEST_POLICY_RESULT) === RequestPolicyInterface::DENY || $this->responsePolicy->check(new CacheableResponse(), $request) === ResponsePolicyInterface::DENY) {
+ return;
+ }
+
+ // Check if MainContentViewSubscriber would render this render array, i.e.
+ // if this render array will in fact be converted into a response.
+ if (is_array($result) && ($request->query->has(MainContentViewSubscriber::WRAPPER_FORMAT) || $request->getRequestFormat() == 'html')) {
+ // If #cache is not set, complain.
+ if (!isset($result['#cache'])) {
+ throw new \LogicException('Controllers must provide cacheability metadata.');
+ }
+ }
+ }
+
+ /**
* Stores a response in case of a Dynamic Page Cache miss, if cacheable.
*
* @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
@@ -323,6 +364,10 @@ public static function getSubscribedEvents() {
// Run before HtmlResponseSubscriber::onRespond(), which has priority 0.
$events[KernelEvents::RESPONSE][] = ['onResponse', 100];
+ // Run before MainContentViewSubscriber::onViewRenderArray(), which has
+ // priority 0.
+ $events[KernelEvents::VIEW][] = ['onViewRenderArray', 100];
+
return $events;
}