diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php
index 94b2d10..eb70804 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php
@@ -10,6 +10,7 @@
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Json;
use Drupal\simpletest\DrupalUnitTestBase;
+use Drupal\Component\Utility\Crypt;
/**
* Tests drupal_render().
@@ -542,6 +543,249 @@ function testDrupalRenderPostRenderCache() {
}
/**
+ * Tests post-render cache-integrated 'render_cache_placeholder' element.
+ */
+ function testDrupalRenderRenderCachePlaceholder() {
+ $context = array(
+ 'bar' => $this->randomContextValue(),
+ 'token' => Crypt::randomBytesBase64(55),
+ );
+ $callback = 'common_test_post_render_cache_placeholder';
+ $test_element = array(
+ '#post_render_cache' => array(
+ $callback => array(
+ $context
+ ),
+ ),
+ '#markup' => drupal_render_cache_generate_placeholder($callback, $context, $context['token']),
+ '#prefix' => '',
+ '#suffix' => ''
+ );
+ $expected_output = '' . $context['bar'] . '';
+
+ // #cache disabled.
+ drupal_static_reset('_drupal_add_js');
+ $element = $test_element;
+ $output = drupal_render($element);
+ $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+ $settings = $this->parseDrupalSettings(drupal_get_js());
+ $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
+
+ // The cache system is turned off for POST requests.
+ $request_method = \Drupal::request()->getMethod();
+ \Drupal::request()->setMethod('GET');
+
+ // GET request: #cache enabled, cache miss.
+ drupal_static_reset('_drupal_add_js');
+ $element = $test_element;
+ $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
+ $output = drupal_render($element);
+ $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+ $this->assertTrue(isset($element['#printed']), 'No cache hit');
+ $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
+ $settings = $this->parseDrupalSettings(drupal_get_js());
+ $this->assertIdentical($settings['common_test'], $context, '#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'];
+ $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.
+ $dom = Html::load($cached_element['#markup']);
+ $xpath = new \DOMXPath($dom);
+ $nodes = $xpath->query('//*[@token]');
+ $this->assertTrue($nodes->length, 'The token attribute was found in the cached markup');
+ $token = '';
+ if ($nodes->length) {
+ $token = $nodes->item(0)->getAttribute('token');
+ }
+ $this->assertIdentical($token, $expected_token, 'The tokens are identical');
+ // Verify the token is in the cached element.
+ $expected_element = array(
+ '#markup' => '',
+ '#post_render_cache' => array(
+ 'common_test_post_render_cache_placeholder' => array(
+ $context
+ ),
+ ),
+ '#cache' => array('tags' => array()),
+ );
+ $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.
+ drupal_static_reset('_drupal_add_js');
+ $element = $test_element;
+ $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
+ $output = drupal_render($element);
+ $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+ $this->assertFalse(isset($element['#printed']), 'Cache hit');
+ $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
+ $settings = $this->parseDrupalSettings(drupal_get_js());
+ $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
+
+ // Restore the previous request method.
+ \Drupal::request()->setMethod($request_method);
+ }
+
+ /**
+ * Tests post-render cache-integrated 'render_cache_placeholder' child
+ * element.
+ */
+ function testDrupalRenderChildElementRenderCachePlaceholder() {
+ $container = array(
+ '#type' => 'container',
+ );
+ $context = array(
+ 'bar' => $this->randomContextValue(),
+ 'token' => Crypt::randomBytesBase64(55),
+ );
+ $callback = 'common_test_post_render_cache_placeholder';
+ $test_element = array(
+ '#post_render_cache' => array(
+ $callback => array(
+ $context
+ ),
+ ),
+ '#markup' => drupal_render_cache_generate_placeholder($callback, $context, $context['token']),
+ '#prefix' => '',
+ '#suffix' => ''
+ );
+ $container['test_element'] = $test_element;
+ $expected_output = '
' . $context['bar'] . '
' . "\n";
+
+ // #cache disabled.
+ drupal_static_reset('_drupal_add_js');
+ $element = $container;
+ $output = drupal_render($element);
+ $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+ $settings = $this->parseDrupalSettings(drupal_get_js());
+ $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
+
+ // The cache system is turned off for POST requests.
+ $request_method = \Drupal::request()->getMethod();
+ \Drupal::request()->setMethod('GET');
+
+ // GET request: #cache enabled, cache miss.
+ drupal_static_reset('_drupal_add_js');
+ $element = $container;
+ $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
+ $element['test_element']['#cache'] = array('cid' => 'render_cache_placeholder_test_child_GET');
+ // Simulate element rendering in a template, where sub-items of a renderable
+ // can be sent to drupal_render() before the parent.
+ $child = &$element['test_element'];
+ $element['#children'] = drupal_render($child, TRUE);
+ // Eventually, drupal_render() gets called on the root element.
+ $output = drupal_render($element);
+ $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+ $this->assertTrue(isset($element['#printed']), 'No cache hit');
+ $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
+ $settings = $this->parseDrupalSettings(drupal_get_js());
+ $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
+
+ // GET request: validate cached data for child element.
+ $child_tokens = $element['test_element']['#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;
+ $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.
+ $dom = Html::load($cached_element['#markup']);
+ $xpath = new \DOMXPath($dom);
+ $nodes = $xpath->query('//*[@token]');
+ $this->assertTrue($nodes->length, 'The token attribute was found in the cached child element markup');
+ $token = '';
+ if ($nodes->length) {
+ $token = $nodes->item(0)->getAttribute('token');
+ }
+ debug(array($token, $expected_token,), 'tokens', TRUE);
+ $this->assertIdentical($token, $expected_token, 'The tokens are identical for the child element');
+ // Verify the token is in the cached element.
+ $expected_element = array(
+ '#markup' => '',
+ '#post_render_cache' => array(
+ 'common_test_post_render_cache_placeholder' => array(
+ $context,
+ ),
+ ),
+ '#cache' => array('tags' => array()),
+ );
+ debug(array($cached_element, $expected_element,), '', TRUE);
+ $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).
+ $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.
+ $dom = Html::load($cached_element['#markup']);
+ $xpath = new \DOMXPath($dom);
+ $nodes = $xpath->query('//*[@token]');
+ $this->assertTrue($nodes->length, 'The token attribute was found in the cached parent element markup');
+ $token = '';
+ if ($nodes->length) {
+ $token = $nodes->item(0)->getAttribute('token');
+ }
+ $this->assertIdentical($token, $expected_token, 'The tokens are identical for the parent element');
+ // Verify the token is in the cached element.
+ $expected_element = array(
+ '#markup' => '
' . "\n",
+ '#post_render_cache' => array(
+ 'common_test_post_render_cache_placeholder' => array(
+ $context,
+ ),
+ ),
+ '#cache' => array('tags' => array()),
+ );
+ $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached for the parent element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
+
+ // GET request: validate cached data.
+ // Check the cache of the child element again after the parent has been
+ // rendered.
+ $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET'));
+ $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]');
+ $this->assertTrue($nodes->length, 'The token attribute was found in the cached child element markup');
+ $token = '';
+ if ($nodes->length) {
+ $token = $nodes->item(0)->getAttribute('token');
+ }
+ $this->assertIdentical($token, $expected_token, 'The tokens are identical for the child element');
+ // Verify the token is in the cached element.
+ $expected_element = array(
+ '#markup' => '',
+ '#post_render_cache' => array(
+ 'common_test_post_render_cache_placeholder' => array(
+ $context,
+ ),
+ ),
+ '#cache' => array('tags' => array()),
+ );
+ $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: #cache enabled, cache hit.
+ drupal_static_reset('_drupal_add_js');
+ $element = $container;
+ $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
+ // Simulate element rendering in a template, where sub-items of a renderable
+ // can be sent to drupal_render before the parent.
+ $child = &$element['test_element'];
+ $element['#children'] = drupal_render($child, TRUE);
+ $output = drupal_render($element);
+ $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+ $this->assertFalse(isset($element['#printed']), 'Cache hit');
+ $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
+ $settings = $this->parseDrupalSettings(drupal_get_js());
+ $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
+
+ // Restore the previous request method.
+ \Drupal::request()->setMethod($request_method);
+ }
+
+ /**
* Tests post-render cache callbacks functionality in children elements.
*/
function testDrupalRenderChildrenPostRenderCache() {
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 1a081df..4982aac 100644
--- a/core/modules/system/tests/modules/common_test/common_test.module
+++ b/core/modules/system/tests/modules/common_test/common_test.module
@@ -204,8 +204,9 @@ function common_test_post_render_cache(array $element, array $context) {
* @return array
* A render array.
*/
-function common_test_post_render_cache_placeholder(array $context) {
- $element = array(
+function common_test_post_render_cache_placeholder(array $element, array $context) {
+ $placeholder = drupal_render_cache_generate_placeholder(__FUNCTION__, $context, $context['token']);
+ $replace_element = array(
'#markup' => '' . $context['bar'] . '',
'#attached' => array(
'js' => array(
@@ -218,6 +219,8 @@ function common_test_post_render_cache_placeholder(array $context) {
),
),
);
+ $replace = drupal_render($replace_element);
+ $element['#markup'] = str_replace($placeholder, $replace, $element['#markup']);
return $element;
}