RendererInterface.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. <?php
  2. namespace Drupal\Core\Render;
  3. /**
  4. * Defines an interface for turning a render array into a string.
  5. */
  6. interface RendererInterface {
  7. /**
  8. * Renders final HTML given a structured array tree.
  9. *
  10. * Calls ::render() in such a way that placeholders are replaced.
  11. *
  12. * Should therefore only be used in occasions where the final rendering is
  13. * happening, just before sending a Response:
  14. * - system internals that are responsible for rendering the final HTML
  15. * - render arrays for non-HTML responses, such as feeds
  16. *
  17. * (Cannot be executed within another render context.)
  18. *
  19. * @param array $elements
  20. * The structured array describing the data to be rendered.
  21. *
  22. * @return \Drupal\Component\Render\MarkupInterface
  23. * The rendered HTML.
  24. *
  25. * @throws \LogicException
  26. * When called from inside another renderRoot() call.
  27. *
  28. * @see \Drupal\Core\Render\RendererInterface::render()
  29. */
  30. public function renderRoot(&$elements);
  31. /**
  32. * Renders final HTML in situations where no assets are needed.
  33. *
  34. * Calls ::render() in such a way that placeholders are replaced.
  35. *
  36. * Useful for instance when rendering the values of tokens or emails, which
  37. * need a render array being turned into a string, but do not need any of the
  38. * bubbleable metadata (the attached assets and cache tags).
  39. *
  40. * Some of these are a relatively common use case and happen *within* a
  41. * ::renderRoot() call, but that is generally highly problematic (and hence an
  42. * exception is thrown when a ::renderRoot() call happens within another
  43. * ::renderRoot() call). However, in this case, we only care about the output,
  44. * not about the bubbling. Hence this uses a separate render context, to not
  45. * affect the parent ::renderRoot() call.
  46. *
  47. * (Can be executed within another render context: it runs in isolation.)
  48. *
  49. * @param array $elements
  50. * The structured array describing the data to be rendered.
  51. *
  52. * @return \Drupal\Component\Render\MarkupInterface
  53. * The rendered HTML.
  54. *
  55. * @see \Drupal\Core\Render\RendererInterface::renderRoot()
  56. * @see \Drupal\Core\Render\RendererInterface::render()
  57. */
  58. public function renderPlain(&$elements);
  59. /**
  60. * Renders final HTML for a placeholder.
  61. *
  62. * Renders the placeholder in isolation.
  63. *
  64. * @param string $placeholder
  65. * An attached placeholder to render. (This must be a key of one of the
  66. * values of $elements['#attached']['placeholders'].)
  67. * @param array $elements
  68. * The structured array describing the data to be rendered.
  69. *
  70. * @return array
  71. * The updated $elements.
  72. *
  73. * @see \Drupal\Core\Render\RendererInterface::render()
  74. */
  75. public function renderPlaceholder($placeholder, array $elements);
  76. /**
  77. * Renders HTML given a structured array tree.
  78. *
  79. * Renderable arrays have two kinds of key/value pairs: properties and
  80. * children. Properties have keys starting with '#' and their values influence
  81. * how the array will be rendered. Children are all elements whose keys do not
  82. * start with a '#'. Their values should be renderable arrays themselves,
  83. * which will be rendered during the rendering of the parent array. The markup
  84. * provided by the children is typically inserted into the markup generated by
  85. * the parent array.
  86. *
  87. * An important aspect of rendering is caching the result, when appropriate.
  88. * Because the HTML of a rendered item includes all of the HTML of the
  89. * rendered children, caching it requires certain information to bubble up
  90. * from child elements to their parents:
  91. * - Cache contexts, so that the render cache is varied by every context that
  92. * affects the rendered HTML. Because cache contexts affect the cache ID,
  93. * and therefore must be resolved for cache hits as well as misses, it is
  94. * up to the implementation of this interface to decide how to implement
  95. * the caching of items whose children specify cache contexts not directly
  96. * specified by the parent. \Drupal\Core\Render\Renderer implements this
  97. * with a lazy two-tier caching strategy. Alternate strategies could be to
  98. * not cache such parents at all or to cache them with the child elements
  99. * replaced by placeholder tokens that are dynamically rendered after cache
  100. * retrieval.
  101. * - Cache tags, so that cached renderings are invalidated when site content
  102. * or configuration that can affect that rendering changes.
  103. * - Placeholders, with associated self-contained placeholder render arrays,
  104. * for executing code to handle dynamic requirements that cannot be cached.
  105. * A render context (\Drupal\Core\Render\RenderContext) can be used to perform
  106. * bubbling; it is a stack of \Drupal\Core\Render\BubbleableMetadata objects.
  107. *
  108. * Additionally, whether retrieving from cache or not, it is important to
  109. * know all of the assets (CSS and JavaScript) required by the rendered HTML,
  110. * and this must also bubble from child to parent. Therefore,
  111. * \Drupal\Core\Render\BubbleableMetadata includes that data as well.
  112. *
  113. * The process of rendering an element is recursive unless the element defines
  114. * an implemented theme hook in #theme. During each call to
  115. * Renderer::render(), the outermost renderable array (also known as an
  116. * "element") is processed using the following steps:
  117. * - If this element has already been printed (#printed = TRUE) or the user
  118. * does not have access to it (#access = FALSE), then an empty string is
  119. * returned.
  120. * - If no render context is set yet, an exception is thrown. Otherwise,
  121. * an empty \Drupal\Core\Render\BubbleableMetadata is pushed onto the
  122. * render context.
  123. * - If this element has #cache defined then the cached markup for this
  124. * element will be returned if it exists in Renderer::render()'s cache. To
  125. * use Renderer::render() caching, set the element's #cache property to an
  126. * associative array with one or several of the following keys:
  127. * - 'keys': An array of one or more keys that identify the element. If
  128. * 'keys' is set, the cache ID is created automatically from these keys.
  129. * - 'contexts': An array of one or more cache context IDs. These are
  130. * converted to a final value depending on the request. (For instance,
  131. * 'user' is mapped to the current user's ID.)
  132. * - 'max-age': A time in seconds. Zero seconds means it is not cacheable.
  133. * \Drupal\Core\Cache\Cache::PERMANENT means it is cacheable forever.
  134. * - 'bin': Specify a cache bin to cache the element in. Default is
  135. * 'default'.
  136. * When there is a render cache hit, there is no rendering work left to be
  137. * done, so the stack must be updated. The empty (and topmost) frame that
  138. * was just pushed onto the stack is updated with all bubbleable rendering
  139. * metadata from the element retrieved from render cache. Then, this stack
  140. * frame is bubbled: the two topmost frames are popped from the stack,
  141. * they are merged, and the result is pushed back onto the stack.
  142. * However, also in case of a cache miss we have to do something. Note
  143. * that a Renderer renders top-down, which means that we try to render a
  144. * parent first, and we try to avoid the work of rendering the children by
  145. * using the render cache. Though in this case, we are dealing with a
  146. * cache miss. So a Renderer traverses down the tree, rendering all
  147. * children. In doing so, the render stack is updated with the bubbleable
  148. * metadata of the children. That means that once the children are
  149. * rendered, we can render cache this element. But the cache ID may have
  150. * *changed* at that point, because the children's cache contexts have
  151. * been bubbled!
  152. * It is for that case that we must store the current (pre-bubbling) cache
  153. * ID, so that we can compare it with the new (post-bubbling) cache ID
  154. * when writing to the cache. We store the current cache ID in
  155. * $pre_bubbling_cid.
  156. * - If this element has #type defined and the default attributes for this
  157. * element have not already been merged in (#defaults_loaded = TRUE) then
  158. * the defaults for this type of element, defined by an element plugin,
  159. * are merged into the array. #defaults_loaded is set by functions that
  160. * process render arrays and call the element info service before passing
  161. * the array to Renderer::render(), such as form_builder() in the Form
  162. * API.
  163. * - If this element has #create_placeholder set to TRUE, and it has a
  164. * #lazy_builder callback, then the element is replaced with another
  165. * element that has only two properties: #markup and #attached. #markup
  166. * will contain placeholder markup, and #attached contains the placeholder
  167. * metadata, that will be used for replacing this placeholder. That
  168. * metadata contains a very compact render array (containing only
  169. * #lazy_builder and #cache) that will be rendered to replace the
  170. * placeholder with its final markup. This means that when the
  171. * #lazy_builder callback is called, it received a render array to add to
  172. * that only contains #cache.
  173. * - If this element has a #lazy_builder or an array of #pre_render
  174. * functions defined, they are called sequentially to modify the element
  175. * before rendering. #lazy_builder is preferred, since it allows for
  176. * placeholdering (see previous step), but #pre_render is still supported.
  177. * Both have their use case: #lazy_builder is for building a render array,
  178. * #pre_render is for decorating an existing render array.
  179. * After the #lazy_builder function is called, #lazy_builder is removed,
  180. * and #built is set to TRUE.
  181. * After the #lazy_builder and all #pre_render functions have been called,
  182. * #printed is checked a second time in case a #lazy_builder or
  183. * #pre_render function flags the element as printed. If #printed is set,
  184. * we return early and hence no rendering work is left to be done,
  185. * similarly to a render cache hit. Once again, the empty (and topmost)
  186. * frame that was just pushed onto the stack is updated with all
  187. * bubbleable rendering metadata from the element whose #printed = TRUE.
  188. * Then, this stack frame is bubbled: the two topmost frames are popped
  189. * from the stack, they are merged, and the result is pushed back onto the
  190. * stack.
  191. * - The child elements of this element are sorted by weight using uasort()
  192. * in \Drupal\Core\Render\Element::children(). Since this is expensive,
  193. * when passing already sorted elements to Renderer::render(), for example
  194. * from a database query, set $elements['#sorted'] = TRUE to avoid sorting
  195. * them a second time.
  196. * - The main render phase to produce #children for this element takes
  197. * place:
  198. * - If this element has #theme defined and #theme is an implemented theme
  199. * hook/suggestion then ThemeManagerInterface::render() is called and
  200. * must render both the element and its children. If #render_children is
  201. * set, ThemeManagerInterface::render() will not be called.
  202. * #render_children is usually only set internally by
  203. * ThemeManagerInterface::render() so that we can avoid the situation
  204. * where Renderer::render() called from within a theme preprocess
  205. * function creates an infinite loop.
  206. * - If this element does not have a defined #theme, or the defined #theme
  207. * hook is not implemented, or #render_children is set, then
  208. * Renderer::render() is called recursively on each of the child
  209. * elements of this element, and the result of each is concatenated onto
  210. * #children. This is skipped if #children is not empty at this point.
  211. * - Once #children has been rendered for this element, if #theme is not
  212. * implemented and #markup is set for this element, #markup will be
  213. * prepended to #children.
  214. * - If this element has #states defined then JavaScript state information
  215. * is added to this element's #attached attribute by
  216. * drupal_process_states().
  217. * - If this element has #attached defined then any required libraries,
  218. * JavaScript, CSS, or other custom data are added to the current page by
  219. * \Drupal\Core\Render\AttachmentsResponseProcessorInterface::processAttachments().
  220. * - If this element has an array of #theme_wrappers defined and
  221. * #render_children is not set, #children is then re-rendered by passing
  222. * the element in its current state to ThemeManagerInterface::render()
  223. * successively for each item in #theme_wrappers. Since #theme and
  224. * #theme_wrappers hooks often define variables with the same names it is
  225. * possible to explicitly override each attribute passed to each
  226. * #theme_wrappers hook by setting the hook name as the key and an array
  227. * of overrides as the value in #theme_wrappers array.
  228. * For example, if we have a render element as follows:
  229. * @code
  230. * array(
  231. * '#theme' => 'image',
  232. * '#attributes' => array('class' => array('foo')),
  233. * '#theme_wrappers' => array('container'),
  234. * );
  235. * @endcode
  236. * and we need to pass the class 'bar' as an attribute for 'container', we
  237. * can rewrite our element thus:
  238. * @code
  239. * array(
  240. * '#theme' => 'image',
  241. * '#attributes' => array('class' => array('foo')),
  242. * '#theme_wrappers' => array(
  243. * 'container' => array(
  244. * '#attributes' => array('class' => array('bar')),
  245. * ),
  246. * ),
  247. * );
  248. * @endcode
  249. * - If this element has an array of #post_render functions defined, they
  250. * are called sequentially to modify the rendered #children. Unlike
  251. * #pre_render functions, #post_render functions are passed both the
  252. * rendered #children attribute as a string and the element itself.
  253. * - If this element has #prefix and/or #suffix defined, they are
  254. * concatenated to #children.
  255. * - The rendering of this element is now complete. The next step will be
  256. * render caching. So this is the perfect time to update the stack. At
  257. * this point, children of this element (if any), have been rendered also,
  258. * and if there were any, their bubbleable rendering metadata will have
  259. * been bubbled up into the stack frame for the element that is currently
  260. * being rendered. The render cache item for this element must contain the
  261. * bubbleable rendering metadata for this element and all of its children.
  262. * However, right now, the topmost stack frame (the one for this element)
  263. * currently only contains the metadata for the children. Therefore, the
  264. * topmost stack frame is updated with this element's metadata, and then
  265. * the element's metadata is replaced with the metadata in the topmost
  266. * stack frame. This element now contains all bubbleable rendering
  267. * metadata for this element and all its children, so it's now ready for
  268. * render caching.
  269. * - If this element has #cache defined, the rendered output of this element
  270. * is saved to Renderer::render()'s internal cache. This includes the
  271. * changes made by #post_render.
  272. * At the same time, if $pre_bubbling_cid is set, it is compared to the
  273. * calculated cache ID. If they are different, then a redirecting cache
  274. * item is created, containing the #cache metadata of the current element,
  275. * and written to cache using the value of $pre_bubbling_cid as the cache
  276. * ID. This ensures the pre-bubbling ("wrong") cache ID redirects to the
  277. * post-bubbling ("right") cache ID.
  278. * - If this element also has #cache_properties defined, all the array items
  279. * matching the specified property names will be cached along with the
  280. * element markup. If properties include children names, the system
  281. * assumes only children's individual markup is relevant and ignores the
  282. * parent markup. This approach is normally not needed and should be
  283. * adopted only when dealing with very advanced use cases.
  284. * - If this element has attached placeholders ([#attached][placeholders]),
  285. * or any of its children has (which we would know thanks to the stack
  286. * having been updated just before the render caching step), its
  287. * placeholder element containing a #lazy_builder function is rendered in
  288. * isolation. The resulting markup is used to replace the placeholder, and
  289. * any bubbleable metadata is merged.
  290. * Placeholders must be unique, to guarantee that for instance, samples of
  291. * placeholders are not replaced as well.
  292. * - Just before finishing the rendering of this element, this element's
  293. * stack frame (the topmost one) is bubbled: the two topmost frames are
  294. * popped from the stack, they are merged and the result is pushed back
  295. * onto the stack.
  296. * So if for instance this element was a child element, then a new frame
  297. * was pushed onto the stack element at the beginning of rendering this
  298. * element, it was updated when the rendering was completed, and now we
  299. * merge it with the frame for the parent, so that the parent now has the
  300. * bubbleable rendering metadata for its child.
  301. * - #printed is set to TRUE for this element to ensure that it is only
  302. * rendered once.
  303. * - The final value of #children for this element is returned as the
  304. * rendered output.
  305. *
  306. * @param array $elements
  307. * The structured array describing the data to be rendered.
  308. * @param bool $is_root_call
  309. * (Internal use only.) Whether this is a recursive call or not. See
  310. * ::renderRoot().
  311. *
  312. * @return \Drupal\Component\Render\MarkupInterface
  313. * The rendered HTML.
  314. *
  315. * @throws \LogicException
  316. * When called outside of a render context (i.e. outside of a renderRoot(),
  317. * renderPlain() or executeInRenderContext() call).
  318. * @throws \Exception
  319. * If a #pre_render callback throws an exception, it is caught to mark the
  320. * renderer as no longer being in a root render call, if any. Then the
  321. * exception is rethrown.
  322. *
  323. * @see \Drupal\Core\Render\ElementInfoManagerInterface::getInfo()
  324. * @see \Drupal\Core\Theme\ThemeManagerInterface::render()
  325. * @see drupal_process_states()
  326. * @see \Drupal\Core\Render\AttachmentsResponseProcessorInterface::processAttachments()
  327. * @see \Drupal\Core\Render\RendererInterface::renderRoot()
  328. */
  329. public function render(&$elements, $is_root_call = FALSE);
  330. /**
  331. * Checks whether a render context is active.
  332. *
  333. * This is useful only in very specific situations to determine whether the
  334. * system is already capable of collecting bubbleable metadata. Normally it
  335. * should not be necessary to be concerned about this.
  336. *
  337. * @return bool
  338. * TRUE if the renderer has a render context active, FALSE otherwise.
  339. */
  340. public function hasRenderContext();
  341. /**
  342. * Executes a callable within a render context.
  343. *
  344. * Only for very advanced use cases. Prefer using ::renderRoot() and
  345. * ::renderPlain() instead.
  346. *
  347. * All rendering must happen within a render context. Within a render context,
  348. * all bubbleable metadata is bubbled and hence tracked. Outside of a render
  349. * context, it would be lost. This could lead to missing assets, incorrect
  350. * cache variations (and thus security issues), insufficient cache
  351. * invalidations, and so on.
  352. *
  353. * Any and all rendering must therefore happen within a render context, and it
  354. * is this method that provides that.
  355. *
  356. * @param \Drupal\Core\Render\RenderContext $context
  357. * The render context to execute the callable within.
  358. * @param callable $callable
  359. * The callable to execute.
  360. *
  361. * @return mixed
  362. * The callable's return value.
  363. *
  364. * @throws \LogicException
  365. * In case bubbling has failed, can only happen in case of broken code.
  366. *
  367. * @see \Drupal\Core\Render\RenderContext
  368. * @see \Drupal\Core\Render\BubbleableMetadata
  369. */
  370. public function executeInRenderContext(RenderContext $context, callable $callable);
  371. /**
  372. * Merges the bubbleable rendering metadata o/t 2nd render array with the 1st.
  373. *
  374. * @param array $a
  375. * A render array.
  376. * @param array $b
  377. * A render array.
  378. *
  379. * @return array
  380. * The first render array, modified to also contain the bubbleable rendering
  381. * metadata of the second render array.
  382. *
  383. * @see \Drupal\Core\Render\BubbleableMetadata
  384. */
  385. public function mergeBubbleableMetadata(array $a, array $b);
  386. /**
  387. * Adds a dependency on an object: merges its cacheability metadata.
  388. *
  389. * For instance, when a render array depends on some configuration, an entity,
  390. * or an access result, we must make sure their cacheability metadata is
  391. * present on the render array. This method makes doing that simple.
  392. *
  393. * @param array &$elements
  394. * The render array to update.
  395. * @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $dependency
  396. * The dependency. If the object implements CacheableDependencyInterface,
  397. * then its cacheability metadata will be used. Otherwise, the passed in
  398. * object must be assumed to be uncacheable, so max-age 0 is set.
  399. *
  400. * @see \Drupal\Core\Cache\CacheableMetadata::createFromObject()
  401. */
  402. public function addCacheableDependency(array &$elements, $dependency);
  403. }