Created
February 18, 2015 15:02
-
-
Save LionsAd/e8ffd028a3dde63b0492 to your computer and use it in GitHub Desktop.
SmartPlaceholderCache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/core/lib/Drupal/Core/Render/BubbleableMetadata.php b/core/lib/Drupal/Core/Render/BubbleableMetadata.php | |
index c56b421..748bc0b 100644 | |
--- a/core/lib/Drupal/Core/Render/BubbleableMetadata.php | |
+++ b/core/lib/Drupal/Core/Render/BubbleableMetadata.php | |
@@ -39,6 +39,13 @@ class BubbleableMetadata { | |
protected $postRenderCache; | |
/** | |
+ * #placeholder metadata. | |
+ * | |
+ * @var array[] | |
+ */ | |
+ protected $placeholders; | |
+ | |
+ /** | |
* Constructs a BubbleableMetadata value object. | |
* | |
* @param string[] $tags | |
@@ -47,9 +54,12 @@ class BubbleableMetadata { | |
* An array of attached assets. | |
* @param array $post_render_cache | |
* An array of #post_render_cache metadata. | |
+ * @param array $placeholders | |
+ * An array of #cache placeholders metadata. | |
*/ | |
- public function __construct(array $tags = [], array $attached = [], array $post_render_cache = []) { | |
+ public function __construct(array $tags = [], array $attached = [], array $post_render_cache = [], array $placeholders = []) { | |
$this->tags = $tags; | |
+ $this->placeholders = $placeholders; | |
$this->attached = $attached; | |
$this->postRenderCache = $post_render_cache; | |
} | |
@@ -70,6 +80,7 @@ public function __construct(array $tags = [], array $attached = [], array $post_ | |
public function merge(BubbleableMetadata $other) { | |
$result = new BubbleableMetadata(); | |
$result->tags = Cache::mergeTags($this->tags, $other->tags); | |
+ $result->placeholders = NestedArray::mergeDeep($this->placeholders, $other->placeholders); | |
$result->attached = drupal_merge_attached($this->attached, $other->attached); | |
$result->postRenderCache = NestedArray::mergeDeep($this->postRenderCache, $other->postRenderCache); | |
return $result; | |
@@ -83,6 +94,7 @@ public function merge(BubbleableMetadata $other) { | |
*/ | |
public function applyTo(array &$build) { | |
$build['#cache']['tags'] = $this->tags; | |
+ $build['#cache']['placeholders'] = $this->placeholders; | |
$build['#attached'] = $this->attached; | |
$build['#post_render_cache'] = $this->postRenderCache; | |
} | |
@@ -98,6 +110,7 @@ public function applyTo(array &$build) { | |
public static function createFromRenderArray(array $build) { | |
$meta = new static(); | |
$meta->tags = (isset($build['#cache']['tags'])) ? $build['#cache']['tags'] : []; | |
+ $meta->placeholders = (isset($build['#cache']['placeholders'])) ? $build['#cache']['placeholders'] : []; | |
$meta->attached = (isset($build['#attached'])) ? $build['#attached'] : []; | |
$meta->postRenderCache = (isset($build['#post_render_cache'])) ? $build['#post_render_cache'] : []; | |
return $meta; | |
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php | |
index 69e99a0..d280cab 100644 | |
--- a/core/lib/Drupal/Core/Render/Renderer.php | |
+++ b/core/lib/Drupal/Core/Render/Renderer.php | |
@@ -64,6 +64,11 @@ class Renderer implements RendererInterface { | |
protected $cacheContexts; | |
/** | |
+ * The stored placeholders. | |
+ */ | |
+ protected static $cachePlaceholders; | |
+ | |
+ /** | |
* The stack containing bubbleable rendering metadata. | |
* | |
* @var \SplStack|null | |
@@ -184,6 +189,11 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
$this->bubbleStack(); | |
return $elements['#markup']; | |
} | |
+ | |
+ // Store the cache ID for later comparison. | |
+ if ($this->requestStack->getCurrentRequest()->isMethodSafe() && $cid = $this->createCacheID($elements)) { | |
+ $elements['#cache_old_id'] = $cid; | |
+ } | |
} | |
// If the default values for this element have not been loaded yet, populate | |
@@ -206,6 +216,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
// Defaults for bubbleable rendering metadata. | |
$elements['#cache']['tags'] = isset($elements['#cache']['tags']) ? $elements['#cache']['tags'] : array(); | |
+ $elements['#cache']['placeholders'] = isset($elements['#cache']['placeholders']) ? $elements['#cache']['placeholders'] : array(); | |
$elements['#attached'] = isset($elements['#attached']) ? $elements['#attached'] : array(); | |
$elements['#post_render_cache'] = isset($elements['#post_render_cache']) ? $elements['#post_render_cache'] : array(); | |
@@ -339,6 +350,11 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
$suffix = isset($elements['#suffix']) ? SafeMarkup::checkAdminXss($elements['#suffix']) : ''; | |
$elements['#markup'] = $prefix . $elements['#children'] . $suffix; | |
+ | |
+ // @todo This means the root call element should not have #cache to be cached properly. | |
+ if ($is_root_call) { | |
+ $this->processPlaceholders($elements); | |
+ } | |
// We've rendered this element (and its subtree!), now update the stack. | |
$this->updateStack($elements); | |
@@ -394,6 +410,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
*/ | |
protected function resetStack() { | |
static::$stack = NULL; | |
+ static::$cachePlaceholders = NULL; | |
} | |
/** | |
@@ -468,6 +485,17 @@ protected function processPostRenderCache(array &$elements) { | |
} | |
} | |
+ protected function processPlaceholders(array &$elements) { | |
+ if (isset($elements['#cache']['placeholders'])) { | |
+ foreach ($elements['#cache']['placeholders'] as $placeholder => $element) { | |
+ $data = static::$cachePlaceholders[$placeholder]; | |
+ // This is already rendered, but we want to bubble the data up to $elements. | |
+ $markup = $this->doRender($data); | |
+ $elements['#markup'] = str_replace($placeholder, $markup, $elements['#markup']); | |
+ } | |
+ } | |
+ } | |
+ | |
/** | |
* Gets the cached, prerendered element of a renderable element from the cache. | |
* | |
@@ -493,12 +521,45 @@ protected function cacheGet(array $elements) { | |
if (!empty($cid) && $cache = $this->cacheFactory->get($bin)->get($cid)) { | |
$cached_element = $cache->data; | |
+ if (!empty($cached_element['#cache']['placeholders']) && !$this->checkPlaceholders($cached_element['#cache']['placeholders'])) { | |
+ return FALSE; | |
+ } | |
+ | |
// Return the cached element. | |
return $cached_element; | |
} | |
return FALSE; | |
} | |
+ protected function checkPlaceholders($placeholders) { | |
+ $cid_map = []; | |
+ foreach ($placeholders as $placeholder => $elements) { | |
+ $cid_map[$placeholder] = $this->createCacheID($elements); | |
+ } | |
+ $cids = array_keys($cid_map); | |
+ | |
+ // @todo do this per 'bin'. | |
+ $cached = $this->cacheFactory->get('render')->getMultiple($cids); | |
+ | |
+ // A cache miss is fatal. | |
+ if (count($cached) != count($cid_map)) { | |
+ return FALSE; | |
+ } | |
+ | |
+ $data = array(); | |
+ // Store in this class - unless reset. | |
+ foreach ($cid_map as $placeholder => $cid) { | |
+ $cached_element = $cached[$cid]->data; | |
+ if (!empty($cached_element['#cache']['placeholders']) && !$this->checkPlaceholders($cached_element['#cache']['placeholders'])) { | |
+ return FALSE; | |
+ } | |
+ $data[$placeholder] = $cached_element; | |
+ } | |
+ static::$cachePlaceholders = array_merge(static::$cachePlaceholders, $data); | |
+ | |
+ return TRUE; | |
+ } | |
+ | |
/** | |
* Caches the rendered output of a renderable element. | |
* | |
@@ -530,7 +591,19 @@ protected function cacheSet(array &$elements) { | |
$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']); | |
+ $cache = $this->cacheFactory->get($bin); | |
+ | |
+ // This assumes bubbling worked correctly and createCacheID() working on contexts instead of cache keys. | |
+ if (isset($elements['#cache_old_id']) && $cid != $elements['#cache_old_id']) { | |
+ $cache_old_id = $elements['#cache_old_id']; | |
+ $placeholder = '<!-- PLACEHOLDER: ' . $cache_old_id . ' -->'; | |
+ | |
+ $elements = [ '#markup' => $placeholder ]; | |
+ $elements['#cache']['placeholders'][$placeholder] = [ '#cache' => $elements['#cache'] ]; | |
+ $this->cacheFactory->get('render')->set($cache_old_id, $elements, $expire, $data['#cache']['tags']); | |
+ static::$cachePlaceholders[$placeholder] = $data; | |
+ } | |
+ $cache->set($cid, $data, $expire, $data['#cache']['tags']); | |
} | |
/** | |
@@ -569,6 +642,7 @@ public function getCacheableRenderArray(array $elements) { | |
'#post_render_cache' => $elements['#post_render_cache'], | |
'#cache' => [ | |
'tags' => $elements['#cache']['tags'], | |
+ 'placeholders' => $elements['#cache']['placeholders'], | |
], | |
]; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment