fixed compilation of templates when the body of a child template is not empty
authorFabien Potencier <fabien.potencier@gmail.com>
Tue, 26 Jul 2011 06:02:51 +0000 (08:02 +0200)
committerFabien Potencier <fabien.potencier@gmail.com>
Tue, 26 Jul 2011 06:02:51 +0000 (08:02 +0200)
CHANGELOG
lib/Twig/Node/Module.php
lib/Twig/Parser.php
test/Twig/Tests/Node/ModuleTest.php
test/Twig/Tests/Node/SandboxedModuleTest.php
test/Twig/Tests/ParserTest.php

index 969b409..cdf0530 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,6 @@
 * 1.1.2
 
+ * fixed compilation of templates when the body of a child template is not empty
  * fixed output when a macro throws an exception
  * fixed a parsing problem when a large chunk of text is enclosed in a comment tag
  * added PHPDoc for all Token parsers and Core extension functions
index 1af7622..923dea8 100644 (file)
@@ -87,20 +87,10 @@ class Twig_Node_Module extends Twig_Node
     protected function compileDisplayBody(Twig_Compiler $compiler)
     {
         $compiler->write("\$context = array_merge(\$this->env->getGlobals(), \$context);\n\n");
+        $compiler->subcompile($this->getNode('body'));
 
         if (null !== $this->getNode('parent')) {
-            // remove all output nodes
-            foreach ($this->getNode('body') as $node) {
-                if (!$node instanceof Twig_NodeOutputInterface) {
-                    $compiler->subcompile($node);
-                }
-            }
-
-            $compiler
-                ->write("\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));\n")
-            ;
-        } else {
-            $compiler->subcompile($this->getNode('body'));
+            $compiler->write("\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
         }
     }
 
index 0e7a8af..fc41374 100644 (file)
@@ -81,7 +81,7 @@ class Twig_Parser implements Twig_ParserInterface
             $body = $this->subparse(null);
 
             if (null !== $this->parent) {
-                $this->checkBodyNodes($body);
+                $body = $this->filterBodyNodes($body);
             }
         } catch (Twig_Error_Syntax $e) {
             if (null === $e->getTemplateFile()) {
@@ -292,17 +292,27 @@ class Twig_Parser implements Twig_ParserInterface
         return $this->stream->getCurrent();
     }
 
-    protected function checkBodyNodes($body)
+    protected function filterBodyNodes(Twig_NodeInterface $node)
     {
         // check that the body does not contain non-empty output nodes
-        foreach ($body as $node) {
-            if (
-                ($node instanceof Twig_Node_Text && !ctype_space($node->getAttribute('data')))
-                ||
-                (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface)
-            ) {
-                throw new Twig_Error_Syntax(sprintf('A template that extends another one cannot have a body (%s).', $node), $node->getLine(), $this->stream->getFilename());
+        if (
+            ($node instanceof Twig_Node_Text && !ctype_space($node->getAttribute('data')))
+            ||
+            (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface)
+        ) {
+            throw new Twig_Error_Syntax(sprintf('A template that extends another one cannot have a body (%s).', $node), $node->getLine(), $this->stream->getFilename());
+        }
+
+        if ($node instanceof Twig_NodeOutputInterface) {
+            return null;
+        }
+
+        foreach ($node as $k => $n) {
+            if (null === $n = $this->filterBodyNodes($n)) {
+                $node->removeNode($k);
             }
         }
+
+        return $node;
     }
 }
index f928800..d5ea618 100644 (file)
@@ -91,7 +91,7 @@ EOF
 
         $import = new Twig_Node_Import(new Twig_Node_Expression_Constant('foo.twig', 0), new Twig_Node_Expression_AssignName('macro', 0), 0);
 
-        $body = new Twig_Node(array($import, new Twig_Node_Text('foo', 0)));
+        $body = new Twig_Node(array($import));
         $extends = new Twig_Node_Expression_Constant('layout.twig', 0);
 
         $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, $filename);
@@ -133,7 +133,7 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
 EOF
         , $twig);
 
-        $body = new Twig_Node_Text('foo', 0);
+        $body = new Twig_Node();
         $extends = new Twig_Node_Expression_Conditional(
                         new Twig_Node_Expression_Constant(true, 0),
                         new Twig_Node_Expression_Constant('foo', 0),
index 097574e..0b2b445 100644 (file)
@@ -96,7 +96,7 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
 EOF
         , $twig);
 
-        $body = new Twig_Node_Text('foo', 0);
+        $body = new Twig_Node();
         $extends = new Twig_Node_Expression_Constant('layout.twig', 0);
         $blocks = new Twig_Node();
         $macros = new Twig_Node();
index 2159a5c..7dfddda 100644 (file)
@@ -18,4 +18,63 @@ class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase
         $parser = new Twig_Parser(new Twig_Environment());
         $parser->setMacro('display', $this->getMock('Twig_Node_Macro', null, array(), '', null));
     }
+
+    /**
+     * @dataProvider getFilterBodyNodesData
+     */
+    public function testFilterBodyNodes($input, $expected)
+    {
+        list($parser, $invoker) = $this->getParserForFilterBodyNodes();
+
+        $this->assertEquals($expected, $invoker->invoke($parser, $input));
+    }
+
+    public function getFilterBodyNodesData()
+    {
+        return array(
+            array(
+                new Twig_Node(array(new Twig_Node_Text('   ', 0))),
+                new Twig_Node(array()),
+            ),
+            array(
+                $input = new Twig_Node(array(new Twig_Node_Set(false, new Twig_Node(), new Twig_Node(), 0))),
+                $input,
+            ),
+        );
+    }
+
+    /**
+     * @dataProvider getFilterBodyNodesDataThrowsException
+     * @expectedException Twig_Error_Syntax
+     */
+    public function testFilterBodyNodesThrowsException($input)
+    {
+        list($parser, $invoker) = $this->getParserForFilterBodyNodes();
+
+        $invoker->invoke($parser, $input);
+    }
+
+    public function getFilterBodyNodesDataThrowsException()
+    {
+        return array(
+            array(new Twig_Node_Text('foo', 0)),
+            array(new Twig_Node(array(new Twig_Node(array(new Twig_Node_Text('foo', 0)))))),
+        );
+    }
+
+    protected function getParserForFilterBodyNodes()
+    {
+        $invoker = new ReflectionMethod('Twig_Parser', 'filterBodyNodes');
+        $invoker->setAccessible(true);
+
+        $p = new ReflectionProperty('Twig_Parser', 'stream');
+        $p->setAccessible(true);
+
+        $parser = new Twig_Parser(new Twig_Environment());
+        $parser->setParent(new Twig_Node());
+        $p->setValue($parser, $this->getMockBuilder('Twig_TokenStream')->disableOriginalConstructor()->getMock());
+
+        return array($parser, $invoker);
+    }
 }
+//Twig_Error_Syntax