diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index 8de7ebe..c539e3a 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -198,27 +198,9 @@ protected function doRender(&$elements, $is_root_call = FALSE) { // @see ::cacheSet() $pre_bubbling_elements = $elements; - if (isset($elements['#cache']['keys']) && $this->requestStack->getCurrentRequest()->isMethodSafe()) { + if (isset($elements['#cache']['keys'])) { $cached_element = $this->cacheGet($elements); - // Is this placeholder eligible because it is either: - // - Cached and therefore cache retrievable? - // - Or has a #pre_render_cache callback? - if (!isset($elements['#cache_no_placeholder']) && ($cached_element || isset($pre_bubbling_elements['#pre_render_cache']))) { - // Do we _need_ to create a placeholder? - if (isset($elements['#cache']['max-age']) && $elements['#cache']['max-age'] == 0) { - $elements = []; - // @todo Add helper function for this based on the render array. - // @todo Use hash of $pre_bubbling_elements['#pre_render_cache','#cache']? - $pholder_id = \Drupal\Component\Utility\Html::getId('X-FF-PH--' . implode(':', $pre_bubbling_elements['#cache']['keys'])); - $elements['#markup'] = '
'; - $elements['#cache']['placeholders'][$pholder_id] = $pre_bubbling_elements; - if ($cached_element) { - // @todo If we already have data, then store it in the renderer. - } - } - } - if ($cached_element !== FALSE) { $elements = $cached_element; // Only when we're not in a root (non-recursive) drupal_render() call, @@ -573,6 +555,8 @@ protected function processPlaceholders(array &$elements) { * * @param array $elements * A renderable array. + * @param bool $is_recursive + * Whether this function was called recursively. * * @return array * A renderable array, with the original element and all its children pre- @@ -581,15 +565,18 @@ protected function processPlaceholders(array &$elements) { * @see ::render() * @see ::saveToCache() */ - protected function cacheGet(array $elements) { + protected function cacheGet(array $elements, $is_recursive = FALSE) { // 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)) { + if (!$this->requestStack->getCurrentRequest()->isMethodSafe()) { return FALSE; } + + $cid = $this->createCacheID($elements); $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render'; + $cached_element = FALSE; if (!empty($cid) && ($cache_bin = $this->cacheFactory->get($bin)) && $cache = $cache_bin->get($cid)) { $cached_element = $cache->data; @@ -597,12 +584,69 @@ protected function cacheGet(array $elements) { // @see ::doRender() // @see ::cacheSet() if (isset($cached_element['#cache_redirect'])) { - return $this->cacheGet($cached_element); + $cache_redirect = $cached_element; + $cached_element = $this->cacheGet($cached_element, TRUE); } - // Return the cached element. + } + + // 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. + // In all those case we want to return a placeholder instead of the element. + // This should be considered a cache hit by the renderer. + + // If this is a recursive call or has a no-placeholder tag, return what we have now. + if ($is_recursive || isset($elements['#cache_no_placeholder'])) { return $cached_element; } - return FALSE; + + // If the data is not cached and not re-creatable, return. + // Is this placeholder eligible because it is either: + // - Cached and therefore cache retrievable? + // - Or has a #pre_render_cache callback? + if (!$cached_element && !isset($elements['#pre_render_cache'])) { + return $cached_element; + } + + // Do we need to create a placeholder? + if (!empty($cache_redirect['#cache_placeholder']) || (isset($elements['#cache']['max-age']) && $elements['#cache']['max-age'] == 0)) { + $elements['#cache_placeholder'] = TRUE; + } + + // Are we asked to create a placeholder? + if (!empty($elements['#cache_placeholder'])) { + return $this->createRenderCacheArrayPlaceholder($elements, $cached_element); + } + + // Return the cached element. + return $cached_element; + } + + /** + * Create a render cache array placeholder. + * + * @param array $elements + * The elements to create a placeholder for. + * @param array|null|false $data + * Already retrieved render array data that can be used for replacing the + * placeholder in this request. + * + * @return array + * The render array placeholder. + */ + protected function createRenderCacheArrayPlaceholder(array $elements, $data = FALSE) { + $render_cache_array = array_intersect_key($elements, ['#cache' => 0, '#pre_render_cache' => 1]); + + // @todo Use hash of $pre_bubbling_elements['#pre_render_cache','#cache']? + $pholder_id = \Drupal\Component\Utility\Html::getId('X-FF-PH--' . implode(':', $render_cache_array['#cache']['keys'])); + + $placeholder = []; + $placeholder['#markup'] = '
'; + $placeholder['#cache']['placeholders'][$pholder_id] = $render_cache_array; + if ($data) { + // @todo If we already have data, then store it in the renderer. + } + return $placeholder; } /**