core/core.services.yml | 2 +- .../Core/EventSubscriber/SmartCacheSubscriber.php | 5 +++ .../Render/MainContent/SmartCacheHtmlRenderer.php | 39 ++++++++++++++++------ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index 09ab1c2..e00b79b 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -856,7 +856,7 @@ services: - { name: event_subscriber } main_content_renderer.html: class: Drupal\Core\Render\MainContent\SmartCacheHtmlRenderer - arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@cache_contexts', '@current_route_match', '@cache.smart_cache_contexts', '@cache.smart_cache_html'] + arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@cache_contexts', '@current_route_match', '@cache.smart_cache_contexts', '@cache.smart_cache_html', '@request_stack'] tags: - { name: render.main_content_renderer, format: html } main_content_renderer.ajax: diff --git a/core/lib/Drupal/Core/EventSubscriber/SmartCacheSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/SmartCacheSubscriber.php index 9fb0d66..0dc4fc0 100644 --- a/core/lib/Drupal/Core/EventSubscriber/SmartCacheSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/SmartCacheSubscriber.php @@ -76,6 +76,11 @@ public function __construct(RouteMatchInterface $route_match, CacheContexts $cac * The event to process. */ public function onRouteMatch(GetResponseEvent $event) { + // SmartCache only supports master requests that are safe and ask for HTML. + if (!$event->isMasterRequest() || !$event->getRequest()->isMethodSafe() || $event->getRequest()->getRequestFormat() !== 'html') { + return; + } + $this->routeMatch->getRouteName(); // Smart cache caches per route. diff --git a/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php index bccd9f6..aab13c6 100644 --- a/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php @@ -18,6 +18,7 @@ use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\RequestStack; /** * SmartCache main content renderer for HTML requests. @@ -46,6 +47,13 @@ class SmartCacheHtmlRenderer extends HtmlRenderer { protected $smartHtmlCache; /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + + /** * Constructs a new SmartCacheHtmlRenderer. * * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver @@ -66,12 +74,15 @@ class SmartCacheHtmlRenderer extends HtmlRenderer { * The Smart Cache contexts cache bin. * @param \Drupal\Core\Cache\CacheBackendInterface $html_cache * The Smart Cache #type => html render array cache bin. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, RendererInterface $renderer, CacheContexts $cache_contexts, RouteMatchInterface $route_match, CacheBackendInterface $contexts_cache, CacheBackendInterface $html_cache) { + public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ModuleHandlerInterface $module_handler, RendererInterface $renderer, CacheContexts $cache_contexts, RouteMatchInterface $route_match, CacheBackendInterface $contexts_cache, CacheBackendInterface $html_cache, RequestStack $request_stack) { parent::__construct($title_resolver, $display_variant_manager, $event_dispatcher, $module_handler, $renderer, $cache_contexts); $this->routeMatch = $route_match; $this->smartContextsCache = $contexts_cache; $this->smartHtmlCache = $html_cache; + $this->requestStack = $request_stack; } /** @@ -102,11 +113,13 @@ protected function finish(array $html) { // contexts, if any), and the total set of cache tags (to associate with the // smart_cache_html cache item). $cache_contexts = []; + $html_cache_max_age = Cache::PERMANENT; $html_cache_tags = []; foreach (Element::children($cacheable_html) as $child) { $cacheable_html[$child] = $this->renderer->getCacheableRenderArray($cacheable_html[$child]); $cache_contexts = Cache::mergeContexts($cache_contexts, $cacheable_html[$child]['#cache']['contexts']); $html_cache_tags = Cache::mergeTags($html_cache_tags, $cacheable_html[$child]['#cache']['tags']); + $html_cache_max_age = Cache::mergeMaxAges($html_cache_max_age, $cacheable_html[$child]['#cache']['max-age']); } // @todo Remove this. Work-around to support the deep-render-array-scanning- @@ -117,18 +130,22 @@ protected function finish(array $html) { $cacheable_html['page'][$page_region] = ['#preprocess_functions_messing_with_cacheability' => TRUE]; } - // If the set of cache contexts is different, store the union of the already - // stored cache contexts and the contexts for this request. - if ($cache_contexts !== $stored_cache_contexts) { - if (is_array($stored_cache_contexts)) { - $cache_contexts = Cache::mergeContexts($cache_contexts, $stored_cache_contexts); + // SmartCache only caches cacheable pages. + if ($html_cache_max_age !== 0) { + // If the set of cache contexts is different, store the union of the already + // stored cache contexts and the contexts for this request. + if ($cache_contexts !== $stored_cache_contexts) { + if (is_array($stored_cache_contexts)) { + $cache_contexts = Cache::mergeContexts($cache_contexts, $stored_cache_contexts); + } + $this->smartContextsCache->set($contexts_cid, $cache_contexts); } - $this->smartContextsCache->set($contexts_cid, $cache_contexts); - } - // Finally, cache the html render array according to those contexts. - $cid = 'smartcache:html_render_array:' . $cid_part_route_match . implode(':', $this->cacheContexts->convertTokensToKeys($cache_contexts)); - $this->smartHtmlCache->set($cid, $cacheable_html, Cache::PERMANENT, $html_cache_tags); + // Finally, cache the html render array according to those contexts. + $cid = 'smartcache:html_render_array:' . $cid_part_route_match . implode(':', $this->cacheContexts->convertTokensToKeys($cache_contexts)); + $expire = ($html_cache_max_age === Cache::PERMANENT) ? Cache::PERMANENT : (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME') + $html_cache_max_age; + $this->smartHtmlCache->set($cid, $cacheable_html, $expire, $html_cache_tags); + } return parent::finish($cacheable_html); }