diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php index 64a48c7..53b914b 100644 --- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php @@ -213,6 +213,10 @@ public function renderResponse(array $main_content, Request $request, RouteMatch ksort($placeholders); + // Hack: We now want all remaining placeholders to be processed directly. + global $process_remaining_placeholders; + $process_remaining_placeholders = TRUE; + foreach ($placeholders as $placeholder => $placeholder_elements) { // Check if the placeholder is present at all. if (strpos($markup, $placeholder) === FALSE) { diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index acfb17f..a22fc6f 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -546,7 +546,7 @@ protected function processPostRenderCache(array &$elements) { * The structured array describing the data being rendered. */ protected function processPlaceholders(array &$elements) { - if (!isset($elements['#cache']['placeholders'])) { + if (empty($elements['#cache']['placeholders'])) { return; } foreach ($elements['#cache']['placeholders'] as $placeholder => $placeholder_elements) { @@ -558,12 +558,16 @@ protected function processPlaceholders(array &$elements) { $elements['#markup'] = str_replace('
', $markup, $elements['#markup']); unset($elements['#cache']['placeholders'][$placeholder]); } -/* + + // @todo Render strategies will need to take care of this. + global $process_remaining_placeholders; + + if (isset($process_remaining_placeholders)) { $placeholder_elements['#cache_no_placeholder'] = TRUE; $markup = $this->doRender($placeholder_elements); - $elements['#markup'] = str_replace($placeholder, $markup, $elements['#markup']); + $elements['#markup'] = str_replace('
', $markup, $elements['#markup']); unset($elements['#cache']['placeholders'][$placeholder]); -*/ + } } } @@ -606,6 +610,15 @@ protected function cacheGet(array $elements, $is_recursive = FALSE) { } } + // Ensure that all placeholders can be 'satisfied for this request. + if (!empty($cached_element['#cache']['placeholders'])) { + if (!$this->checkPlaceholders($cached_element['#cache']['placeholders'])) { + // If placeholders are not cached AND don't use #pre_render_cache there + // is nothing we can do, treat the element as cache-miss. + $cached_element = FALSE; + } + } + // Placeholder support: A bubbled up max-age=0 or high-frequency cache context // could have added a #cache_placeholder tag to the $cache_redirect entry. // Or the original $elements could have a max-age=0 or a #cache_placeholder tag. @@ -640,6 +653,50 @@ protected function cacheGet(array $elements, $is_recursive = FALSE) { } /** + * Checks that all placeholders can be satisfied. + * + * A placeholder can be satisfied if it is: + * - cached already for this request + * - possible to re-create via #pre_render_cache + * + * @param array $placeholders + * An array of placeholders keyed by placeholder id with a + * render cache array as content. + * + * @return bool + * TRUE if all placeholders can be satisfied, FALSE otherwise. + */ + protected function checkPlaceholders(array $placeholders) { + // @todo: Use $this->getCacheMultiple() instead. + $cached_placeholders = []; + foreach ($placeholders as $placeholder => $elements) { + // If we can satisfy the information internally already, do so. + // This can happen if an earlier checkPlaceholders() failed + // (e.g. full page), but a lower level uses the same placeholder. + if (isset($this->placeholderData[$placeholder])) { + $cached_placeholders[$placeholder] = $this->placeholderData[$placeholder]; + continue; + } + $cached_placeholders[$placeholder] = $this->cacheGet($elements); + } + + $can_recreate_placeholders = TRUE; + foreach ($placeholders as $placeholder => $elements) { + if (!$cached_placeholders[$placeholder]) { + if (!isset($elements['#pre_render_cache'])) { + // This will make this fail, but still store the other placeholders. + $can_recreate_placeholders = FALSE; + } + continue; + } + $this->placeholderData[$placeholder] = $cached_placeholders[$placeholder]; + } + + // A cache miss and no #pre_render_cache is fatal. + return $can_recreate_placeholders; + } + + /** * Create a render cache array placeholder. * * @param array $elements diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php index c892150..6b3ad60 100644 --- a/core/modules/block/src/BlockViewBuilder.php +++ b/core/modules/block/src/BlockViewBuilder.php @@ -116,6 +116,11 @@ public static function preRenderBlock($build, $context) { $build['#configuration']['label'] = SafeMarkup::checkPlain($configuration['label']); + // Give the plugin a chance to alter the configuration first. + if (method_exists($plugin, 'alterBuild')) { + $plugin->alterBuild($build); + } + // Don't run in ::buildBlock() to ensure cache keys can be altered. If an // alter hook wants to modify the block contents, it can append another // #pre_render hook. diff --git a/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php b/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php index a86fccc..77c354a 100644 --- a/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php +++ b/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php @@ -187,10 +187,30 @@ public function build() { usleep(2000*1000); // Bubble up that this is uncacheable. +/* $build['site_slogan']['#cache']['max-age'] = 0; $build['site_slogan']['#cache']['contexts'] = ['user']; $build['site_slogan']['#debug'] = TRUE; +*/ + $build['site_slogan']['#cache']['contexts'] = ['user']; + +// $build['site_slogan']['dynamic'] = [ '#markup' => '
It is: ' . time(NULL) . '
' ]; + $build['site_slogan']['dynamic'] = [ '#pre_render_cache' => [get_class($this) . '::buildDynamic' => []] ]; + $build['site_slogan']['dynamic']['#cache']['max-age'] = 0; + + // This is a stage, where a placeholder can be created. + $build['site_slogan']['dynamic']['#cache']['keys'] = [ 'fabian', 'site-branding-dynamic' ]; + + return $build; + } + + public function alterBuild(&$build) { + $build['#cache_placeholder'] = TRUE; + } + public static function buildDynamic($build, $context) { + $build['#markup'] = '
It is: ' . time(NULL) . '
'; + usleep(500*1000); return $build; }