Commit 62c31b56 authored by alexpott's avatar alexpott

Issue #2379741 by Wim Leers, damiankloip: Add...

Issue #2379741 by Wim Leers, damiankloip: Add Renderer::getCacheableRenderArray() to encapsulate which data is needed for caching a render array and have views use it
parent c84e90c7
...@@ -1153,4 +1153,4 @@ services: ...@@ -1153,4 +1153,4 @@ services:
- { name: mime_type_guesser } - { name: mime_type_guesser }
renderer: renderer:
class: Drupal\Core\Render\Renderer class: Drupal\Core\Render\Renderer
arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info'] arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@request_stack', '@cache_factory', '@cache_contexts']
...@@ -2265,79 +2265,6 @@ function show(&$element) { ...@@ -2265,79 +2265,6 @@ function show(&$element) {
return $element; return $element;
} }
/**
* Gets the cached, prerendered element of a renderable element from the cache.
*
* @param array $elements
* A renderable array.
*
* @return array
* A renderable array, with the original element and all its children pre-
* rendered, or FALSE if no cached copy of the element is available.
*
* @see drupal_render()
* @see drupal_render_cache_set()
*/
function drupal_render_cache_get(array $elements) {
if (!\Drupal::request()->isMethodSafe() || !$cid = drupal_render_cid_create($elements)) {
return FALSE;
}
$bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
if (!empty($cid) && $cache = \Drupal::cache($bin)->get($cid)) {
$cached_element = $cache->data;
// Return the cached element.
return $cached_element;
}
return FALSE;
}
/**
* Caches the rendered output of a renderable element.
*
* This is called by drupal_render() if the #cache property is set on an
* element.
*
* @param $markup
* The rendered output string of $elements.
* @param array $elements
* A renderable array.
*
* @see drupal_render_cache_get()
*/
function drupal_render_cache_set(&$markup, array $elements) {
// Create the cache ID for the element.
if (!\Drupal::request()->isMethodSafe() || !$cid = drupal_render_cid_create($elements)) {
return FALSE;
}
// Cache implementations are allowed to modify the markup, to support
// replacing markup with edge-side include commands. The supporting cache
// backend will store the markup in some other key (like
// $data['#real-value']) and return an include command instead. When the
// ESI command is executed by the content accelerator, the real value can
// be retrieved and used.
$data['#markup'] = $markup;
// Persist attached data associated with this element.
$data['#attached'] = $elements['#attached'];
// Persist #post_render_cache callbacks associated with this element.
if (isset($elements['#post_render_cache'])) {
$data['#post_render_cache'] = $elements['#post_render_cache'];
}
// Persist cache tags associated with this element. Also associate the
// "rendered" cache tag. This allows us to invalidate the entire render cache,
// regardless of the cache bin.
$data['#cache']['tags'] = $elements['#cache']['tags'];
$data['#cache']['tags'][] = 'rendered';
$bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
$expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : Cache::PERMANENT;
\Drupal::cache($bin)->set($cid, $data, $expire, $data['#cache']['tags']);
}
/** /**
* Generates a render cache placeholder. * Generates a render cache placeholder.
* *
...@@ -2358,7 +2285,7 @@ function drupal_render_cache_set(&$markup, array $elements) { ...@@ -2358,7 +2285,7 @@ function drupal_render_cache_set(&$markup, array $elements) {
* *
* @throws \Exception * @throws \Exception
* *
* @see drupal_render_cache_get() * @see \Drupal\Core\Render\Renderer::getFromCache()
*/ */
function drupal_render_cache_generate_placeholder($callback, array &$context) { function drupal_render_cache_generate_placeholder($callback, array &$context) {
if (is_string($callback) && strpos($callback, '::') === FALSE) { if (is_string($callback) && strpos($callback, '::') === FALSE) {
...@@ -2382,33 +2309,6 @@ function drupal_render_cache_generate_placeholder($callback, array &$context) { ...@@ -2382,33 +2309,6 @@ function drupal_render_cache_generate_placeholder($callback, array &$context) {
return '<drupal-render-cache-placeholder callback="' . $callback . '" token="' . $context['token'] . '"></drupal-render-cache-placeholder>'; return '<drupal-render-cache-placeholder callback="' . $callback . '" token="' . $context['token'] . '"></drupal-render-cache-placeholder>';
} }
/**
* Creates the cache ID for a renderable element.
*
* This creates the cache ID string, either by returning the #cache['cid']
* property if present or by building the cache ID out of the #cache['keys'].
*
* @param $elements
* A renderable array.
*
* @return
* The cache ID string, or FALSE if the element may not be cached.
*/
function drupal_render_cid_create($elements) {
if (isset($elements['#cache']['cid'])) {
return $elements['#cache']['cid'];
}
elseif (isset($elements['#cache']['keys'])) {
// Cache keys may either be static (just strings) or tokens (placeholders
// that are converted to static keys by the @cache_contexts service,
// depending on the request).
$cache_contexts = \Drupal::service("cache_contexts");
$keys = $cache_contexts->convertTokensToKeys($elements['#cache']['keys']);
return implode(':', $keys);
}
return FALSE;
}
/** /**
* Retrieves the default properties for the defined element type. * Retrieves the default properties for the defined element type.
* *
......
...@@ -185,16 +185,12 @@ protected function prepare(array $main_content, Request $request, RouteMatchInte ...@@ -185,16 +185,12 @@ protected function prepare(array $main_content, Request $request, RouteMatchInte
// title. We set its $is_root_call parameter to FALSE, to ensure // title. We set its $is_root_call parameter to FALSE, to ensure
// #post_render_cache callbacks are not yet applied. This is essentially // #post_render_cache callbacks are not yet applied. This is essentially
// "pre-rendering" the main content, the "full rendering" will happen in // "pre-rendering" the main content, the "full rendering" will happen in
// ::renderContentIntoResponse(). // ::renderResponse().
// @todo Remove this once https://www.drupal.org/node/2359901 lands. // @todo Remove this once https://www.drupal.org/node/2359901 lands.
if (!empty($main_content)) { if (!empty($main_content)) {
$this->renderer->render($main_content, FALSE); $this->renderer->render($main_content, FALSE);
$main_content = [ $main_content = $this->renderer->getCacheableRenderArray($main_content) + [
'#markup' => $main_content['#markup'], '#title' => isset($main_content['#title']) ? $main_content['#title'] : NULL
'#attached' => $main_content['#attached'],
'#cache' => ['tags' => $main_content['#cache']['tags']],
'#post_render_cache' => $main_content['#post_render_cache'],
'#title' => isset($main_content['#title']) ? $main_content['#title'] : NULL,
]; ];
} }
......
...@@ -7,11 +7,14 @@ ...@@ -7,11 +7,14 @@
namespace Drupal\Core\Render; namespace Drupal\Core\Render;
use Drupal\Core\Cache\CacheContexts;
use Drupal\Core\Cache\CacheFactoryInterface;
use Drupal\Core\Controller\ControllerResolverInterface; use Drupal\Core\Controller\ControllerResolverInterface;
use Drupal\Core\Theme\ThemeManagerInterface; use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\Cache;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Symfony\Component\HttpFoundation\RequestStack;
/** /**
* Turns a render array into a HTML string. * Turns a render array into a HTML string.
...@@ -39,6 +42,27 @@ class Renderer implements RendererInterface { ...@@ -39,6 +42,27 @@ class Renderer implements RendererInterface {
*/ */
protected $elementInfo; protected $elementInfo;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The cache factory.
*
* @var \Drupal\Core\Cache\CacheFactoryInterface
*/
protected $cacheFactory;
/**
* The cache contexts service.
*
* @var \Drupal\Core\Cache\CacheContexts
*/
protected $cacheContexts;
/** /**
* The stack containing bubbleable rendering metadata. * The stack containing bubbleable rendering metadata.
* *
...@@ -55,11 +79,20 @@ class Renderer implements RendererInterface { ...@@ -55,11 +79,20 @@ class Renderer implements RendererInterface {
* The theme manager. * The theme manager.
* @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info * @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info
* The element info. * The element info.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
* @param \Drupal\Core\Cache\CacheFactoryInterface $cache_factory
* The cache factory.
* @param \Drupal\Core\Cache\CacheContexts $cache_contexts
* The cache contexts service.
*/ */
public function __construct(ControllerResolverInterface $controller_resolver, ThemeManagerInterface $theme, ElementInfoManagerInterface $element_info) { public function __construct(ControllerResolverInterface $controller_resolver, ThemeManagerInterface $theme, ElementInfoManagerInterface $element_info, RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContexts $cache_contexts) {
$this->controllerResolver = $controller_resolver; $this->controllerResolver = $controller_resolver;
$this->theme = $theme; $this->theme = $theme;
$this->elementInfo = $element_info; $this->elementInfo = $element_info;
$this->requestStack = $request_stack;
$this->cacheFactory = $cache_factory;
$this->cacheContexts = $cache_contexts;
} }
/** /**
...@@ -133,7 +166,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { ...@@ -133,7 +166,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
// Try to fetch the prerendered element from cache, run any // Try to fetch the prerendered element from cache, run any
// #post_render_cache callbacks and return the final markup. // #post_render_cache callbacks and return the final markup.
if (isset($elements['#cache'])) { if (isset($elements['#cache'])) {
$cached_element = drupal_render_cache_get($elements); $cached_element = $this->cacheGet($elements);
if ($cached_element !== FALSE) { if ($cached_element !== FALSE) {
$elements = $cached_element; $elements = $cached_element;
// Only when we're not in a root (non-recursive) drupal_render() call, // Only when we're not in a root (non-recursive) drupal_render() call,
...@@ -312,7 +345,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { ...@@ -312,7 +345,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
// Cache the processed element if #cache is set. // Cache the processed element if #cache is set.
if (isset($elements['#cache'])) { if (isset($elements['#cache'])) {
drupal_render_cache_set($elements['#markup'], $elements); $this->cacheSet($elements);
} }
// Only when we're in a root (non-recursive) drupal_render() call, // Only when we're in a root (non-recursive) drupal_render() call,
...@@ -437,4 +470,109 @@ protected function processPostRenderCache(array &$elements) { ...@@ -437,4 +470,109 @@ protected function processPostRenderCache(array &$elements) {
} }
} }
/**
* Gets the cached, prerendered element of a renderable element from the cache.
*
* @param array $elements
* A renderable array.
*
* @return array
* A renderable array, with the original element and all its children pre-
* rendered, or FALSE if no cached copy of the element is available.
*
* @see ::render()
* @see ::saveToCache()
*/
protected function cacheGet(array $elements) {
// Form submissions rely on the form being built during the POST request,
// and render caching of forms prevents this from happening.
// @todo remove the isMethodSafe() check when
// https://www.drupal.org/node/2367555 lands.
if (!$this->requestStack->getCurrentRequest()->isMethodSafe() || !$cid = $this->createCacheID($elements)) {
return FALSE;
}
$bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
if (!empty($cid) && $cache = $this->cacheFactory->get($bin)->get($cid)) {
$cached_element = $cache->data;
// Return the cached element.
return $cached_element;
}
return FALSE;
}
/**
* Caches the rendered output of a renderable element.
*
* This is called by ::render() if the #cache property is set on an element.
*
* @param array $elements
* A renderable array.
*
* @return bool|null
* Returns FALSE if no cache item could be created, NULL otherwise.
*
* @see ::getFromCache()
*/
protected function cacheSet(array &$elements) {
// Form submissions rely on the form being built during the POST request,
// and render caching of forms prevents this from happening.
// @todo remove the isMethodSafe() check when
// https://www.drupal.org/node/2367555 lands.
if (!$this->requestStack->getCurrentRequest()->isMethodSafe() || !$cid = $this->createCacheID($elements)) {
return FALSE;
}
$data = $this->getCacheableRenderArray($elements);
// Cache tags are cached, but we also want to assocaite the "rendered" cache
// tag. This allows us to invalidate the entire render cache, regardless of
// the cache bin.
$data['#cache']['tags'][] = 'rendered';
$bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
$expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : Cache::PERMANENT;
$this->cacheFactory->get($bin)->set($cid, $data, $expire, $data['#cache']['tags']);
}
/**
* Creates the cache ID for a renderable element.
*
* This creates the cache ID string, either by returning the #cache['cid']
* property if present or by building the cache ID out of the #cache['keys'].
*
* @param array $elements
* A renderable array.
*
* @return string
* The cache ID string, or FALSE if the element may not be cached.
*/
protected function createCacheID(array $elements) {
if (isset($elements['#cache']['cid'])) {
return $elements['#cache']['cid'];
}
elseif (isset($elements['#cache']['keys'])) {
// Cache keys may either be static (just strings) or tokens (placeholders
// that are converted to static keys by the @cache_contexts service,
// depending on the request).
$keys = $this->cacheContexts->convertTokensToKeys($elements['#cache']['keys']);
return implode(':', $keys);
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function getCacheableRenderArray(array $elements) {
return [
'#markup' => $elements['#markup'],
'#attached' => $elements['#attached'],
'#post_render_cache' => $elements['#post_render_cache'],
'#cache' => [
'tags' => $elements['#cache']['tags'],
],
];
}
} }
...@@ -97,8 +97,7 @@ public function renderPlain(&$elements); ...@@ -97,8 +97,7 @@ public function renderPlain(&$elements);
* 'keys' is set, the cache ID is created automatically from these keys. * 'keys' is set, the cache ID is created automatically from these keys.
* Cache keys may either be static (just strings) or tokens * Cache keys may either be static (just strings) or tokens
* (placeholders that are converted to static keys by the * (placeholders that are converted to static keys by the
* @cache_contexts service, depending on the request). See * 'cache_contexts' service, depending on the request).
* drupal_render_cid_create().
* - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is * - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is
* required. If 'cid' is set, 'keys' is ignored. Use only if you have * required. If 'cid' is set, 'keys' is ignored. Use only if you have
* special requirements. * special requirements.
...@@ -269,4 +268,23 @@ public function renderPlain(&$elements); ...@@ -269,4 +268,23 @@ public function renderPlain(&$elements);
*/ */
public function render(&$elements, $is_root_call = FALSE); public function render(&$elements, $is_root_call = FALSE);
/**
* Gets a cacheable render array for a render array and its rendered output.
*
* Given a render array and its rendered output (HTML string), return an array
* data structure that allows the render array and its associated metadata to
* be cached reliably (and is serialization-safe).
*
* If Drupal needs additional rendering metadata to be cached at some point,
* consumers of this method will continue to work. Those who only cache
* certain parts of a render array will cease to work.
*
* @param array $elements
* A renderable array, on which ::render() has already been invoked.
*
* @return array
* An array representing the cacheable data for this render array.
*/
public function getCacheableRenderArray(array $elements);
} }
...@@ -160,7 +160,7 @@ protected function verifyRenderCacheHandling() { ...@@ -160,7 +160,7 @@ protected function verifyRenderCacheHandling() {
// Test that a cache entry is created. // Test that a cache entry is created.
$build = $this->getBlockRenderArray(); $build = $this->getBlockRenderArray();
$cid = drupal_render_cid_create($build); $cid = 'entity_view:block:test_block:en:core';
drupal_render($build); drupal_render($build);
$this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.'); $this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
...@@ -223,10 +223,10 @@ public function testBlockViewBuilderAlter() { ...@@ -223,10 +223,10 @@ public function testBlockViewBuilderAlter() {
)); ));
$alter_add_key = $this->randomMachineName(); $alter_add_key = $this->randomMachineName();
\Drupal::state()->set('block_test_view_alter_cache_key', $alter_add_key); \Drupal::state()->set('block_test_view_alter_cache_key', $alter_add_key);
$cid = 'entity_view:block:test_block:en:core:' . $alter_add_key;
$expected_keys = array_merge($default_keys, array($alter_add_key)); $expected_keys = array_merge($default_keys, array($alter_add_key));
$build = $this->getBlockRenderArray(); $build = $this->getBlockRenderArray();
$this->assertIdentical($expected_keys, $build['#cache']['keys'], 'An altered cacheable block has the expected cache keys.'); $this->assertIdentical($expected_keys, $build['#cache']['keys'], 'An altered cacheable block has the expected cache keys.');
$cid = drupal_render_cid_create(array('#cache' => array('keys' => $expected_keys)));
$this->assertIdentical(drupal_render($build), ''); $this->assertIdentical(drupal_render($build), '');
$cache_entry = $this->container->get('cache.render')->get($cid); $cache_entry = $this->container->get('cache.render')->get($cid);
$this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.'); $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
...@@ -242,7 +242,6 @@ public function testBlockViewBuilderAlter() { ...@@ -242,7 +242,6 @@ public function testBlockViewBuilderAlter() {
$build = $this->getBlockRenderArray(); $build = $this->getBlockRenderArray();
sort($build['#cache']['tags']); sort($build['#cache']['tags']);
$this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.'); $this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.');
$cid = drupal_render_cid_create(array('#cache' => array('keys' => $expected_keys)));
$this->assertIdentical(drupal_render($build), ''); $this->assertIdentical(drupal_render($build), '');
$cache_entry = $this->container->get('cache.render')->get($cid); $cache_entry = $this->container->get('cache.render')->get($cid);
$this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.'); $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
...@@ -272,6 +271,8 @@ public function testBlockViewBuilderAlter() { ...@@ -272,6 +271,8 @@ public function testBlockViewBuilderAlter() {
* @see \Drupal\block\Tests\BlockCacheTest * @see \Drupal\block\Tests\BlockCacheTest
*/ */
public function testBlockViewBuilderCacheContexts() { public function testBlockViewBuilderCacheContexts() {
$cache_contexts = \Drupal::service("cache_contexts");
// Force a request via GET so we can get drupal_render() cache working. // Force a request via GET so we can get drupal_render() cache working.
$request = \Drupal::request(); $request = \Drupal::request();
$request_method = $request->server->get('REQUEST_METHOD'); $request_method = $request->server->get('REQUEST_METHOD');
...@@ -282,7 +283,7 @@ public function testBlockViewBuilderCacheContexts() { ...@@ -282,7 +283,7 @@ public function testBlockViewBuilderCacheContexts() {
'max_age' => 600, 'max_age' => 600,
)); ));
$build = $this->getBlockRenderArray(); $build = $this->getBlockRenderArray();
$cid = drupal_render_cid_create($build); $cid = implode(':', $build['#cache']['keys']);
drupal_render($build); drupal_render($build);
$this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.'); $this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.');
...@@ -293,7 +294,7 @@ public function testBlockViewBuilderCacheContexts() { ...@@ -293,7 +294,7 @@ public function testBlockViewBuilderCacheContexts() {
)); ));
$old_cid = $cid; $old_cid = $cid;
$build = $this->getBlockRenderArray(); $build = $this->getBlockRenderArray();
$cid = drupal_render_cid_create($build); $cid = implode(':', $cache_contexts->convertTokensToKeys($build['#cache']['keys']));
drupal_render($build); drupal_render($build);
$this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.'); $this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.');
$this->assertNotEqual($cid, $old_cid, 'The cache ID has changed.'); $this->assertNotEqual($cid, $old_cid, 'The cache ID has changed.');
...@@ -306,7 +307,7 @@ public function testBlockViewBuilderCacheContexts() { ...@@ -306,7 +307,7 @@ public function testBlockViewBuilderCacheContexts() {
$this->container->set('cache_context.url', $temp_context); $this->container->set('cache_context.url', $temp_context);
$old_cid = $cid; $old_cid = $cid;
$build = $this->getBlockRenderArray(); $build = $this->getBlockRenderArray();
$cid = drupal_render_cid_create($build); $cid = implode(':', $cache_contexts->convertTokensToKeys($build['#cache']['keys']));
drupal_render($build); drupal_render($build);
$this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.'); $this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.');
$this->assertNotEqual($cid, $old_cid, 'The cache ID has changed.'); $this->assertNotEqual($cid, $old_cid, 'The cache ID has changed.');
......
...@@ -525,7 +525,7 @@ function testDrupalRenderPostRenderCache() { ...@@ -525,7 +525,7 @@ function testDrupalRenderPostRenderCache() {
// GET request: validate cached data. // GET request: validate cached data.
$element = array('#cache' => array('cid' => 'post_render_cache_test_GET')); $element = array('#cache' => array('cid' => 'post_render_cache_test_GET'));
$cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; $cached_element = \Drupal::cache('render')->get('post_render_cache_test_GET')->data;
$expected_element = array( $expected_element = array(
'#markup' => '<p>#cache enabled, GET</p>', '#markup' => '<p>#cache enabled, GET</p>',
'#attached' => $test_element['#attached'], '#attached' => $test_element['#attached'],
...@@ -566,8 +566,7 @@ function testDrupalRenderPostRenderCache() { ...@@ -566,8 +566,7 @@ function testDrupalRenderPostRenderCache() {
$this->assertIdentical($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the one added by the #post_render_cache callback exist.'); $this->assertIdentical($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the one added by the #post_render_cache callback exist.');
// POST request: Ensure no data was cached. // POST request: Ensure no data was cached.
$element = array('#cache' => array('cid' => 'post_render_cache_test_POST')); $cached_element = \Drupal::cache('render')->get('post_render_cache_test_POST');
$cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element));
$this->assertFalse($cached_element, 'No data is cached because this is a POST request.'); $this->assertFalse($cached_element, 'No data is cached because this is a POST request.');
// Restore the previous request method. // Restore the previous request method.
...@@ -630,8 +629,7 @@ function testDrupalRenderChildrenPostRenderCache() { ...@@ -630,8 +629,7 @@ function testDrupalRenderChildrenPostRenderCache() {
$this->assertIdentical($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.'); $this->assertIdentical($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
// GET request: validate cached data. // GET request: validate cached data.
$element = array('#cache' => $element['#cache']); $cached_element = \Drupal::cache('render')->get('simpletest:drupal_render:children_post_render_cache')->data;
$cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data;
$expected_element = array( $expected_element = array(
'#attached' => array( '#attached' => array(
'drupalSettings' => [ 'drupalSettings' => [
...@@ -697,11 +695,8 @@ function testDrupalRenderChildrenPostRenderCache() { ...@@ -697,11 +695,8 @@ function testDrupalRenderChildrenPostRenderCache() {
$this->assertIdentical($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.'); $this->assertIdentical($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
// GET request: validate cached data for both the parent and child. // GET request: validate cached data for both the parent and child.
$element = $test_element; $cached_parent_element = \Drupal::cache('render')->get('simpletest:drupal_render:children_post_render_cache:nested_cache_parent')->data;
$element['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_parent'); $cached_child_element = \Drupal::cache('render')->get('simpletest:drupal_render:children_post_render_cache:nested_cache_child')->data;
$element['child']['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_child');
$cached_parent_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data;
$cached_child_element = \Drupal::cache('render')->get(drupal_render_cid_create($element['child']))->data;
$expected_parent_element = array( $expected_parent_element = array(
'#attached' => array( '#attached' => array(
'drupalSettings' => [ 'drupalSettings' => [
...@@ -834,8 +829,7 @@ function testDrupalRenderRenderCachePlaceholder() { ...@@ -834,8 +829,7 @@ function testDrupalRenderRenderCachePlaceholder() {
// GET request: validate cached data. // GET request: validate cached data.
$expected_token = $context['token']; $expected_token = $context['token'];
$element = array('#cache' => array('cid' => 'render_cache_placeholder_test_GET')); $cached_element = \Drupal::cache('render')->get('render_cache_placeholder_test_GET')->data;
$cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data;
// Parse unique token out of the cached markup. // Parse unique token out of the cached markup.
$dom = Html::load($cached_element['#markup']); $dom = Html::load($cached_element['#markup']);
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
...@@ -927,8 +921,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { ...@@ -927,8 +921,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
// GET request: validate cached data for child element. // GET request: validate cached data for child element.
$expected_token = $context['token']; $expected_token = $context['token'];
$element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET')); $cached_element = \Drupal::cache('render')->get('render_cache_placeholder_test_child_GET')->data;
$cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data;
// Parse unique token out of the cached markup. // Parse unique token out of the cached markup.
$dom = Html::load($cached_element['#markup']); $dom = Html::load($cached_element['#markup']);
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
...@@ -953,8 +946,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { ...@@ -953,8 +946,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
$this->assertIdentical($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.'); $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
// GET request: validate cached data (for the parent/entire render array). // GET request: validate cached data (for the parent/entire render array).
$element = array('#cache' => array('cid' => 'render_cache_placeholder_test_GET')); $cached_element = \Drupal::cache('render')->get('render_cache_placeholder_test_GET')->data;
$cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data;
// Parse unique token out of the cached markup. // Parse unique token out of the cached markup.
$dom = Html::load($cached_element['#markup']); $dom = Html::load($cached_element['#markup']);
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
...@@ -981,8 +973,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { ...@@ -981,8 +973,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
// GET request: validate cached data. // GET request: validate cached data.
// Check the cache of the child element again after the parent has been // Check the cache of the child element again after the parent has been
// rendered. // rendered.
$element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET')); $cached_element = \Drupal::cache('render')->get('render_cache_placeholder_test_child_GET')->data;
$cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data;
// Verify that the child element contains the correct // Verify that the child element contains the correct
// render_cache_placeholder markup. // render_cache_placeholder markup.
$dom = Html::load($cached_element['#markup']); $dom = Html::load($cached_element['#markup']);
......
...@@ -33,6 +33,8 @@ protected function setUp() { ...@@ -33,6 +33,8 @@ protected function setUp() {
* Tests entity render cache handling. * Tests entity render cache handling.
*/ */
public function testEntityViewBuilderCache() { public function testEntityViewBuilderCache() {
$cache_contexts = \Drupal::service("cache_contexts");
// Force a request via GET so we can get drupal_render() cache working. // Force a request via GET so we can get drupal_render() cache working.
$request = \Drupal::request(); $request = \Drupal::request();
$request_method = $request->server->get('REQUEST_METHOD'); $request_method = $request->server->get('REQUEST_METHOD');
...@@ -48,7 +50,7 @@ public function testEntityViewBuilderCache() { ...@@ -48,7 +50,7 @@ public function testEntityViewBuilderCache() {
// Get a fully built entity view render array. // Get a fully built entity view render array.
$entity_test->save(); $entity_test->save();
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
$cid = drupal_render_cid_create($build); $cid = implode(':', $cache_contexts->convertTokensToKeys($build['#cache']['keys']));
$bin = $build['#cache']['bin']; $bin = $build['#cache']['bin'];
// Mock the build array to not require the theme registry. // Mock the build array to not require the theme registry.
...@@ -79,6 +81,8 @@ public function testEntityViewBuilderCache() { ...@@ -79,6 +81,8 @@ public function testEntityViewBuilderCache() {
* Tests entity render cache with references. * Tests entity render cache with references.
*/ */
public function testEntityViewBuilderCacheWithReferences() { public function testEntityViewBuilderCacheWithReferences() {
$cache_contexts = \Drupal::service("cache_contexts");
// Force a request via GET so we can get drupal_render() cache working. // Force a request via GET so we can get drupal_render() cache working.
$request = \Drupal::request(); $request = \Drupal::request();
$request_method = $request->server->get('REQUEST_METHOD'); $request_method = $request->server->get('REQUEST_METHOD');
...@@ -95,7 +99,7 @@ public function testEntityViewBuilderCacheWithReferences() { ...@@ -95,7 +99,7 @@ public function testEntityViewBuilderCacheWithReferences() {
// Get a fully built entity view render array for the referenced entity. // Get a fully built entity view render array for the referenced entity.
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full'); $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full');
$cid_reference = drupal_render_cid_create($build); $cid_reference = implode(':', $cache_contexts->convertTokensToKeys($build['#cache']['keys']));
$bin_reference = $build['#cache']['bin']; $bin_reference = $build['#cache']['bin'];
// Mock the build array to not require the theme registry. // Mock the build array to not require the theme registry.
...@@ -114,7 +118,7 @@ public function testEntityViewBuilderCacheWithReferences() { ...@@ -114,7 +118,7 @@ public function testEntityViewBuilderCacheWithReferences() {