bug #1361 Fixed recursively calling blocks in templates with inheritance (hason)

This PR was merged into the 1.15-dev branch.

Discussion
----------

Fixed recursively calling blocks in templates with inheritance

Replaces #1321
Fixes KnpLabs/KnpMenu#136 and KnpLabs/KnpMenu#119

Commits
-------

efb7b47 Add "broken" block inheritance test case
ef98ea2 Fixed recursively calling blocks in templates with inheritance
This commit is contained in:
Fabien Potencier
2014-03-15 11:49:13 +01:00
4 changed files with 102 additions and 14 deletions
+15 -14
View File
@@ -105,9 +105,9 @@ abstract class Twig_Template implements Twig_TemplateInterface
$name = (string) $name;
if (isset($this->traits[$name])) {
$this->traits[$name][0]->displayBlock($name, $context, $blocks);
$this->traits[$name][0]->displayBlock($name, $context, $blocks, false);
} elseif (false !== $parent = $this->getParent($context)) {
$parent->displayBlock($name, $context, $blocks);
$parent->displayBlock($name, $context, $blocks, false);
} else {
throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
}
@@ -119,18 +119,18 @@ abstract class Twig_Template implements Twig_TemplateInterface
* This method is for internal use only and should never be called
* directly.
*
* @param string $name The block name to display
* @param array $context The context
* @param array $blocks The current set of blocks
* @param string $name The block name to display
* @param array $context The context
* @param array $blocks The current set of blocks
* @param Boolean $useBlocks Whether to use the current set of blocks
*/
public function displayBlock($name, array $context, array $blocks = array())
public function displayBlock($name, array $context, array $blocks = array(), $useBlocks = true)
{
$name = (string) $name;
if (isset($blocks[$name])) {
if ($useBlocks && isset($blocks[$name])) {
$template = $blocks[$name][0];
$block = $blocks[$name][1];
unset($blocks[$name]);
} elseif (isset($this->blocks[$name])) {
$template = $this->blocks[$name][0];
$block = $this->blocks[$name][1];
@@ -148,7 +148,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getTemplateName(), $e);
}
} elseif (false !== $parent = $this->getParent($context)) {
$parent->displayBlock($name, $context, array_merge($this->blocks, $blocks));
$parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
}
}
@@ -178,16 +178,17 @@ abstract class Twig_Template implements Twig_TemplateInterface
* This method is for internal use only and should never be called
* directly.
*
* @param string $name The block name to render
* @param array $context The context
* @param array $blocks The current set of blocks
* @param string $name The block name to render
* @param array $context The context
* @param array $blocks The current set of blocks
* @param Boolean $useBlocks Whether to use the current set of blocks
*
* @return string The rendered block
*/
public function renderBlock($name, array $context, array $blocks = array())
public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true)
{
ob_start();
$this->displayBlock($name, $context, $blocks);
$this->displayBlock($name, $context, $blocks, $useBlocks);
return ob_get_clean();
}
@@ -0,0 +1,21 @@
--TEST--
"block" function recursively called in a parent template
--TEMPLATE--
{% extends "ordered_menu.twig" %}
{% block label %}"{{ parent() }}"{% endblock %}
{% block list %}{% set class = 'b' %}{{ parent() }}{% endblock %}
--TEMPLATE(ordered_menu.twig)--
{% extends "menu.twig" %}
{% block list %}{% set class = class|default('a') %}<ol class="{{ class }}">{{ block('children') }}</ol>{% endblock %}
--TEMPLATE(menu.twig)--
{% extends "base.twig" %}
{% block list %}<ul>{{ block('children') }}</ul>{% endblock %}
{% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %}
{% block item %}<li>{% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %}</li>{% endblock %}
{% block label %}{{ item }}{{ block('unknown') }}{% endblock %}
--TEMPLATE(base.twig)--
{{ block('list') }}
--DATA--
return array('item' => array('1', '2', array('3.1', array('3.2.1', '3.2.2'), '3.4')))
--EXPECT--
<ol class="b"><li>"1"</li><li>"2"</li><li><ol class="b"><li>"3.1"</li><li><ol class="b"><li>"3.2.1"</li><li>"3.2.2"</li></ol></li><li>"3.4"</li></ol></li></ol>
@@ -0,0 +1,32 @@
--TEST--
block_expr
--TEMPLATE--
{% extends "base.twig" %}
{% block element -%}
Element:
{{- parent() -}}
{% endblock %}
--TEMPLATE(base.twig)--
{% spaceless %}
{% block element -%}
<div>
{%- if item.children is defined %}
{%- for item in item.children %}
{{- block('element') -}}
{% endfor %}
{%- endif -%}
</div>
{%- endblock %}
{% endspaceless %}
--DATA--
return array(
'item' => array(
'children' => array(
null,
null,
)
)
)
--EXPECT--
Element:<div>Element:<div></div>Element:<div></div></div>
@@ -0,0 +1,34 @@
--TEST--
block_expr2
--TEMPLATE--
{% extends "base2.twig" %}
{% block element -%}
Element:
{{- parent() -}}
{% endblock %}
--TEMPLATE(base2.twig)--
{% extends "base.twig" %}
--TEMPLATE(base.twig)--
{% spaceless %}
{% block element -%}
<div>
{%- if item.children is defined %}
{%- for item in item.children %}
{{- block('element') -}}
{% endfor %}
{%- endif -%}
</div>
{%- endblock %}
{% endspaceless %}
--DATA--
return array(
'item' => array(
'children' => array(
null,
null,
)
)
)
--EXPECT--
Element:<div>Element:<div></div>Element:<div></div></div>