Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Core/Render/PlaceholderingRenderCache.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Core\Render; | |
4 | |
5 use Drupal\Core\Cache\CacheFactoryInterface; | |
6 use Drupal\Core\Cache\Context\CacheContextsManager; | |
7 use Symfony\Component\HttpFoundation\RequestStack; | |
8 | |
9 /** | |
10 * Adds automatic placeholdering to the RenderCache. | |
11 * | |
12 * This automatic placeholdering is performed to ensure the containing elements | |
13 * and overarching response are as cacheable as possible. Elements whose subtree | |
14 * bubble either max-age=0 or high-cardinality cache contexts (such as 'user' | |
15 * and 'session') are considered poorly cacheable. | |
16 * | |
17 * @see sites/default/default.services.yml | |
18 * | |
19 * Automatic placeholdering is performed only on elements whose subtree was | |
20 * generated using a #lazy_builder callback and whose bubbled cacheability meets | |
21 * the auto-placeholdering conditions as configured in the renderer.config | |
22 * container parameter. | |
23 * | |
24 * This RenderCache implementation automatically replaces an element with a | |
25 * placeholder: | |
26 * - on render cache hit, i.e. ::get() | |
27 * - on render cache miss, i.e. ::set() (in subsequent requests, it will be a | |
28 * cache hit) | |
29 * | |
30 * In either case, the render cache is guaranteed to contain the to-be-rendered | |
31 * placeholder, so replacing (rendering) the placeholder will be very fast. | |
32 * | |
33 * Finally, in case the render cache item disappears between the time it is | |
34 * decided to automatically placeholder the element and the time where the | |
35 * placeholder is replaced (rendered), that is guaranteed to not be problematic. | |
36 * Because this only automatically placeholders elements that have a | |
37 * #lazy_builder callback set, which means that in the worst case, it will need | |
38 * to be re-rendered. | |
39 */ | |
40 class PlaceholderingRenderCache extends RenderCache { | |
41 | |
42 /** | |
43 * The placeholder generator. | |
44 * | |
45 * @var \Drupal\Core\Render\PlaceholderGeneratorInterface | |
46 */ | |
47 protected $placeholderGenerator; | |
48 | |
49 /** | |
50 * Stores rendered results for automatically placeholdered elements. | |
51 * | |
52 * This allows us to avoid talking to the cache twice per auto-placeholdered | |
53 * element, or in case of an uncacheable element, to render it twice. | |
54 * | |
55 * Scenario A. The double cache read would happen because: | |
56 * 1. when rendering, cache read, but auto-placeholdered | |
57 * 2. when rendering placeholders, again cache read | |
58 * | |
59 * Scenario B. The cache write plus read would happen because: | |
60 * 1. when rendering, cache write, but auto-placeholdered | |
61 * 2. when rendering placeholders, cache read | |
62 * | |
63 * Scenario C. The double rendering for an uncacheable element would happen because: | |
64 * 1. when rendering, not cacheable, but auto-placeholdered | |
65 * 2. when rendering placeholders, rendered again | |
66 * | |
67 * In all three scenarios, this static cache avoids the second step, thus | |
68 * avoiding expensive work. | |
69 * | |
70 * @var array | |
71 */ | |
72 protected $placeholderResultsCache = []; | |
73 | |
74 /** | |
75 * Constructs a new PlaceholderingRenderCache object. | |
76 * | |
77 * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack | |
78 * The request stack. | |
79 * @param \Drupal\Core\Cache\CacheFactoryInterface $cache_factory | |
80 * The cache factory. | |
81 * @param \Drupal\Core\Cache\Context\CacheContextsManager $cache_contexts_manager | |
82 * The cache contexts manager. | |
83 * @param \Drupal\Core\Render\PlaceholderGeneratorInterface $placeholder_generator | |
84 * The placeholder generator. | |
85 */ | |
86 public function __construct(RequestStack $request_stack, CacheFactoryInterface $cache_factory, CacheContextsManager $cache_contexts_manager, PlaceholderGeneratorInterface $placeholder_generator) { | |
87 parent::__construct($request_stack, $cache_factory, $cache_contexts_manager); | |
88 $this->placeholderGenerator = $placeholder_generator; | |
89 } | |
90 | |
91 /** | |
92 * {@inheritdoc} | |
93 */ | |
94 public function get(array $elements) { | |
95 // @todo remove this check when https://www.drupal.org/node/2367555 lands. | |
96 if (!$this->requestStack->getCurrentRequest()->isMethodCacheable()) { | |
97 return FALSE; | |
98 } | |
99 | |
100 // When rendering placeholders, special case auto-placeholdered elements: | |
101 // avoid retrieving them from cache again, or rendering them again. | |
102 if (isset($elements['#create_placeholder']) && $elements['#create_placeholder'] === FALSE) { | |
103 $cached_placeholder_result = $this->getFromPlaceholderResultsCache($elements); | |
104 if ($cached_placeholder_result !== FALSE) { | |
105 return $cached_placeholder_result; | |
106 } | |
107 } | |
108 | |
109 $cached_element = parent::get($elements); | |
110 | |
111 if ($cached_element === FALSE) { | |
112 return FALSE; | |
113 } | |
114 else { | |
115 if ($this->placeholderGenerator->canCreatePlaceholder($elements) && $this->placeholderGenerator->shouldAutomaticallyPlaceholder($cached_element)) { | |
116 return $this->createPlaceholderAndRemember($cached_element, $elements); | |
117 } | |
118 | |
119 return $cached_element; | |
120 } | |
121 } | |
122 | |
123 /** | |
124 * {@inheritdoc} | |
125 */ | |
126 public function set(array &$elements, array $pre_bubbling_elements) { | |
127 $result = parent::set($elements, $pre_bubbling_elements); | |
128 | |
129 // @todo remove this check when https://www.drupal.org/node/2367555 lands. | |
130 if (!$this->requestStack->getCurrentRequest()->isMethodCacheable()) { | |
131 return FALSE; | |
132 } | |
133 | |
134 if ($this->placeholderGenerator->canCreatePlaceholder($pre_bubbling_elements) && $this->placeholderGenerator->shouldAutomaticallyPlaceholder($elements)) { | |
135 // Overwrite $elements with a placeholder. The Renderer (which called this | |
136 // method) will update the context with the bubbleable metadata of the | |
137 // overwritten $elements. | |
138 $elements = $this->createPlaceholderAndRemember($this->getCacheableRenderArray($elements), $pre_bubbling_elements); | |
139 } | |
140 | |
141 return $result; | |
142 } | |
143 | |
144 /** | |
145 * Create a placeholder for a renderable array and remember in a static cache. | |
146 * | |
147 * @param array $rendered_elements | |
148 * A fully rendered renderable array. | |
149 * @param array $pre_bubbling_elements | |
150 * A renderable array corresponding to the state (in particular, the | |
151 * cacheability metadata) of $rendered_elements prior to the beginning of | |
152 * its rendering process, and therefore before any bubbling of child | |
153 * information has taken place. Only the #cache property is used by this | |
154 * function, so the caller may omit all other properties and children from | |
155 * this array. | |
156 * | |
157 * @return array | |
158 * Renderable array with placeholder markup and the attached placeholder | |
159 * replacement metadata. | |
160 */ | |
161 protected function createPlaceholderAndRemember(array $rendered_elements, array $pre_bubbling_elements) { | |
162 $placeholder_element = $this->placeholderGenerator->createPlaceholder($pre_bubbling_elements); | |
163 // Remember the result for this placeholder to avoid double work. | |
164 $placeholder = (string) $placeholder_element['#markup']; | |
165 $this->placeholderResultsCache[$placeholder] = $rendered_elements; | |
166 return $placeholder_element; | |
167 } | |
168 | |
169 /** | |
170 * Retrieves an auto-placeholdered renderable array from the static cache. | |
171 * | |
172 * @param array $elements | |
173 * A renderable array. | |
174 * | |
175 * @return array|false | |
176 * A renderable array, with the original element and all its children pre- | |
177 * rendered, or FALSE if no cached copy of the element is available. | |
178 */ | |
179 protected function getFromPlaceholderResultsCache(array $elements) { | |
180 $placeholder_element = $this->placeholderGenerator->createPlaceholder($elements); | |
181 $placeholder = (string) $placeholder_element['#markup']; | |
182 if (isset($this->placeholderResultsCache[$placeholder])) { | |
183 return $this->placeholderResultsCache[$placeholder]; | |
184 } | |
185 return FALSE; | |
186 } | |
187 | |
188 } |