From 3b1f85aa34c6dde064f82bb94396f42592ef18d6 Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Sun, 19 May 2013 16:34:52 -0700 Subject: [PATCH] Issue #1920886 by Cottser, Fabianx: Drupal_render() should only render the child elements when rendering a 'render element' for the first time. --- core/includes/common.inc | 10 ++++++--- core/includes/theme.inc | 2 ++ .../Drupal/system/Tests/Theme/ThemeTest.php | 21 +++++++++++++++++++ .../modules/theme_test/theme_test.module | 17 +++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/core/includes/common.inc b/core/includes/common.inc index e03ba330fe9a..5e8e2d1e683f 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -5233,8 +5233,10 @@ function drupal_render(&$elements) { $elements['#children'] = ''; } // Call the element's #theme function if it is set. Then any children of the - // element have to be rendered there. - if (isset($elements['#theme'])) { + // element have to be rendered there. If the internal #render_children + // property is set, do not call the #theme function to prevent infinite + // recursion. + if (isset($elements['#theme']) && !isset($elements['#render_children'])) { $elements['#children'] = theme($elements['#theme'], $elements); } // If #theme was not set and the element has children, render them now. @@ -5272,7 +5274,9 @@ function drupal_render(&$elements) { // the #type 'page' render array from drupal_render_page() would render the // $page and wrap it into the html.tpl.php template without the attached // assets otherwise. - if (isset($elements['#theme_wrappers'])) { + // If the internal #render_children property is set, do not call the + // #theme_wrappers function(s) to prevent infinite recursion. + if (isset($elements['#theme_wrappers']) && !isset($elements['#render_children'])) { foreach ($elements['#theme_wrappers'] as $theme_wrapper) { $elements['#children'] = theme($theme_wrapper, $elements); } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index c1792e861cc6..721453ad4a8c 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1036,6 +1036,8 @@ function theme($hook, $variables = array()) { } else { $variables[$info['render element']] = $element; + // Give a hint to render engines to prevent infinite recursion. + $variables[$info['render element']]['#render_children'] = TRUE; } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php index 20ccc2bff8a3..664a73b64615 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php @@ -193,6 +193,27 @@ function testRegistryRebuild() { $this->assertIdentical(theme('theme_test_foo', array('foo' => 'c')), 'c', 'The theme registry contains theme_test_foo again after re-enabling the module.'); } + /** + * Tests child element rendering for 'render element' theme hooks. + */ + function testDrupalRenderChildren() { + $element = array( + '#theme' => 'theme_test_render_element_children', + 'child' => array( + '#markup' => 'Foo', + ), + ); + $this->assertIdentical(theme('theme_test_render_element_children', $element), 'Foo', 'drupal_render() avoids #theme recursion loop when rendering a render element.'); + + $element = array( + '#theme_wrappers' => array('theme_test_render_element_children'), + 'child' => array( + '#markup' => 'Foo', + ), + ); + $this->assertIdentical(theme('theme_test_render_element_children', $element), 'Foo', 'drupal_render() avoids #theme_wrappers recursion loop when rendering a render element.'); + } + /** * Tests theme can provide classes. */ diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module index 137fdfcc8532..d610a19c3281 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -17,6 +17,9 @@ function theme_test_theme($existing, $type, $theme, $path) { $items['theme_test_foo'] = array( 'variables' => array('foo' => NULL), ); + $items['theme_test_render_element_children'] = array( + 'render element' => 'element', + ); return $items; } @@ -172,3 +175,17 @@ function theme_theme_test_foo($variables) { return $variables['foo']; } +/** + * Theme function for testing rendering of child elements via drupal_render(). + * + * Theme hooks defining a 'render element' add an internal '#render_children' + * property. When this property is found, drupal_render() avoids calling theme() + * on the top-level element to prevent infinite recursion. + * + * @param array $variables + * An associative array containing: + * - element: An associative array containing the properties of the element. + */ +function theme_theme_test_render_element_children($variables) { + return drupal_render($variables['element']); +} -- GitLab