From d101a059764de6a2279a13f270b263280bcc218e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 22 Sep 2011 12:03:40 +0200 Subject: [PATCH] added traits support for the parent function --- CHANGELOG | 1 + doc/templates.rst | 12 ++++-- lib/Twig/ExpressionParser.php | 4 +- lib/Twig/Node/Module.php | 12 ++++- lib/Twig/Parser.php | 5 ++ lib/Twig/Template.php | 8 +++- .../tags/inheritance/parent_without_extends.test | 8 ++++ .../parent_without_extends_but_traits.test | 14 ++++++ test/Twig/Tests/Fixtures/tags/inheritance/use.test | 44 ++++++++++++++++++++ 9 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test create mode 100644 test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test create mode 100644 test/Twig/Tests/Fixtures/tags/inheritance/use.test diff --git a/CHANGELOG b/CHANGELOG index 231affd..963698a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ * 1.3.0 * allowed empty templates to be used as traits + * added traits support for the "parent" function * 1.2.0 (2011-09-13) diff --git a/doc/templates.rst b/doc/templates.rst index 31ae6e7..90ca31b 100644 --- a/doc/templates.rst +++ b/doc/templates.rst @@ -1628,22 +1628,26 @@ is ignored. To avoid name conflicts, you can rename imported blocks: {% block title %}{% endblock %} {% block content %}{% endblock %} -Renaming also allows you to simulate inheritance by calling the "parent" block -(like what you would have done with ``parent()``): +The ``parent()`` function automatically determines the correct inheritance +tree, so it can be used when overriding a block defined in an imported +template: .. code-block:: jinja {% extends "base.html" %} - {% use "blocks.html" with sidebar as parent_sidebar %} + {% use "blocks.html" %} {% block sidebar %} - {{ block('parent_sidebar') }} + {{ parent() }} {% endblock %} {% block title %}{% endblock %} {% block content %}{% endblock %} +In this example, ``parent()`` will correctly call the ``sidebar`` block from +the ``blocks.html`` template. + .. note:: You can use as many ``use`` statements as you want in any given template. diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index b4fc5d5..97c8431 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -241,8 +241,8 @@ class Twig_ExpressionParser throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line); } - if (!$this->parser->getParent()) { - throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend another one is forbidden', $line); + if (!$this->parser->getParent() && !$this->parser->hasTraits()) { + throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line); } return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line); diff --git a/lib/Twig/Node/Module.php b/lib/Twig/Node/Module.php index 1621232..7c12589 100644 --- a/lib/Twig/Node/Module.php +++ b/lib/Twig/Node/Module.php @@ -152,17 +152,25 @@ class Twig_Node_Module extends Twig_Node } $compiler - ->write("\$this->blocks = array_merge(\n") + ->write("\$this->traits = array_merge(\n") ->indent() ; for ($i = 0; $i < $countTraits; $i++) { $compiler - ->write(sprintf("\$_trait_%s_blocks,\n", $i)) + ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i)) ; } $compiler + ->outdent() + ->write(");\n\n") + ; + + $compiler + ->write("\$this->blocks = array_merge(\n") + ->indent() + ->write("\$this->traits,\n") ->write("array(\n") ; } else { diff --git a/lib/Twig/Parser.php b/lib/Twig/Parser.php index 8c98288..dc5b42c 100644 --- a/lib/Twig/Parser.php +++ b/lib/Twig/Parser.php @@ -234,6 +234,11 @@ class Twig_Parser implements Twig_ParserInterface $this->traits[] = $trait; } + public function hasTraits() + { + return count($this->traits) > 0; + } + public function addImportedFunction($alias, $name, Twig_Node_Expression $node) { $this->importedFunctions[0][$alias] = array('name' => $name, 'node' => $node); diff --git a/lib/Twig/Template.php b/lib/Twig/Template.php index a129fd7..c21179f 100644 --- a/lib/Twig/Template.php +++ b/lib/Twig/Template.php @@ -23,6 +23,7 @@ abstract class Twig_Template implements Twig_TemplateInterface protected $parents; protected $env; protected $blocks; + protected $traits; /** * Constructor. @@ -33,6 +34,7 @@ abstract class Twig_Template implements Twig_TemplateInterface { $this->env = $env; $this->blocks = array(); + $this->traits = array(); } /** @@ -84,10 +86,12 @@ abstract class Twig_Template implements Twig_TemplateInterface */ public function displayParentBlock($name, array $context, array $blocks = array()) { - if (false !== $parent = $this->getParent($context)) { + if (isset($this->traits[$name])) { + $this->traits[$name][0]->displayBlock($name, $context, $blocks); + } elseif (false !== $parent = $this->getParent($context)) { $parent->displayBlock($name, $context, $blocks); } else { - throw new Twig_Error_Runtime('This template has no parent', -1, $this->getTemplateName()); + throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName()); } } diff --git a/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test b/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test new file mode 100644 index 0000000..a9eaa4c --- /dev/null +++ b/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test @@ -0,0 +1,8 @@ +--TEST-- +"parent" tag +--TEMPLATE-- +{% block content %} + {{ parent() }} +{% endblock %} +--EXCEPTION-- +Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3 diff --git a/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test b/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test new file mode 100644 index 0000000..63c7305 --- /dev/null +++ b/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test @@ -0,0 +1,14 @@ +--TEST-- +"parent" tag +--TEMPLATE-- +{% use 'foo.twig' %} + +{% block content %} + {{ parent() }} +{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %}BAR{% endblock %} +--DATA-- +return array() +--EXPECT-- +BAR diff --git a/test/Twig/Tests/Fixtures/tags/inheritance/use.test b/test/Twig/Tests/Fixtures/tags/inheritance/use.test new file mode 100644 index 0000000..8f9ece7 --- /dev/null +++ b/test/Twig/Tests/Fixtures/tags/inheritance/use.test @@ -0,0 +1,44 @@ +--TEST-- +"parent" function +--TEMPLATE-- +{% extends "parent.twig" %} + +{% use "use1.twig" %} +{% use "use2.twig" %} + +{% block content_parent %} + {{ parent() }} +{% endblock %} + +{% block content_use1 %} + {{ parent() }} +{% endblock %} + +{% block content_use2 %} + {{ parent() }} +{% endblock %} + +{% block content %} + {{ block('content_use1_only') }} + {{ block('content_use2_only') }} +{% endblock %} +--TEMPLATE(parent.twig)-- +{% block content_parent 'content_parent' %} +{% block content_use1 'content_parent' %} +{% block content_use2 'content_parent' %} +{% block content '' %} +--TEMPLATE(use1.twig)-- +{% block content_use1 'content_use1' %} +{% block content_use2 'content_use1' %} +{% block content_use1_only 'content_use1_only' %} +--TEMPLATE(use2.twig)-- +{% block content_use2 'content_use2' %} +{% block content_use2_only 'content_use2_only' %} +--DATA-- +return array() +--EXPECT-- + content_parent + content_use1 + content_use2 + content_use1_only + content_use2_only -- 1.7.2.5