summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2013-05-19 23:34:52 (GMT)
committer Alex Pott2013-05-19 23:34:52 (GMT)
commit3b1f85aa34c6dde064f82bb94396f42592ef18d6 (patch)
tree3de2f14eb3cabae2cb1019fa5e50ba9f9690941a
parent340a4342fdc188304d0d1c0a309695816f005b17 (diff)
Issue #1920886 by Cottser, Fabianx: Drupal_render() should only render the child elements when rendering a 'render element' for the first time.
-rw-r--r--core/includes/common.inc10
-rw-r--r--core/includes/theme.inc2
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php21
-rw-r--r--core/modules/system/tests/modules/theme_test/theme_test.module17
4 files changed, 47 insertions, 3 deletions
diff --git a/core/includes/common.inc b/core/includes/common.inc
index e03ba33..5e8e2d1 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 c1792e8..721453a 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 20ccc2b..664a73b 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php
@@ -194,6 +194,27 @@ class ThemeTest extends WebTestBase {
}
/**
+ * 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.
*/
function testClassLoading() {
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 137fdfc..d610a19 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']);
+}