added traits support for the parent function
authorFabien Potencier <fabien.potencier@gmail.com>
Thu, 22 Sep 2011 10:03:40 +0000 (12:03 +0200)
committerFabien Potencier <fabien.potencier@gmail.com>
Thu, 22 Sep 2011 12:06:20 +0000 (14:06 +0200)
CHANGELOG
doc/templates.rst
lib/Twig/ExpressionParser.php
lib/Twig/Node/Module.php
lib/Twig/Parser.php
lib/Twig/Template.php
test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/tags/inheritance/use.test [new file with mode: 0644]

index 231affd..963698a 100644 (file)
--- 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)
 
index 31ae6e7..90ca31b 100644 (file)
@@ -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.
index b4fc5d5..97c8431 100644 (file)
@@ -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);
index 1621232..7c12589 100644 (file)
@@ -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 {
index 8c98288..dc5b42c 100644 (file)
@@ -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);
index a129fd7..c21179f 100644 (file)
@@ -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 (file)
index 0000000..a9eaa4c
--- /dev/null
@@ -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 (file)
index 0000000..63c7305
--- /dev/null
@@ -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 (file)
index 0000000..8f9ece7
--- /dev/null
@@ -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