changed the implementation of template inheritance
authorFabien Potencier <fabien.potencier@gmail.com>
Fri, 1 Oct 2010 12:11:58 +0000 (14:11 +0200)
committerFabien Potencier <fabien.potencier@gmail.com>
Fri, 1 Oct 2010 12:17:16 +0000 (14:17 +0200)
CHANGELOG
lib/Twig/Node/Block.php
lib/Twig/Node/BlockReference.php
lib/Twig/Node/Module.php
lib/Twig/Node/Parent.php
lib/Twig/Template.php
test/Twig/Tests/Node/BlockReferenceTest.php
test/Twig/Tests/Node/BlockTest.php
test/Twig/Tests/Node/ModuleTest.php
test/Twig/Tests/Node/ParentTest.php
test/Twig/Tests/Node/SandboxedModuleTest.php

index c647692..566662b 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,8 @@ Backward incompatibilities:
  * the odd and even filters are now tests:
      {{ foo|odd }} must now be written {{ foo is odd }}
 
+ * the implementation of template inheritance has been rewritten
+   (blocks can now be called individually and still work with inheritance)
  * fixed error handling for if tag when a syntax error occurs within a subparse process
  * added a way to implement custom logic for resolving token parsers given a tag name
  * fixed js escaper to be stricter (now uses a whilelist-based js escaper)
index 5547d95..d73e98d 100644 (file)
@@ -33,7 +33,7 @@ class Twig_Node_Block extends Twig_Node
     {
         $compiler
             ->addDebugInfo($this)
-            ->write(sprintf("public function block_%s(\$context, \$parents)\n", $this['name']), "{\n")
+            ->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this['name']), "{\n")
             ->indent()
         ;
 
index a09b4dc..1314f7b 100644 (file)
@@ -33,7 +33,7 @@ class Twig_Node_BlockReference extends Twig_Node
     {
         $compiler
             ->addDebugInfo($this)
-            ->write(sprintf("\$this->getBlock('%s', \$context);\n", $this['name']))
+            ->write(sprintf("\$this->getBlock('%s', \$context, \$blocks);\n", $this['name']))
         ;
     }
 }
index add0e31..630c25d 100644 (file)
@@ -42,6 +42,8 @@ class Twig_Node_Module extends Twig_Node
             $this->compileConstructor($compiler);
         }
 
+        $this->compileGetParent($compiler);
+
         $this->compileDisplayHeader($compiler);
 
         $this->compileDisplayBody($compiler);
@@ -55,6 +57,48 @@ class Twig_Node_Module extends Twig_Node
         $this->compileClassFooter($compiler);
     }
 
+    protected function compileGetParent($compiler)
+    {
+        if (null === $this->parent) {
+            return;
+        }
+
+        $compiler
+            ->write("public function getParent(array \$context)\n", "{\n")
+            ->indent()
+            ->write("if (null === \$this->parent) {\n")
+            ->indent();
+        ;
+
+        if ($this->parent instanceof Twig_Node_Expression_Constant) {
+            $compiler
+                ->write("\$this->parent = \$this->env->loadTemplate(")
+                ->subcompile($this->parent)
+                ->raw(");\n")
+            ;
+        } else {
+            $compiler
+                ->write("\$this->parent = ")
+                ->subcompile($this->parent)
+                ->raw(";\n")
+                ->write("if (!\$this->parent")
+                ->raw(" instanceof Twig_Template) {\n")
+                ->indent()
+                ->write("\$this->parent = \$this->env->loadTemplate(\$this->parent);\n")
+                ->outdent()
+                ->write("}\n")
+            ;
+        }
+
+        $compiler
+            ->outdent()
+            ->write("}\n\n")
+            ->write("return \$this->parent;\n")
+            ->outdent()
+            ->write("}\n\n")
+        ;
+    }
+
     protected function compileDisplayBody($compiler)
     {
         if (null !== $this->parent) {
@@ -66,36 +110,7 @@ class Twig_Node_Module extends Twig_Node
             }
 
             $compiler
-                ->write("if (null === \$this->parent) {\n")
-                ->indent();
-            ;
-
-            if ($this->parent instanceof Twig_Node_Expression_Constant) {
-                $compiler
-                    ->write("\$this->parent = clone \$this->env->loadTemplate(")
-                    ->subcompile($this->parent)
-                    ->raw(");\n")
-                ;
-            } else {
-                $compiler
-                    ->write("\$parent = ")
-                    ->subcompile($this->parent)
-                    ->raw(";\n")
-                    ->write("if (!\$parent")
-                    ->raw(" instanceof Twig_Template) {\n")
-                    ->indent()
-                    ->write("\$parent = \$this->env->loadTemplate(\$parent);\n")
-                    ->outdent()
-                    ->write("}\n")
-                    ->write("\$this->parent = clone \$parent;\n")
-                ;
-            }
-
-            $compiler
-                ->write("\$this->parent->pushBlocks(\$this->blocks);\n")
-                ->outdent()
-                ->write("}\n")
-                ->write("\$this->parent->display(\$context);\n")
+                ->write("\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));\n")
             ;
         } else {
             $compiler->subcompile($this->body);
@@ -131,7 +146,7 @@ class Twig_Node_Module extends Twig_Node
 
         foreach ($this->blocks as $name => $node) {
             $compiler
-                ->write(sprintf("'%s' => array(array(\$this, 'block_%s')),\n", $name, $name))
+                ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
             ;
         }
 
@@ -146,7 +161,7 @@ class Twig_Node_Module extends Twig_Node
     protected function compileDisplayHeader($compiler)
     {
         $compiler
-            ->write("public function display(array \$context)\n", "{\n")
+            ->write("public function display(array \$context, array \$blocks = array())\n", "{\n")
             ->indent()
         ;
     }
index deed784..dc779cf 100644 (file)
@@ -33,7 +33,9 @@ class Twig_Node_Parent extends Twig_Node
     {
         $compiler
             ->addDebugInfo($this)
-            ->write("\$this->getParent(\$context, \$parents);\n")
+            ->write("\$this->getParentBlock(")
+            ->string($this['name'])
+            ->raw(", \$context, \$blocks);\n")
         ;
     }
 }
index bbbee48..2cc752d 100644 (file)
@@ -22,44 +22,48 @@ abstract class Twig_Template implements Twig_TemplateInterface
         $this->blocks = array();
     }
 
-    public function __clone()
-    {
-        foreach ($this->blocks as $name => $calls) {
-            foreach ($calls as $i => $call) {
-                $this->blocks[$name][$i][0] = $this;
-            }
-        }
-    }
-
     public function getEnvironment()
     {
         return $this->env;
     }
 
-    public function getBlock($name, array $context)
+    public function getParent(array $context)
     {
-        return call_user_func($this->blocks[$name][0], $context, array_slice($this->blocks[$name], 1));
+        return false;
     }
 
-    public function hasBlock($name)
+    public function getParentBlock($name, array $context, array $blocks = array())
     {
-        return isset($this->blocks[$name][0]);
+        if (false !== $parent = $this->getParent($context)) {
+            return $parent->getBlock($name, $context, $blocks);
+        } else {
+            throw new \LogicException('This template has no parent.');
+        }
     }
 
-    protected function getParent($context, $parents)
+    public function getBlock($name, array $context, array $blocks = array())
     {
-        return call_user_func($parents[0], $context, array_slice($parents, 1));
+        if (isset($blocks[$name])) {
+            $b = $blocks;
+            unset($b[$name]);
+            return call_user_func($blocks[$name], $context, $b);
+        } elseif (isset($this->blocks[$name])) {
+            return call_user_func($this->blocks[$name], $context, $blocks);
+        }
+
+        if (false !== $parent = $this->getParent($context)) {
+            return $parent->getBlock($name, $context, array_merge($this->blocks, $blocks));
+        }
     }
 
-    public function pushBlocks($blocks)
+    public function hasBlock($name)
     {
-        foreach ($blocks as $name => $call) {
-            if (!isset($this->blocks[$name])) {
-                $this->blocks[$name] = array();
-            }
+        return isset($this->blocks[$name]);
+    }
 
-            $this->blocks[$name] = array_merge($call, $this->blocks[$name]);
-        }
+    public function getBlockNames()
+    {
+        return array_keys($this->blocks);
     }
 
     /**
index 77e3986..b5e7fcf 100644 (file)
@@ -35,7 +35,7 @@ class Twig_Tests_Node_BlockReferenceTest extends Twig_Tests_Node_TestCase
     public function getTests()
     {
         return array(
-            array(new Twig_Node_BlockReference('foo', 0), '$this->getBlock(\'foo\', $context);'),
+            array(new Twig_Node_BlockReference('foo', 0), '$this->getBlock(\'foo\', $context, $blocks);'),
         );
     }
 }
index 6c4d86c..eb4c4ff 100644 (file)
@@ -41,7 +41,7 @@ class Twig_Tests_Node_BlockTest extends Twig_Tests_Node_TestCase
 
         return array(
             array($node, <<<EOF
-public function block_foo(\$context, \$parents)
+public function block_foo(\$context, array \$blocks = array())
 {
     echo "foo";
 }
index 37a891e..b52308b 100644 (file)
@@ -67,7 +67,7 @@ class Twig_Tests_Node_ModuleTest extends Twig_Tests_Node_TestCase
 /* foo.twig */
 class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
 {
-    public function display(array \$context)
+    public function display(array \$context, array \$blocks = array())
     {
         echo "foo";
     }
@@ -90,14 +90,19 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
 {
     protected \$parent;
 
-    public function display(array \$context)
+    public function getParent(array \$context)
     {
-        \$context['macro'] = \$this->env->loadTemplate("foo.twig", true);
         if (null === \$this->parent) {
-            \$this->parent = clone \$this->env->loadTemplate("layout.twig");
-            \$this->parent->pushBlocks(\$this->blocks);
+            \$this->parent = \$this->env->loadTemplate("layout.twig");
         }
-        \$this->parent->display(\$context);
+
+        return \$this->parent;
+    }
+
+    public function display(array \$context, array \$blocks = array())
+    {
+        \$context['macro'] = \$this->env->loadTemplate("foo.twig", true);
+        \$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));
     }
 
 }
@@ -121,17 +126,21 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
 {
     protected \$parent;
 
-    public function display(array \$context)
+    public function getParent(array \$context)
     {
         if (null === \$this->parent) {
-            \$parent = (true) ? ("foo") : ("foo");
-            if (!\$parent instanceof Twig_Template) {
-                \$parent = \$this->env->loadTemplate(\$parent);
+            \$this->parent = (true) ? ("foo") : ("foo");
+            if (!\$this->parent instanceof Twig_Template) {
+                \$this->parent = \$this->env->loadTemplate(\$this->parent);
             }
-            \$this->parent = clone \$parent;
-            \$this->parent->pushBlocks(\$this->blocks);
         }
-        \$this->parent->display(\$context);
+
+        return \$this->parent;
+    }
+
+    public function display(array \$context, array \$blocks = array())
+    {
+        \$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));
     }
 
 }
index 8ea761c..6b40b24 100644 (file)
@@ -35,7 +35,7 @@ class Twig_Tests_Node_ParentTest extends Twig_Tests_Node_TestCase
     public function getTests()
     {
         $tests = array();
-        $tests[] = array(new Twig_Node_Parent('foo', 0), '$this->getParent($context, $parents);');
+        $tests[] = array(new Twig_Node_Parent('foo', 0), '$this->getParentBlock("foo", $context, $blocks);');
 
         return $tests;
     }
index e823741..4551133 100644 (file)
@@ -65,7 +65,7 @@ class Twig_Tests_Node_SandboxedModuleTest extends Twig_Tests_Node_TestCase
 /* foo.twig */
 class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
 {
-    public function display(array \$context)
+    public function display(array \$context, array \$blocks = array())
     {
         \$this->checkSecurity();
         echo "foo";
@@ -99,13 +99,18 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
 {
     protected \$parent;
 
-    public function display(array \$context)
+    public function getParent(array \$context)
     {
         if (null === \$this->parent) {
-            \$this->parent = clone \$this->env->loadTemplate("layout.twig");
-            \$this->parent->pushBlocks(\$this->blocks);
+            \$this->parent = \$this->env->loadTemplate("layout.twig");
         }
-        \$this->parent->display(\$context);
+
+        return \$this->parent;
+    }
+
+    public function display(array \$context, array \$blocks = array())
+    {
+        \$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));
     }
 
     protected function checkSecurity() {