core/lib/Drupal/Core/Render/Renderer.php | 3 +- .../modules/system/src/Tests/Common/RenderTest.php | 55 +++++++++++++++------- .../tests/modules/common_test/common_test.module | 28 +++++++++++ 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index 2c9d4b2..97565a4 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -361,6 +361,7 @@ public function render(&$elements, $is_root_call = FALSE) { * #post_render_cache callbacks may modify: * - #markup: to replace placeholders * - #attached: to add libraries or JavaScript settings + * - #post_render_cache: to execute additional #post_render_cache callbacks * * Note that in either of these cases, #post_render_cache callbacks are * implicitly idempotent: a placeholder that has been replaced can't be @@ -368,8 +369,6 @@ public function render(&$elements, $is_root_call = FALSE) { * * @param array &$elements * The structured array describing the data being rendered. - * - * @see drupal_render_collect_post_render_cache */ protected function processPostRenderCache(array &$elements) { if (isset($elements['#post_render_cache'])) { diff --git a/core/modules/system/src/Tests/Common/RenderTest.php b/core/modules/system/src/Tests/Common/RenderTest.php index 739e3b5..61e3305 100644 --- a/core/modules/system/src/Tests/Common/RenderTest.php +++ b/core/modules/system/src/Tests/Common/RenderTest.php @@ -796,6 +796,10 @@ function testDrupalRenderChildrenPostRenderCache() { function testDrupalRenderRenderCachePlaceholder() { $context = array( 'bar' => $this->randomContextValue(), + // Provide a token instead of letting one be generated by + // drupal_render_cache_generate_placeholder(), otherwise we cannot know + // what the token is. + 'token' => \Drupal\Component\Utility\Crypt::randomBytesBase64(55), ); $callback = 'common_test_post_render_cache_placeholder'; $placeholder = drupal_render_cache_generate_placeholder($callback, $context); @@ -837,7 +841,7 @@ function testDrupalRenderRenderCachePlaceholder() { $this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.'); // GET request: validate cached data. - $expected_token = $element['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; + $expected_token = $context['token']; $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Parse unique token out of the cached markup. @@ -861,7 +865,7 @@ function testDrupalRenderRenderCachePlaceholder() { ), '#cache' => array('tags' => array('rendered')), ); - $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached: 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: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.'); // GET request: #cache enabled, cache hit. $element = $test_element; @@ -883,6 +887,10 @@ function testDrupalRenderRenderCachePlaceholder() { function testDrupalRenderChildElementRenderCachePlaceholder() { $context = array( 'bar' => $this->randomContextValue(), + // Provide a token instead of letting one be generated by + // drupal_render_cache_generate_placeholder(), otherwise we cannot know + // what the token is. + 'token' => \Drupal\Component\Utility\Crypt::randomBytesBase64(55), ); $callback = 'common_test_post_render_cache_placeholder'; $placeholder = drupal_render_cache_generate_placeholder($callback, $context); @@ -926,9 +934,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { $this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.'); // GET request: validate cached data for child element. - $child_tokens = $element['foo']['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; - $parent_tokens = $element['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; - $expected_token = $child_tokens; + $expected_token = $context['token']; $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Parse unique token out of the cached markup. @@ -987,7 +993,6 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Verify that the child element contains the correct // render_cache_placeholder markup. - $expected_token = $child_tokens; $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); @@ -1025,6 +1030,31 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { } /** + * Tests a #post_render_cache callback that adds another #post_render_cache + * callback. + * + * E.g. when rendering a node in a #post_render_cache callback, the rendering + * of that node needs a #post_render_cache callback of its own to be executed + * (to render the node links). + */ + function testRecursivePostRenderCache() { + $context = array('foo' => $this->randomContextValue()); + $element = []; + $element['#markup'] = ''; + $element['#post_render_cache']['common_test_post_render_cache_recursion'] = array( + $context + ); + + $output = drupal_render_root($element); + $this->assertEqual('
overridden
', $output, 'The output has been modified by the indirect, recursive #post_render_cache callback.'); + $this->assertIdentical($element['#markup'], 'overridden
', '#markup is overridden by the indirect, recursive #post_render_cache callback.'); + $expected_js = [ + ['type' => 'setting', 'data' => ['common_test' => $context]], + ]; + $this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified by the indirect, recursive #post_render_cache callback.'); + } + + /** * #pre_render callback for testDrupalRenderBubbling(). */ public static function bubblingPreRender($elements) { @@ -1123,18 +1153,7 @@ function testDrupalRenderBubbling() { ), ); $this->assertEqual($expected_attached, $test_element['#attached'], 'Expected assets found.'); - $expected_post_render_cache = array( - 'Drupal\\system\\Tests\\Common\\RenderTest::bubblingPostRenderCache' => array( - 0 => array ( - 'foo' => 'bar', - 'baz' => 'qux', - ), - ), - ); - $post_render_cache = $test_element['#post_render_cache']; - // We don't care about the exact token. - unset($post_render_cache['Drupal\\system\\Tests\\Common\\RenderTest::bubblingPostRenderCache'][0]['token']); - $this->assertEqual($expected_post_render_cache, $post_render_cache, 'Expected post-render cache data found.'); + $this->assertEqual([], $test_element['#post_render_cache'], '#post_render_cache property is empty after rendering'); // Ensure that #pre_render callbacks are only executed if they don't have // a render cache hit. diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module index a1100b4..24aa3da 100644 --- a/core/modules/system/tests/modules/common_test/common_test.module +++ b/core/modules/system/tests/modules/common_test/common_test.module @@ -255,6 +255,34 @@ function common_test_post_render_cache_placeholder(array $element, array $contex } /** + * #post_render_cache callback; bubbles another #post_render_cache callback. + * + * @param array $element + * A render array with the following keys: + * - #markup + * - #attached + * @param array $context + * An array with the following keys: + * - foo: contains a random string. + * + * @return array $element + * The updated $element. + */ +function common_test_post_render_cache_recursion(array $element, array $context) { + // Render a child which itself also has a #post_render_cache callback that + // must be bubbled. + $child = []; + $child['#markup'] = 'foo'; + $child['#post_render_cache']['common_test_post_render_cache'][] = $context; + + // Render the child. + $element['#markup'] = drupal_render($child); + + return $element; +} + + +/** * Implements hook_page_attachments(). * * @see \Drupal\system\Tests\Common\PageRenderTest::assertPageRenderHookExceptions()