.../Core/Render/MainContent/HtmlRenderer.php | 4 +++ .../src/EventSubscriber/SmartCacheSubscriber.php | 32 ++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php index 4b5f0a5..6092a28 100644 --- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php @@ -142,6 +142,10 @@ public function renderResponse(array $main_content, Request $request, RouteMatch // @see \Drupal\Core\Render\HtmlResponseAttachmentsProcessor. $render_context = new RenderContext(); $this->renderer->executeInRenderContext($render_context, function() use (&$html) { + // RendererInterface::render() renders the $html render array, it updates + // $html by reference. We don't care about the return value (which is just + // $html['#markup']), but about the resulting render array. + // @todo Simplify this when https://www.drupal.org/node/2495001 lands. $this->renderer->render($html); }); if (!$render_context->isEmpty()) { diff --git a/core/modules/smart_cache/src/EventSubscriber/SmartCacheSubscriber.php b/core/modules/smart_cache/src/EventSubscriber/SmartCacheSubscriber.php index 4b825a0..2e58521 100644 --- a/core/modules/smart_cache/src/EventSubscriber/SmartCacheSubscriber.php +++ b/core/modules/smart_cache/src/EventSubscriber/SmartCacheSubscriber.php @@ -23,11 +23,32 @@ /** * Uses the SmartCache as early as possible, to avoid as much work as possible. * + * SmartCache is able to cache so much because it exploits cache contexts: the + * cache contexts that are present capture the variations of every component of + * the page. That, combined with the fact that cacheability metadata is bubbled, + * means that the cache contexts at the page level represent the complete set of + * contexts that the page varies by. + * + * The reason SmartCache is implemented as two event subscribers (a late REQUEST + * subscriber immediately after routing for cache hits, and an early RESPONSE + * subscriber for cache misses) is because many cache contexts can only be + * evaluated after routing. (Examples: 'user', 'user.permissions', 'route' …) + * Consequently, it is impossible to implement SmartCache as a kernel middleware + * that simply caches per URL. + * * @see \Drupal\Core\Render\MainContent\HtmlRenderer */ class SmartCacheSubscriber implements EventSubscriberInterface { /** + * Attribute name of the Smart Cache request policy result. + * + * @see onRouteMatch() + * @see onRespond() + */ + const SMART_CACHE_REQUEST_POLICY_RESULT = '_smart_cache_request_policy_result'; + + /** * The cache contexts manager. * * @var \Drupal\Core\Cache\Context\CacheContextsManager @@ -92,8 +113,12 @@ public function __construct(CacheContextsManager $cache_contexts_manager, CacheB */ public function onRouteMatch(GetResponseEvent $event) { // Don't cache the HTML response if the SmartCache request policies are not - // met. - if ($this->requestPolicy->check($event->getRequest()) === RequestPolicyInterface::DENY) { + // met. Store the result in a request attribute, so that onResponse() does + // not have to redo the request policy check. + $request = $event->getRequest(); + $request_policy_result = $this->requestPolicy->check($request); + $event->getRequest()->attributes->set(self::SMART_CACHE_REQUEST_POLICY_RESULT, $request_policy_result); + if ($request_policy_result === RequestPolicyInterface::DENY) { return; } @@ -137,8 +162,9 @@ public function onResponse(FilterResponseEvent $event) { // Don't cache the HTML response if the SmartCache request & response // policies are not met. + // @see onRouteMatch() $request = $event->getRequest(); - if ($this->requestPolicy->check($request) === RequestPolicyInterface::DENY || $this->responsePolicy->check($response, $request) === ResponsePolicyInterface::DENY) { + if ($request->attributes->get(self::SMART_CACHE_REQUEST_POLICY_RESULT) === RequestPolicyInterface::DENY || $this->responsePolicy->check($response, $request) === ResponsePolicyInterface::DENY) { return; }