Mercurial > hg > isophonics-drupal-site
view core/lib/Drupal/Core/Render/PlaceholderingRenderCache.php @ 13:5fb285c0d0e3
Update Drupal core to 8.4.7 via Composer. Security update; I *think* we've
been lucky to get away with this so far, as we don't support self-registration
which seems to be used by the so-called "drupalgeddon 2" attack that 8.4.5
was vulnerable to.
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:33:26 +0100 |
parents | 4c8ae668cc8c |
children |
line wrap: on
line source
<?php 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) { // @todo remove this check when https://www.drupal.org/node/2367555 lands. if (!$this->requestStack->getCurrentRequest()->isMethodCacheable()) { return FALSE; } // 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); // @todo remove this check when https://www.drupal.org/node/2367555 lands. if (!$this->requestStack->getCurrentRequest()->isMethodCacheable()) { return FALSE; } 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; } }