Newer
Older
Alex Pott
committed
<?php
namespace Drupal\Core\Render;
/**
* Defines an interface for turning a render array into a string.
*/
interface RendererInterface {
/**
* Renders final HTML given a structured array tree.
*
* Calls ::render() in such a way that placeholders are replaced.
Alex Pott
committed
*
* Should therefore only be used in occasions where the final rendering is
* happening, just before sending a Response:
* - system internals that are responsible for rendering the final HTML
* - render arrays for non-HTML responses, such as feeds
*
Alex Pott
committed
* (Cannot be executed within another render context.)
*
Alex Pott
committed
* @param array $elements
* The structured array describing the data to be rendered.
*
Alex Bronstein
committed
* @return \Drupal\Component\Render\MarkupInterface
Alex Pott
committed
* The rendered HTML.
*
Alex Pott
committed
* @throws \LogicException
* When called from inside another renderRoot() call.
* @see \Drupal\Core\Render\RendererInterface::render()
Alex Pott
committed
*/
public function renderRoot(&$elements);
/**
* Renders final HTML in situations where no assets are needed.
*
* Calls ::render() in such a way that placeholders are replaced.
*
Jennifer Hodgdon
committed
* Useful for instance when rendering the values of tokens or emails, which
* need a render array being turned into a string, but do not need any of the
* bubbleable metadata (the attached assets and cache tags).
*
* Some of these are a relatively common use case and happen *within* a
* ::renderRoot() call, but that is generally highly problematic (and hence an
* exception is thrown when a ::renderRoot() call happens within another
* ::renderRoot() call). However, in this case, we only care about the output,
Alex Pott
committed
* not about the bubbling. Hence this uses a separate render context, to not
* affect the parent ::renderRoot() call.
*
Alex Pott
committed
* (Can be executed within another render context: it runs in isolation.)
*
* @param array $elements
* The structured array describing the data to be rendered.
*
Alex Bronstein
committed
* @return \Drupal\Component\Render\MarkupInterface
* The rendered HTML.
*
* @see \Drupal\Core\Render\RendererInterface::renderRoot()
* @see \Drupal\Core\Render\RendererInterface::render()
*/
public function renderInIsolation(&$elements);
/**
* @deprecated in drupal:10.3.0 and is removed from drupal:12.0.0. Use
* \Drupal\Core\Render\RendererInterface::renderInIsolation() instead.
*
* @see https://www.drupal.org/node/3407994
*/
public function renderPlain(&$elements);
/**
* Renders final HTML for a placeholder.
*
* Renders the placeholder in isolation.
*
* @param string $placeholder
* An attached placeholder to render. (This must be a key of one of the
* values of $elements['#attached']['placeholders'].)
* @param array $elements
* The structured array describing the data to be rendered.
*
* @return array
* The updated $elements.
*
* @see \Drupal\Core\Render\RendererInterface::render()
*/
public function renderPlaceholder($placeholder, array $elements);
Alex Pott
committed
/**
* Renders HTML given a structured array tree.
*
* Renderable arrays have two kinds of key/value pairs: properties and
* children. Properties have keys starting with '#' and their values influence
* how the array will be rendered. Children are all elements whose keys do not
* start with a '#'. Their values should be renderable arrays themselves,
* which will be rendered during the rendering of the parent array. The markup
* provided by the children is typically inserted into the markup generated by
* the parent array.
*
* An important aspect of rendering is caching the result, when appropriate.
* Because the HTML of a rendered item includes all of the HTML of the
* rendered children, caching it requires certain information to bubble up
* from child elements to their parents:
* - Cache contexts, so that the render cache is varied by every context that
* affects the rendered HTML. Because cache contexts affect the cache ID,
* and therefore must be resolved for cache hits as well as misses, it is
* up to the implementation of this interface to decide how to implement
* the caching of items whose children specify cache contexts not directly
* specified by the parent. \Drupal\Core\Render\Renderer implements this
* with a lazy two-tier caching strategy. Alternate strategies could be to
* not cache such parents at all or to cache them with the child elements
* replaced by placeholder tokens that are dynamically rendered after cache
* retrieval.
* - Cache tags, so that cached renderings are invalidated when site content
* or configuration that can affect that rendering changes.
* - Placeholders, with associated self-contained placeholder render arrays,
* for executing code to handle dynamic requirements that cannot be cached.
Alex Pott
committed
* A render context (\Drupal\Core\Render\RenderContext) can be used to perform
* bubbling; it is a stack of \Drupal\Core\Render\BubbleableMetadata objects.
*
* Additionally, whether retrieving from cache or not, it is important to
* know all of the assets (CSS and JavaScript) required by the rendered HTML,
* and this must also bubble from child to parent. Therefore,
* \Drupal\Core\Render\BubbleableMetadata includes that data as well.
Alex Pott
committed
*
* The process of rendering an element is recursive unless the element defines
* an implemented theme hook in #theme. During each call to
* Renderer::render(), the outermost renderable array (also known as an
* "element") is processed using the following steps:
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
* - If this element has already been printed (#printed = TRUE) or the user
* does not have access to it (#access = FALSE), then an empty string is
* returned.
* - If no render context is set yet, an exception is thrown. Otherwise,
* an empty \Drupal\Core\Render\BubbleableMetadata is pushed onto the
* render context.
* - If this element has #cache defined then the cached markup for this
* element will be returned if it exists in Renderer::render()'s cache. To
* use Renderer::render() caching, set the element's #cache property to an
* associative array with one or several of the following keys:
* - 'keys': An array of one or more keys that identify the element. If
* 'keys' is set, the cache ID is created automatically from these keys.
* - 'contexts': An array of one or more cache context IDs. These are
* converted to a final value depending on the request. (For instance,
* 'user' is mapped to the current user's ID.)
* - 'max-age': A time in seconds. Zero seconds means it is not cacheable.
* \Drupal\Core\Cache\Cache::PERMANENT means it is cacheable forever.
* - 'bin': Specify a cache bin to cache the element in. Default is
* 'default'.
* When there is a render cache hit, there is no rendering work left to be
* done, so the stack must be updated. The empty (and topmost) frame that
* was just pushed onto the stack is updated with all bubbleable rendering
* metadata from the element retrieved from render cache. Then, this stack
* frame is bubbled: the two topmost frames are popped from the stack,
* they are merged, and the result is pushed back onto the stack.
* However, also in case of a cache miss we have to do something. Note
* that a Renderer renders top-down, which means that we try to render a
* parent first, and we try to avoid the work of rendering the children by
* using the render cache. Though in this case, we are dealing with a
* cache miss. So a Renderer traverses down the tree, rendering all
* children. In doing so, the render stack is updated with the bubbleable
* metadata of the children. That means that once the children are
* rendered, we can render cache this element. But the cache ID may have
* *changed* at that point, because the children's cache contexts have
* been bubbled!
* It is for that case that we must store the current (pre-bubbling) cache
* ID, so that we can compare it with the new (post-bubbling) cache ID
* when writing to the cache. We store the current cache ID in
* $pre_bubbling_cid.
* - If this element has #type defined and the default attributes for this
* element have not already been merged in (#defaults_loaded = TRUE) then
* the defaults for this type of element, defined by an element plugin,
* are merged into the array. #defaults_loaded is set by functions that
* process render arrays and call the element info service before passing
* the array to Renderer::render(), such as form_builder() in the Form
* API.
* - If this element has #create_placeholder set to TRUE, and it has a
* #lazy_builder callback, then the element is replaced with another
* element that has only two properties: #markup and #attached. #markup
* will contain placeholder markup, and #attached contains the placeholder
* metadata, that will be used for replacing this placeholder. That
* metadata contains a very compact render array (containing only
* #lazy_builder and #cache) that will be rendered to replace the
* placeholder with its final markup. This means that when the
* #lazy_builder callback is called, it received a render array to add to
* that only contains #cache.
* - If this element has a #lazy_builder or an array of #pre_render
* functions defined, they are called sequentially to modify the element
* before rendering. #lazy_builder is preferred, since it allows for
* placeholdering (see previous step), but #pre_render is still supported.
* Both have their use case: #lazy_builder is for building a render array,
* #pre_render is for decorating an existing render array.
* After the #lazy_builder function is called, #lazy_builder is removed,
* and #built is set to TRUE.
* After the #lazy_builder and all #pre_render functions have been called,
* #printed is checked a second time in case a #lazy_builder or
* #pre_render function flags the element as printed. If #printed is set,
* we return early and hence no rendering work is left to be done,
* similarly to a render cache hit. Once again, the empty (and topmost)
* frame that was just pushed onto the stack is updated with all
* bubbleable rendering metadata from the element whose #printed = TRUE.
* Then, this stack frame is bubbled: the two topmost frames are popped
* from the stack, they are merged, and the result is pushed back onto the
* stack.
* - The child elements of this element are sorted by weight using uasort()
* in \Drupal\Core\Render\Element::children(). Since this is expensive,
* when passing already sorted elements to Renderer::render(), for example
* from a database query, set $elements['#sorted'] = TRUE to avoid sorting
* them a second time.
* - The main render phase to produce #children for this element takes
* place:
* - If this element has #theme defined and #theme is an implemented theme
* hook/suggestion then ThemeManagerInterface::render() is called and
* must render both the element and its children. If #render_children is
* set, ThemeManagerInterface::render() will not be called.
* #render_children is usually only set internally by
* ThemeManagerInterface::render() so that we can avoid the situation
* where Renderer::render() called from within a theme preprocess
* function creates an infinite loop.
* - If this element does not have a defined #theme, or the defined #theme
* hook is not implemented, or #render_children is set, then
* Renderer::render() is called recursively on each of the child
* elements of this element, and the result of each is concatenated onto
* #children. This is skipped if #children is not empty at this point.
* - Once #children has been rendered for this element, if #theme is not
* implemented and #markup is set for this element, #markup will be
* prepended to #children.
* - If this element has #states defined then JavaScript state information
* is added to this element's #attached attribute by
* \Drupal\Core\Form\FormHelper::processStates().
* - If this element has #attached defined then any required libraries,
* JavaScript, CSS, or other custom data are added to the current page by
* \Drupal\Core\Render\AttachmentsResponseProcessorInterface::processAttachments().
* - If this element has an array of #theme_wrappers defined and
* #render_children is not set, #children is then re-rendered by passing
* the element in its current state to ThemeManagerInterface::render()
* successively for each item in #theme_wrappers. Since #theme and
* #theme_wrappers hooks often define variables with the same names it is
* possible to explicitly override each attribute passed to each
* #theme_wrappers hook by setting the hook name as the key and an array
* of overrides as the value in #theme_wrappers array.
* For example, if we have a render element as follows:
* @code
* array(
* '#theme' => 'image',
* '#attributes' => array('class' => array('foo')),
* '#theme_wrappers' => array('container'),
* );
* @endcode
* and we need to pass the class 'bar' as an attribute for 'container', we
* can rewrite our element thus:
* @code
* array(
* '#theme' => 'image',
* '#attributes' => array('class' => array('foo')),
* '#theme_wrappers' => array(
* 'container' => array(
* '#attributes' => array('class' => array('bar')),
Alex Pott
committed
* ),
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
* ),
* );
* @endcode
* - If this element has an array of #post_render functions defined, they
* are called sequentially to modify the rendered #children. Unlike
* #pre_render functions, #post_render functions are passed both the
* rendered #children attribute as a string and the element itself.
* - If this element has #prefix and/or #suffix defined, they are
* concatenated to #children.
* - The rendering of this element is now complete. The next step will be
* render caching. So this is the perfect time to update the stack. At
* this point, children of this element (if any), have been rendered also,
* and if there were any, their bubbleable rendering metadata will have
* been bubbled up into the stack frame for the element that is currently
* being rendered. The render cache item for this element must contain the
* bubbleable rendering metadata for this element and all of its children.
* However, right now, the topmost stack frame (the one for this element)
* currently only contains the metadata for the children. Therefore, the
* topmost stack frame is updated with this element's metadata, and then
* the element's metadata is replaced with the metadata in the topmost
* stack frame. This element now contains all bubbleable rendering
* metadata for this element and all its children, so it's now ready for
* render caching.
* - If this element has #cache defined, the rendered output of this element
* is saved to Renderer::render()'s internal cache. This includes the
* changes made by #post_render.
* At the same time, if $pre_bubbling_cid is set, it is compared to the
* calculated cache ID. If they are different, then a redirecting cache
* item is created, containing the #cache metadata of the current element,
* and written to cache using the value of $pre_bubbling_cid as the cache
* ID. This ensures the pre-bubbling ("wrong") cache ID redirects to the
* post-bubbling ("right") cache ID.
* - If this element also has #cache_properties defined, all the array items
* matching the specified property names will be cached along with the
* element markup. If properties include children names, the system
* assumes only children's individual markup is relevant and ignores the
* parent markup. This approach is normally not needed and should be
* adopted only when dealing with very advanced use cases.
* - If this element has attached placeholders ([#attached][placeholders]),
* or any of its children has (which we would know thanks to the stack
* having been updated just before the render caching step), its
* placeholder element containing a #lazy_builder function is rendered in
* isolation. The resulting markup is used to replace the placeholder, and
* any bubbleable metadata is merged.
* Placeholders must be unique, to guarantee that for instance, samples of
* placeholders are not replaced as well.
* - Just before finishing the rendering of this element, this element's
* stack frame (the topmost one) is bubbled: the two topmost frames are
* popped from the stack, they are merged and the result is pushed back
* onto the stack.
* So if for instance this element was a child element, then a new frame
* was pushed onto the stack element at the beginning of rendering this
* element, it was updated when the rendering was completed, and now we
* merge it with the frame for the parent, so that the parent now has the
* bubbleable rendering metadata for its child.
* - #printed is set to TRUE for this element to ensure that it is only
* rendered once.
* - The final value of #children for this element is returned as the
* rendered output.
Alex Pott
committed
*
* @param array $elements
* The structured array describing the data to be rendered.
* @param bool $is_root_call
* (Internal use only.) Whether this is a recursive call or not. See
* ::renderRoot().
*
Alex Bronstein
committed
* @return \Drupal\Component\Render\MarkupInterface
Alex Pott
committed
* The rendered HTML.
*
* @throws \LogicException
* When called outside of a render context (i.e. outside of a renderRoot(),
* renderInIsolation() or executeInRenderContext() call).
Alex Pott
committed
* @throws \Exception
Alex Pott
committed
* If a #pre_render callback throws an exception, it is caught to mark the
* renderer as no longer being in a root render call, if any. Then the
* exception is rethrown.
Alex Pott
committed
*
* @see \Drupal\Core\Render\ElementInfoManagerInterface::getInfo()
* @see \Drupal\Core\Theme\ThemeManagerInterface::render()
* @see \Drupal\Core\Form\FormHelper::processStates()
Alex Pott
committed
* @see \Drupal\Core\Render\AttachmentsResponseProcessorInterface::processAttachments()
* @see \Drupal\Core\Render\RendererInterface::renderRoot()
Alex Pott
committed
*/
public function render(&$elements, $is_root_call = FALSE);
/**
* Checks whether a render context is active.
*
* This is useful only in very specific situations to determine whether the
* system is already capable of collecting bubbleable metadata. Normally it
* should not be necessary to be concerned about this.
*
* @return bool
* TRUE if the renderer has a render context active, FALSE otherwise.
*/
public function hasRenderContext();
Alex Pott
committed
/**
* Executes a callable within a render context.
*
* Only for very advanced use cases. Prefer using ::renderRoot() and
* ::renderInIsolation() instead.
Alex Pott
committed
*
* All rendering must happen within a render context. Within a render context,
* all bubbleable metadata is bubbled and hence tracked. Outside of a render
* context, it would be lost. This could lead to missing assets, incorrect
* cache variations (and thus security issues), insufficient cache
* invalidations, and so on.
*
* Any and all rendering must therefore happen within a render context, and it
* is this method that provides that.
*
* @param \Drupal\Core\Render\RenderContext $context
* The render context to execute the callable within.
* @param callable $callable
* The callable to execute.
*
Alex Pott
committed
* @return mixed
* The callable's return value.
*
* @throws \LogicException
* In case bubbling has failed, can only happen in case of broken code.
*
* @see \Drupal\Core\Render\RenderContext
* @see \Drupal\Core\Render\BubbleableMetadata
Alex Pott
committed
*/
public function executeInRenderContext(RenderContext $context, callable $callable);
/**
* Merges the bubbleable rendering metadata o/t 2nd render array with the 1st.
*
* @param array $a
* A render array.
* @param array $b
* A render array.
*
* @return array
* The first render array, modified to also contain the bubbleable rendering
* metadata of the second render array.
*
* @see \Drupal\Core\Render\BubbleableMetadata
*/
Alex Pott
committed
public function mergeBubbleableMetadata(array $a, array $b);
Alex Pott
committed
/**
* Adds a dependency on an object: merges its cacheability metadata.
*
Jennifer Hodgdon
committed
* For instance, when a render array depends on some configuration, an entity,
* or an access result, we must make sure their cacheability metadata is
* present on the render array. This method makes doing that simple.
Alex Pott
committed
*
* @param array &$elements
* The render array to update.
* @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $dependency
* The dependency. If the object implements CacheableDependencyInterface,
* then its cacheability metadata will be used. Otherwise, the passed in
* object must be assumed to be uncacheable, so max-age 0 is set.
*
* @see \Drupal\Core\Cache\CacheableMetadata::createFromObject()
Alex Pott
committed
*/
public function addCacheableDependency(array &$elements, $dependency);
Alex Pott
committed