core/core.services.yml | 21 +++++++++- .../PageCache/ResponsePolicy/NoAdminRoutes.php | 49 ++++++++++++++++++++++ .../Render/MainContent/SmartCacheHtmlRenderer.php | 40 +++++++++++++----- .../Core/SmartCache/DefaultRequestPolicy.php | 31 ++++++++++++++ 4 files changed, 129 insertions(+), 12 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index ac85799..8edce6b 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -214,17 +214,26 @@ services: class: Drupal\Core\PageCache\ResponsePolicy\KillSwitch tags: - { name: page_cache_response_policy } + - { name: smart_cache_response_policy } page_cache_no_cache_routes: class: Drupal\Core\PageCache\ResponsePolicy\DenyNoCacheRoutes arguments: ['@current_route_match'] public: false tags: - { name: page_cache_response_policy } + - { name: smart_cache_response_policy } page_cache_no_server_error: class: Drupal\Core\PageCache\ResponsePolicy\NoServerError public: false tags: - { name: page_cache_response_policy } + - { name: smart_cache_response_policy } + smart_cache_no_admin_routes: + class: Drupal\Core\PageCache\ResponsePolicy\NoAdminRoutes + arguments: ['@current_route_match'] + public: false + tags: + - { name: smart_cache_response_policy } config.manager: class: Drupal\Core\Config\ConfigManager arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage', '@event_dispatcher'] @@ -862,7 +871,7 @@ services: - { name: event_subscriber } main_content_renderer.html: class: Drupal\Core\Render\MainContent\SmartCacheHtmlRenderer - arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@element_info', '@module_handler', '@renderer', '@render_cache', '@cache_contexts_manager', '@current_route_match', '@cache.smart_cache_contexts', '@cache.smart_cache_html', '@request_stack'] + arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@element_info', '@module_handler', '@renderer', '@render_cache', '@cache_contexts_manager', '@smart_cache_request_policy', '@smart_cache_response_policy', '@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: @@ -896,6 +905,16 @@ services: factory_method: get factory_service: cache_factory arguments: [smart_cache_html] + smart_cache_request_policy: + class: Drupal\Core\SmartCache\DefaultRequestPolicy + tags: + - { name: service_collector, tag: smart_cache_request_policy, call: addPolicy} + lazy: true + smart_cache_response_policy: + class: Drupal\Core\PageCache\ChainResponsePolicy + tags: + - { name: service_collector, tag: smart_cache_response_policy, call: addPolicy} + lazy: true smart_cache_subscriber: class: Drupal\Core\EventSubscriber\SmartCacheSubscriber arguments: ['@current_route_match', '@cache_contexts_manager', '@cache.smart_cache_contexts', '@cache.smart_cache_html'] diff --git a/core/lib/Drupal/Core/PageCache/ResponsePolicy/NoAdminRoutes.php b/core/lib/Drupal/Core/PageCache/ResponsePolicy/NoAdminRoutes.php new file mode 100644 index 0000000..97cec61 --- /dev/null +++ b/core/lib/Drupal/Core/PageCache/ResponsePolicy/NoAdminRoutes.php @@ -0,0 +1,49 @@ +routeMatch = $route_match; + } + + /** + * {@inheritdoc} + */ + public function check(Response $response, Request $request) { + if (($route = $this->routeMatch->getRouteObject()) && $route->getOption('_admin_route')) { + return static::DENY; + } + } + +} + diff --git a/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php index 4839a59..c1cc3e0 100644 --- a/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php @@ -13,6 +13,8 @@ use Drupal\Core\Cache\CacheContextsManager; use Drupal\Core\Controller\TitleResolverInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\PageCache\RequestPolicyInterface; +use Drupal\Core\PageCache\ResponsePolicyInterface; use Drupal\Core\Render\Element; use Drupal\Core\Render\ElementInfoManagerInterface; use Drupal\Core\Render\RenderCacheInterface; @@ -21,6 +23,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; /** * SmartCache main content renderer for HTML requests. @@ -28,6 +31,20 @@ class SmartCacheHtmlRenderer extends HtmlRenderer { /** + * A policy rule determining the cacheability of a request. + * + * @var \Drupal\Core\PageCache\RequestPolicyInterface + */ + protected $requestPolicy; + + /** + * A policy rule determining the cacheability of the response. + * + * @var \Drupal\Core\PageCache\ResponsePolicyInterface + */ + protected $responsePolicy; + + /** * The Smart Cache contexts cache bin. * * @var \Drupal\Core\Cache\CacheBackendInterface @@ -67,6 +84,10 @@ class SmartCacheHtmlRenderer extends HtmlRenderer { * The render cache service. * @param \Drupal\Core\Cache\CacheContextsManager $cache_contexts_manager * The cache contexts service. + * @param \Drupal\Core\PageCache\RequestPolicyInterface $request_policy + * A policy rule determining the cacheability of a request. + * @param \Drupal\Core\PageCache\ResponsePolicyInterface $response_policy + * A policy rule determining the cacheability of the response. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The current route match. * @param \Drupal\Core\Cache\CacheBackendInterface $contexts_cache @@ -76,8 +97,10 @@ class SmartCacheHtmlRenderer extends HtmlRenderer { * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. */ - public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ElementInfoManagerInterface $element_info_manager, ModuleHandlerInterface $module_handler, RendererInterface $renderer, RenderCacheInterface $render_cache, CacheContextsManager $cache_contexts_manager, RouteMatchInterface $route_match, CacheBackendInterface $contexts_cache, CacheBackendInterface $html_cache, RequestStack $request_stack) { + public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, ElementInfoManagerInterface $element_info_manager, ModuleHandlerInterface $module_handler, RendererInterface $renderer, RenderCacheInterface $render_cache, CacheContextsManager $cache_contexts_manager, RequestPolicyInterface $request_policy, ResponsePolicyInterface $response_policy, RouteMatchInterface $route_match, CacheBackendInterface $contexts_cache, CacheBackendInterface $html_cache, RequestStack $request_stack) { parent::__construct($title_resolver, $display_variant_manager, $event_dispatcher, $element_info_manager, $module_handler, $renderer, $render_cache, $cache_contexts_manager); + $this->requestPolicy = $request_policy; + $this->responsePolicy = $response_policy; $this->routeMatch = $route_match; $this->smartContextsCache = $contexts_cache; $this->smartHtmlCache = $html_cache; @@ -96,16 +119,11 @@ protected function finish(array $html) { return parent::finish($html); } - // Don't cache routes that have marked themselves as uncacheable. - // @see \Drupal\Core\PageCache\ResponsePolicy\DenyNoCacheRoutes - if ($this->routeMatch->getRouteObject()->getOption('no_cache')) { - return parent::finish($html); - } - - // @todo For now, SmartCache doesn't handle admin routes. It may be too much - // work to add the necessary cacheability metadata to all admin routes - // before 8.0.0, but that can happen in 8.1.0 without a BC break. - if ($this->routeMatch->getRouteObject()->getOption('_admin_route')) { + // Don't cache the render array if the associated response will not meet the + // SmartCache request & response policies. + $response = new Response(); + $request = $this->requestStack->getCurrentRequest(); + if ($this->requestPolicy->check($request) === RequestPolicyInterface::DENY || $this->responsePolicy->check($response, $request) === ResponsePolicyInterface::DENY) { return parent::finish($html); } diff --git a/core/lib/Drupal/Core/SmartCache/DefaultRequestPolicy.php b/core/lib/Drupal/Core/SmartCache/DefaultRequestPolicy.php new file mode 100644 index 0000000..7a684dc --- /dev/null +++ b/core/lib/Drupal/Core/SmartCache/DefaultRequestPolicy.php @@ -0,0 +1,31 @@ +addPolicy(new CommandLineOrUnsafeMethod()); + } + +}