Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
20.00% |
1 / 5 |
CRAP | |
46.15% |
12 / 26 |
PlaceholderingRenderCache | |
0.00% |
0 / 1 |
|
20.00% |
1 / 5 |
44.60 | |
46.15% |
12 / 26 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
get | |
0.00% |
0 / 1 |
10.14 | |
60.00% |
6 / 10 |
|||
set | |
0.00% |
0 / 1 |
3.14 | |
75.00% |
3 / 4 |
|||
createPlaceholderAndRemember | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
getFromPlaceholderResultsCache | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Render\PlaceholderingRenderCache. | |
*/ | |
namespace Drupal\Core\Render; | |
use Drupal\Core\Cache\CacheFactoryInterface; | |
use Drupal\Core\Cache\Context\CacheContextsManager; | |
use Symfony\Component\HttpFoundation\RequestStack; | |
/** | |
* Adds automatic placeholdering to the RenderCache. | |
* | |
* This automatic placeholdering is performed to ensure the containing elements | |
* and overarching response are as cacheable as possible. Elements whose subtree | |
* bubble either max-age=0 or high-cardinality cache contexts (such as 'user' | |
* and 'session') are considered poorly cacheable. | |
* | |
* @see sites/default/default.services.yml | |
* | |
* Automatic placeholdering is performed only on elements whose subtree was | |
* generated using a #lazy_builder callback and whose bubbled cacheability meets | |
* the auto-placeholdering conditions as configured in the renderer.config | |
* container parameter. | |
* | |
* This RenderCache implementation automatically replaces an element with a | |
* placeholder: | |
* - on render cache hit, i.e. ::get() | |
* - on render cache miss, i.e. ::set() (in subsequent requests, it will be a | |
* cache hit) | |
* | |
* In either case, the render cache is guaranteed to contain the to-be-rendered | |
* placeholder, so replacing (rendering) the placeholder will be very fast. | |
* | |
* Finally, in case the render cache item disappears between the time it is | |
* decided to automatically placeholder the element and the time where the | |
* placeholder is replaced (rendered), that is guaranteed to not be problematic. | |
* Because this only automatically placeholders elements that have a | |
* #lazy_builder callback set, which means that in the worst case, it will need | |
* to be re-rendered. | |
*/ | |
class PlaceholderingRenderCache extends RenderCache { | |
/** | |
* The placeholder generator. | |
* | |
* @var \Drupal\Core\Render\PlaceholderGeneratorInterface | |
*/ | |
protected $placeholderGenerator; | |
/** | |
* Stores rendered results for automatically placeholdered elements. | |
* | |
* This allows us to avoid talking to the cache twice per auto-placeholdered | |
* element, or in case of an uncacheable element, to render it twice. | |
* | |
* Scenario A. The double cache read would happen because: | |
* 1. when rendering, cache read, but auto-placeholdered | |
* 2. when rendering placeholders, again cache read | |
* | |
* Scenario B. The cache write plus read would happen because: | |
* 1. when rendering, cache write, but auto-placeholdered | |
* 2. when rendering placeholders, cache read | |
* | |
* Scenario C. The double rendering for an uncacheable element would happen because: | |
* 1. when rendering, not cacheable, but auto-placeholdered | |
* 2. when rendering placeholders, rendered again | |
* | |
* In all three scenarios, this static cache avoids the second step, thus | |
* avoiding expensive work. | |
* | |
* @var array | |
*/ | |
protected $placeholderResultsCache = []; | |
/** | |
* Constructs a new PlaceholderingRenderCache object. | |
* | |
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack | |
* The request stack. | |
* @param \Drupal\Core\Cache\CacheFactoryInterface $cache_factory | |
* The cache factory. | |
* @param \Drupal\Core\Cache\Context\CacheContextsManager $cache_contexts_manager | |
* The cache contexts manager. | |
* @param \Drupal\Core\Render\PlaceholderGeneratorInterface $placeholder_generator | |
* The placeholder generator. | |
*/ | |
public function __construct(RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContextsManager $cache_contexts_manager, PlaceholderGeneratorInterface $placeholder_generator) { | |
parent::__construct($request_stack, $cache_factory, $cache_contexts_manager); | |
$this->placeholderGenerator = $placeholder_generator; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function get(array $elements) { | |
// When rendering placeholders, special case auto-placeholdered elements: | |
// avoid retrieving them from cache again, or rendering them again. | |
if (isset($elements['#create_placeholder']) && $elements['#create_placeholder'] === FALSE) { | |
$cached_placeholder_result = $this->getFromPlaceholderResultsCache($elements); | |
if ($cached_placeholder_result !== FALSE) { | |
return $cached_placeholder_result; | |
} | |
} | |
$cached_element = parent::get($elements); | |
if ($cached_element === FALSE) { | |
return FALSE; | |
} | |
else { | |
if ($this->placeholderGenerator->canCreatePlaceholder($elements) && $this->placeholderGenerator->shouldAutomaticallyPlaceholder($cached_element)) { | |
return $this->createPlaceholderAndRemember($cached_element, $elements); | |
} | |
return $cached_element; | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function set(array &$elements, array $pre_bubbling_elements) { | |
$result = parent::set($elements, $pre_bubbling_elements); | |
if ($this->placeholderGenerator->canCreatePlaceholder($pre_bubbling_elements) && $this->placeholderGenerator->shouldAutomaticallyPlaceholder($elements)) { | |
// Overwrite $elements with a placeholder. The Renderer (which called this | |
// method) will update the context with the bubbleable metadata of the | |
// overwritten $elements. | |
$elements = $this->createPlaceholderAndRemember($this->getCacheableRenderArray($elements), $pre_bubbling_elements); | |
} | |
return $result; | |
} | |
/** | |
* Create a placeholder for a renderable array and remember in a static cache. | |
* | |
* @param array $rendered_elements | |
* A fully rendered renderable array. | |
* @param array $pre_bubbling_elements | |
* A renderable array corresponding to the state (in particular, the | |
* cacheability metadata) of $rendered_elements prior to the beginning of | |
* its rendering process, and therefore before any bubbling of child | |
* information has taken place. Only the #cache property is used by this | |
* function, so the caller may omit all other properties and children from | |
* this array. | |
* | |
* @return array | |
* Renderable array with placeholder markup and the attached placeholder | |
* replacement metadata. | |
*/ | |
protected function createPlaceholderAndRemember(array $rendered_elements, array $pre_bubbling_elements) { | |
$placeholder_element = $this->placeholderGenerator->createPlaceholder($pre_bubbling_elements); | |
// Remember the result for this placeholder to avoid double work. | |
$placeholder = (string) $placeholder_element['#markup']; | |
$this->placeholderResultsCache[$placeholder] = $rendered_elements; | |
return $placeholder_element; | |
} | |
/** | |
* Retrieves an auto-placeholdered renderable array from the static cache. | |
* | |
* @param array $elements | |
* A renderable array. | |
* | |
* @return array|false | |
* A renderable array, with the original element and all its children pre- | |
* rendered, or FALSE if no cached copy of the element is available. | |
*/ | |
protected function getFromPlaceholderResultsCache(array $elements) { | |
$placeholder_element = $this->placeholderGenerator->createPlaceholder($elements); | |
$placeholder = (string) $placeholder_element['#markup']; | |
if (isset($this->placeholderResultsCache[$placeholder])) { | |
return $this->placeholderResultsCache[$placeholder]; | |
} | |
return FALSE; | |
} | |
} |