core/core.services.yml | 2 +- .../Render/MainContent/SmartCacheHtmlRenderer.php | 15 ++++++++- .../src/Tests/Cache/SmartCacheIntegrationTest.php | 39 ++++++++++++++++------ 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/core/core.services.yml b/core/core.services.yml index c0cf5a5..d6c3e17 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -896,7 +896,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', '@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'] + arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@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', %renderer.config%] tags: - { name: render.main_content_renderer, format: html } main_content_renderer.ajax: diff --git a/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php index 590fca7..985e9bd 100644 --- a/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/SmartCacheHtmlRenderer.php @@ -75,6 +75,13 @@ class SmartCacheHtmlRenderer extends HtmlRenderer { protected $requestStack; /** + * The renderer configuration array. + * + * @var array + */ + protected $rendererConfig; + + /** * Constructs a new SmartCacheHtmlRenderer. * * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver @@ -103,8 +110,10 @@ class SmartCacheHtmlRenderer extends HtmlRenderer { * The Smart Cache #type => html render array cache bin. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. + * @param array $renderer_config + * The renderer configuration array. */ - public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, 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) { + public function __construct(TitleResolverInterface $title_resolver, PluginManagerInterface $display_variant_manager, EventDispatcherInterface $event_dispatcher, 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, array $renderer_config) { parent::__construct($title_resolver, $display_variant_manager, $event_dispatcher, $module_handler, $renderer, $render_cache); $this->cacheContextsManager = $cache_contexts_manager; $this->requestPolicy = $request_policy; @@ -113,6 +122,7 @@ public function __construct(TitleResolverInterface $title_resolver, PluginManage $this->smartContextsCache = $contexts_cache; $this->smartHtmlCache = $html_cache; $this->requestStack = $request_stack; + $this->rendererConfig = $renderer_config; } /** @@ -171,7 +181,10 @@ protected function finish(array $html) { // Get the cacheability metadata. $html_cacheability = CacheableMetadata::createFromRenderArray($cacheable_html) + // SmartCache caches per route. ->addCacheContexts(['route']) + // SmartCache also respects the Renderer's required cache contexts. + ->addCacheContexts($this->rendererConfig['required_cache_contexts']) ->addCacheTags(['rendered']); // Retain page titles defined in the main content render array. diff --git a/core/modules/system/src/Tests/Cache/SmartCacheIntegrationTest.php b/core/modules/system/src/Tests/Cache/SmartCacheIntegrationTest.php index 34c28f0..ded4167 100644 --- a/core/modules/system/src/Tests/Cache/SmartCacheIntegrationTest.php +++ b/core/modules/system/src/Tests/Cache/SmartCacheIntegrationTest.php @@ -7,7 +7,9 @@ namespace Drupal\system\Tests\Cache; +use Drupal\Core\Cache\Cache; use Drupal\Core\EventSubscriber\MainContentViewSubscriber; +use Drupal\Core\Session\AnonymousUserSession; use Drupal\Core\Url; use Drupal\simpletest\WebTestBase; use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait; @@ -56,7 +58,7 @@ public function testSmartCache() { $url = Url::fromUri('route:smart_cache_test.html'); $this->drupalGet($url); $this->assertEqual('MISS', $this->drupalGetHeader('X-Drupal-SmartCache'), 'Render array returned, rendered as HTML response: SmartCache is active, SmartCache MISS.'); - $this->assertSmartCache($url, [], []); + $this->assertSmartCache($url, []); $this->drupalGet($url); $this->assertEqual('HIT', $this->drupalGetHeader('X-Drupal-SmartCache'), 'Render array returned, rendered as HTML response: SmartCache is active, SmartCache HIT.'); @@ -68,7 +70,7 @@ public function testSmartCache() { $url = Url::fromUri('route:smart_cache_test.html.with_cache_contexts', ['query' => ['animal' => $animal]]); $this->drupalGet($url); $this->assertEqual('MISS', $this->drupalGetHeader('X-Drupal-SmartCache'), 'Render array returned, rendered as HTML response: SmartCache is active, SmartCache MISS.'); - $this->assertSmartCache($url, ['url.query_args:animal'], [$animal]); + $this->assertSmartCache($url, ['url.query_args:animal' => $animal]); $this->drupalGet($url); $this->assertEqual('HIT', $this->drupalGetHeader('X-Drupal-SmartCache'), 'Render array returned, rendered as HTML response: SmartCache is active, SmartCache HIT.'); @@ -105,21 +107,38 @@ public function testSmartCache() { * * @param \Drupal\Core\Url $url * The URL to test. - * @param string[] $expected_cache_contexts - * The expected cache contexts for the given URL. - * @param string[] $cid_parts_for_cache_contexts - * The CID parts corresponding to the values in $expected_cache_contexts. + * @param string[] $final_cache_contexts + * Assocative array, with the keys being the expected cache contexts for the + * given URL and the values being the corresponding cache keys. + * (Excluding the required cache contexts (%renderer.config%) and the + * 'route' cache context, all of which are added by SmartCache automatically + * and are asserted here too.) */ - protected function assertSmartCache(Url $url, array $expected_cache_contexts, array $cid_parts_for_cache_contexts) { + protected function assertSmartCache(Url $url, array $final_cache_contexts) { + // The complete cache context to key mapping consists of: + // - the expected cache contexts as passed in; + $cache_context_to_key_mapping = $final_cache_contexts; + // - the required cache contexts; + $cache_context_to_key_mapping['languages:language_interface'] = 'en'; + $cache_context_to_key_mapping['theme'] = 'classy'; + $cache_context_to_key_mapping['user.permissions'] = 'ph.' . \Drupal::service('user_permissions_hash_generator')->generate(new AnonymousUserSession()); + // - the 'route' cache context added by SmartCache. + $cache_context_to_key_mapping['route'] = $url->getRouteName() . hash('sha256', serialize($url->getRouteParameters())); + + // The final list of cache contexts that should be present. + $final_cache_contexts = Cache::mergeContexts(array_keys($cache_context_to_key_mapping)); + // Assert SmartCache contexts item. $cid_parts = ['smartcache', 'contexts', $url->getRouteName() . hash('sha256', serialize($url->getRouteParameters()))]; $cid = implode(':', $cid_parts); $cache_item = \Drupal::cache('smart_cache_contexts')->get($cid); - $this->assertEqual($expected_cache_contexts, array_values(array_diff($cache_item->data, ['route']))); + $this->assertEqual($final_cache_contexts, array_values($cache_item->data)); // Assert SmartCache html render array item. - $cid_parts = ['smartcache', 'html_render_array', $url->getRouteName() . hash('sha256', serialize($url->getRouteParameters()))]; - $cid_parts = array_merge($cid_parts, $cid_parts_for_cache_contexts); + $cid_parts = ['smartcache', 'html_render_array']; + foreach ($final_cache_contexts as $cache_context_token) { + $cid_parts[] = $cache_context_to_key_mapping[$cache_context_token]; + } $cid = implode(':', $cid_parts); $cache_item = \Drupal::cache('smart_cache_html')->get($cid); $this->assertEqual(['#markup', '#attached', '#cache'], array_keys($cache_item->data));