Last active
September 1, 2015 10:55
-
-
Save LionsAd/cbf84e5e70b05c1ca11e to your computer and use it in GitHub Desktop.
Cleanup renderer, prepare for passing just one cacheable metadata to doRender()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php | |
| index f2be9ce..82f6228 100644 | |
| --- a/core/lib/Drupal/Core/Render/Renderer.php | |
| +++ b/core/lib/Drupal/Core/Render/Renderer.php | |
| @@ -131,10 +131,23 @@ public function renderRoot(&$elements) { | |
| // Render in its own render context. | |
| $this->isRenderingRoot = TRUE; | |
| - $output = $this->executeInRenderContext(new RenderContext(), function () use (&$elements) { | |
| - return $this->render($elements, TRUE); | |
| - }); | |
| - $this->isRenderingRoot = FALSE; | |
| + try { | |
| + $output = $this->renderPlain($elements); | |
| + } | |
| + // Since #pre_render, #post_render, #lazy_builder callbacks and theme | |
| + // functions or templates may be used for generating a render array's | |
| + // content, and we might be rendering the main content for the page, it is | |
| + // possible that any of them throw an exception that will cause a different | |
| + // page to be rendered (e.g. throwing | |
| + // \Symfony\Component\HttpKernel\Exception\NotFoundHttpException will cause | |
| + // the 404 page to be rendered). That page might also use | |
| + // Renderer::renderRoot() but if exceptions aren't caught here, it will be | |
| + // impossible to call Renderer::renderRoot() again. | |
| + // Hence, catch all exceptions, reset the isRenderingRoot property and | |
| + // re-throw exceptions. | |
| + finally { | |
| + $this->isRenderingRoot = FALSE; | |
| + } | |
| return $output; | |
| } | |
| @@ -143,8 +156,9 @@ public function renderRoot(&$elements) { | |
| * {@inheritdoc} | |
| */ | |
| public function renderPlain(&$elements) { | |
| - return $this->executeInRenderContext(new RenderContext(), function () use (&$elements) { | |
| - return $this->render($elements, TRUE); | |
| + $context = new RenderContext(); | |
| + return $this->executeInRenderContext($context, function () use (&$elements, $context) { | |
| + return $this->doRenderRoot($elements, $context); | |
| }); | |
| } | |
| @@ -163,10 +177,8 @@ public function renderPlain(&$elements) { | |
| * The updated $elements. | |
| * | |
| * @see ::replacePlaceholders() | |
| - * | |
| - * @todo Make public as part of https://www.drupal.org/node/2469431 | |
| */ | |
| - protected function renderPlaceholder($placeholder, array $elements) { | |
| + public function renderPlaceholder($placeholder, array $elements) { | |
| // Get the render array for the given placeholder | |
| $placeholder_elements = $elements['#attached']['placeholders'][$placeholder]; | |
| @@ -192,31 +204,56 @@ protected function renderPlaceholder($placeholder, array $elements) { | |
| * {@inheritdoc} | |
| */ | |
| public function render(&$elements, $is_root_call = FALSE) { | |
| - // Since #pre_render, #post_render, #lazy_builder callbacks and theme | |
| - // functions or templates may be used for generating a render array's | |
| - // content, and we might be rendering the main content for the page, it is | |
| - // possible that any of them throw an exception that will cause a different | |
| - // page to be rendered (e.g. throwing | |
| - // \Symfony\Component\HttpKernel\Exception\NotFoundHttpException will cause | |
| - // the 404 page to be rendered). That page might also use | |
| - // Renderer::renderRoot() but if exceptions aren't caught here, it will be | |
| - // impossible to call Renderer::renderRoot() again. | |
| - // Hence, catch all exceptions, reset the isRenderingRoot property and | |
| - // re-throw exceptions. | |
| - try { | |
| - return $this->doRender($elements, $is_root_call); | |
| + $context = $this->getCurrentRenderContext(); | |
| + if (!isset($context)) { | |
| + throw new \LogicException("Render context is empty, because render() was called outside of a renderRoot() or renderPlain() call. Use renderPlain()/renderRoot() or #lazy_builder/#pre_render instead."); | |
| } | |
| - catch (\Exception $e) { | |
| - // Mark the ::rootRender() call finished due to this exception & re-throw. | |
| - $this->isRenderingRoot = FALSE; | |
| - throw $e; | |
| + | |
| + if ($is_root_call) { | |
| + trigger_error('render() with $is_root_call is deprecated; use renderRoot() instead.'); | |
| + return $this->doRenderRoot($elements, $context); | |
| } | |
| + | |
| + return $this->doRender($elements, $context); | |
| } | |
| /** | |
| * See the docs for ::render(). | |
| */ | |
| - protected function doRender(&$elements, $is_root_call = FALSE) { | |
| + protected function doRenderRoot(&$elements, $context) { | |
| + // Set the bubbleable rendering metadata that has configurable defaults, if: | |
| + // - this is the root call, to ensure that the final render array definitely | |
| + // has these configurable defaults, even when no subtree is render cached. | |
| + $required_cache_contexts = $this->rendererConfig['required_cache_contexts']; | |
| + | |
| + if (isset($elements['#cache']['contexts'])) { | |
| + $elements['#cache']['contexts'] = Cache::mergeContexts($elements['#cache']['contexts'], $required_cache_contexts); | |
| + } | |
| + else { | |
| + $elements['#cache']['contexts'] = $required_cache_contexts; | |
| + } | |
| + | |
| + // Render the elements normally. | |
| + $return = $this->doRender($elements, $context); | |
| + | |
| + // If there is no output, return early as placeholders can't make a | |
| + // difference. | |
| + if ($return === '') { | |
| + return $return; | |
| + } | |
| + | |
| + // Only when we're in a root (non-recursive) Renderer::render() call, | |
| + // placeholders must be processed, to prevent breaking the render cache in | |
| + // case of nested elements with #cache set. | |
| + $this->replacePlaceholders($elements); | |
| + | |
| + return $elements['#markup']; | |
| + } | |
| + | |
| + /** | |
| + * See the docs for ::render(). | |
| + */ | |
| + protected function doRender(&$elements, $context) { | |
| if (empty($elements)) { | |
| return ''; | |
| } | |
| @@ -248,10 +285,6 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
| return ''; | |
| } | |
| - $context = $this->getCurrentRenderContext(); | |
| - if (!isset($context)) { | |
| - throw new \LogicException("Render context is empty, because render() was called outside of a renderRoot() or renderPlain() call. Use renderPlain()/renderRoot() or #lazy_builder/#pre_render instead."); | |
| - } | |
| $context->push(new BubbleableMetadata()); | |
| // Set the bubbleable rendering metadata that has configurable defaults, if: | |
| @@ -259,7 +292,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
| // has these configurable defaults, even when no subtree is render cached. | |
| // - this is a render cacheable subtree, to ensure that the cached data has | |
| // the configurable defaults (which may affect the ID and invalidation). | |
| - if ($is_root_call || isset($elements['#cache']['keys'])) { | |
| + if (isset($elements['#cache']['keys'])) { | |
| $required_cache_contexts = $this->rendererConfig['required_cache_contexts']; | |
| if (isset($elements['#cache']['contexts'])) { | |
| $elements['#cache']['contexts'] = Cache::mergeContexts($elements['#cache']['contexts'], $required_cache_contexts); | |
| @@ -275,12 +308,6 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
| $cached_element = $this->renderCache->get($elements); | |
| if ($cached_element !== FALSE) { | |
| $elements = $cached_element; | |
| - // Only when we're in a root (non-recursive) Renderer::render() call, | |
| - // placeholders must be processed, to prevent breaking the render cache | |
| - // in case of nested elements with #cache set. | |
| - if ($is_root_call) { | |
| - $this->replacePlaceholders($elements); | |
| - } | |
| // Mark the element markup as safe if is it a string. | |
| if (is_string($elements['#markup'])) { | |
| $elements['#markup'] = SafeString::create($elements['#markup']); | |
| @@ -291,6 +318,8 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
| // Render cache hit, so rendering is finished, all necessary info | |
| // collected! | |
| $context->bubble(); | |
| + | |
| + | |
| return $elements['#markup']; | |
| } | |
| } | |
| @@ -452,7 +481,21 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
| // same process as Renderer::render() but is inlined for speed. | |
| if ((!$theme_is_implemented || isset($elements['#render_children'])) && empty($elements['#children'])) { | |
| foreach ($children as $key) { | |
| - $elements['#children'] .= $this->doRender($elements[$key]); | |
| + $child_element = &$elements[$key]; | |
| + if (isset($child_element['#cache']['keys'])) { | |
| + $new_context = new RenderContext(); | |
| + $elements['#children'] .= $this->executeInRenderContext($new_context, function () use (&$child_element, $new_context) { | |
| + return $this->doRender($child_element, $new_context); | |
| + }); | |
| + // @todo This should not be necessary. | |
| + if (!$new_context->isEmpty()) { | |
| + $frame = $context->pop()->merge($new_context->pop()); | |
| + $context->push($frame); | |
| + } | |
| + } | |
| + else { | |
| + $elements['#children'] .= $this->doRender($elements[$key], $context); | |
| + } | |
| } | |
| $elements['#children'] = SafeString::create($elements['#children']); | |
| } | |
| @@ -532,23 +575,6 @@ protected function doRender(&$elements, $is_root_call = FALSE) { | |
| $this->renderCache->set($elements, $pre_bubbling_elements); | |
| } | |
| - // Only when we're in a root (non-recursive) Renderer::render() call, | |
| - // placeholders must be processed, to prevent breaking the render cache in | |
| - // case of nested elements with #cache set. | |
| - // | |
| - // By running them here, we ensure that: | |
| - // - they run when #cache is disabled, | |
| - // - they run when #cache is enabled and there is a cache miss. | |
| - // Only the case of a cache hit when #cache is enabled, is not handled here, | |
| - // that is handled earlier in Renderer::render(). | |
| - if ($is_root_call) { | |
| - $this->replacePlaceholders($elements); | |
| - // @todo remove as part of https://www.drupal.org/node/2511330. | |
| - if ($context->count() !== 1) { | |
| - throw new \LogicException('A stray drupal_render() invocation with $is_root_call = TRUE is causing bubbling of attached assets to break.'); | |
| - } | |
| - } | |
| - | |
| // Rendering is finished, all necessary info collected! | |
| $context->bubble(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment