Changed Twig_Node_Expression_Filter to accept only one filter
authorArnaud Le Blanc <arnaud.lb@gmail.com>
Mon, 15 Nov 2010 22:07:19 +0000 (23:07 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Fri, 19 Nov 2010 07:06:32 +0000 (08:06 +0100)
Chained filters are now represented by nested
Twig_Node_Expression_Filter nodes internally.

This makes it easier to analyse the tree

lib/Twig/ExpressionParser.php
lib/Twig/Node/Expression/Filter.php
lib/Twig/NodeVisitor/Escaper.php
lib/Twig/NodeVisitor/SafeAnalysis.php
lib/Twig/NodeVisitor/Sandbox.php
lib/Twig/SimpleTokenParser.php
lib/Twig/TokenParser/Filter.php
test/Twig/Tests/Node/Expression/FilterTest.php
test/Twig/Tests/Node/TransTest.php

index 8a76372..e5d530a 100644 (file)
@@ -401,9 +401,10 @@ class Twig_ExpressionParser
 
         $end = $this->parseExpression();
 
-        $filters = new Twig_Node(array(new Twig_Node_Expression_Constant('range', $lineno), new Twig_Node(array($end))));
+        $name = new Twig_Node_Expression_Constant('range', $lineno);
+        $arguments = new Twig_Node(array($end));
 
-        return new Twig_Node_Expression_Filter($node, $filters, $lineno);
+        return new Twig_Node_Expression_Filter($node, $name, $arguments, $lineno);
     }
 
     public function parseSubscriptExpression($node)
@@ -438,26 +439,27 @@ class Twig_ExpressionParser
 
     public function parseFilterExpression($node)
     {
-        $lineno = $this->parser->getCurrentToken()->getLine();
-
         $this->parser->getStream()->next();
 
-        return new Twig_Node_Expression_Filter($node, $this->parseFilterExpressionRaw(), $lineno);
+        return $this->parseFilterExpressionRaw($node);
     }
 
-    public function parseFilterExpressionRaw()
+    public function parseFilterExpressionRaw($node, $tag = null)
     {
-        $filters = array();
+        $lineno = $this->parser->getCurrentToken()->getLine();
+
         while (true) {
             $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE);
 
-            $filters[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
+            $name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
             if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '(')) {
-                $filters[] = new Twig_Node();
+                $arguments = new Twig_Node();
             } else {
-                $filters[] = $this->parseArguments();
+                $arguments = $this->parseArguments();
             }
 
+            $node = new Twig_Node_Expression_Filter($node, $name, $arguments, $token->getLine(), $tag);
+
             if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '|')) {
                 break;
             }
@@ -465,7 +467,7 @@ class Twig_ExpressionParser
             $this->parser->getStream()->next();
         }
 
-        return new Twig_Node($filters);
+        return $node;
     }
 
     public function parseArguments()
index a291a05..33223d5 100644 (file)
  */
 class Twig_Node_Expression_Filter extends Twig_Node_Expression
 {
-    public function __construct(Twig_NodeInterface $node, Twig_NodeInterface $filters, $lineno, $tag = null)
+    public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filter_name, Twig_NodeInterface $arguments, $lineno, $tag = null)
     {
-        parent::__construct(array('node' => $node, 'filters' => $filters), array(), $lineno, $tag);
+        parent::__construct(array('node' => $node, 'filter' => $filter_name, 'arguments' => $arguments), array(), $lineno, $tag);
     }
 
     public function compile($compiler)
     {
         $filterMap = $compiler->getEnvironment()->getFilters();
 
-        $postponed = array();
-        for ($i = count($this->getNode('filters')) - 1; $i >= 0; $i -= 2) {
-            $name = $this->getNode('filters')->getNode($i - 1)->getAttribute('value');
-            $attrs = $this->getNode('filters')->getNode($i);
-            if (!isset($filterMap[$name])) {
-                throw new Twig_Error_Syntax(sprintf('The filter "%s" does not exist', $name), $this->getLine());
-            } else {
-                $compiler->raw($filterMap[$name]->compile().($filterMap[$name]->needsEnvironment() ? '($this->env, ' : '('));
-            }
-            $postponed[] = $attrs;
+        $name = $this->getNode('filter')->getAttribute('value');
+        $attrs = $this->getNode('arguments');
+
+        if (!isset($filterMap[$name])) {
+            throw new Twig_Error_Syntax(sprintf('The filter "%s" does not exist', $name), $this->getLine());
+        } else {
+            $compiler->raw($filterMap[$name]->compile().($filterMap[$name]->needsEnvironment() ? '($this->env, ' : '('));
         }
 
         $this->getNode('node')->compile($compiler);
 
-        foreach (array_reverse($postponed) as $attributes) {
-            foreach ($attributes as $node) {
-                $compiler
-                    ->raw(', ')
-                    ->subcompile($node)
+        foreach ($attrs as $node) {
+            $compiler
+                ->raw(', ')
+                ->subcompile($node)
                 ;
-            }
-            $compiler->raw(')');
-        }
-    }
-
-    public function prependFilter(Twig_Node_Expression_Constant $name, Twig_Node $end)
-    {
-        $filters = array($name, $end);
-        foreach ($this->getNode('filters') as $node) {
-            $filters[] = $node;
-        }
-
-        $this->setNode('filters', new Twig_Node($filters, array(), $this->getNode('filters')->getLine()));
-    }
-
-    public function appendFilter(Twig_Node_Expression_Constant $name, Twig_Node $end)
-    {
-        $filters = array();
-        foreach ($this->getNode('filters') as $node) {
-            $filters[] = $node;
-        }
-
-        $filters[] = $name;
-        $filters[] = $end;
-
-        $this->setNode('filters', new Twig_Node($filters, array(), $this->getNode('filters')->getLine()));
-    }
-
-    public function appendFilters(Twig_NodeInterface $filters)
-    {
-        for ($i = 0; $i < count($filters); $i += 2) {
-            $this->appendFilter($filters->getNode($i), $filters->getNode($i + 1));
-        }
-    }
-
-    public function hasFilter($name)
-    {
-        for ($i = 0; $i < count($this->getNode('filters')); $i += 2) {
-            if ($name == $this->getNode('filters')->getNode($i)->getAttribute('value')) {
-                return true;
-            }
         }
 
-        return false;
+        $compiler->raw(')');
     }
 }
index 6926d49..44e2f6b 100644 (file)
@@ -90,22 +90,14 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
             return $node;
         }
 
-        // escape
-        if ($expression instanceof Twig_Node_Expression_Filter) {
-            $filter = $this->getEscaperFilter($type, $expression->getLine());
-            $expression->appendFilter($filter[0], $filter[1]);
-
-            return $node;
-        }
-
         if ($node instanceof Twig_Node_Print) {
             return new Twig_Node_Print(
-                new Twig_Node_Expression_Filter($expression, new Twig_Node($this->getEscaperFilter($type, $node->getLine())), $node->getLine()),
+                $this->getEscaperFilter($type, $expression),
                 $node->getLine()
             );
         }
 
-        return new Twig_Node_Expression_Filter($node, new Twig_Node($this->getEscaperFilter($type, $node->getLine())), $node->getLine());
+        return $this->getEscaperFilter($type, $node);
     }
 
     protected function needEscaping(Twig_Environment $env)
@@ -121,8 +113,11 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
         return false;
     }
 
-    protected function getEscaperFilter($type, $line)
+    protected function getEscaperFilter($type, Twig_NodeInterface $node)
     {
-        return array(new Twig_Node_Expression_Constant('escape', $line), new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line))));
+        $line = $node->getLine();
+        $name = new Twig_Node_Expression_Constant('escape', $line);
+        $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line)));
+        return new Twig_Node_Expression_Filter($node, $name, $args, $line);
     }
 }
index 79d1f06..7adfe11 100644 (file)
@@ -34,12 +34,10 @@ class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
             $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3')));
             $this->setSafe($node, $safe);
         } elseif ($node instanceof Twig_Node_Expression_Filter) {
-            // filter expression is safe when the last filter is safe
+            // filter expression is safe when the filter is safe
             $filterMap = $env->getFilters();
-            $filters = $node->getNode('filters');
-            $i = count($filters) - 2;
-            $name = $filters->getNode($i)->getAttribute('value');
-            $args = $filters->getNode($i+1);
+            $name = $node->getNode('filter')->getAttribute('value');
+            $args = $node->getNode('arguments');
             if (isset($filterMap[$name])) {
                 $this->setSafe($node, $filterMap[$name]->getSafe($args));
             } else {
index 8720134..77a507b 100644 (file)
@@ -45,9 +45,7 @@ class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
 
             // look for filters
             if ($node instanceof Twig_Node_Expression_Filter) {
-                for ($i = 0; $i < count($node->getNode('filters')); $i += 2) {
-                    $this->filters[] = $node->getNode('filters')->getNode($i)->getAttribute('value');
-                }
+                $this->filters[] = $node->getNode('filter')->getAttribute('value');
             }
 
             // look for simple print statements ({{ article }})
index 9a681cb..40c71ba 100644 (file)
@@ -65,7 +65,8 @@ abstract class Twig_SimpleTokenParser extends Twig_TokenParser
     {
         return new Twig_Node_Expression_Filter(
             $node,
-            new Twig_Node(array(new Twig_Node_Expression_Constant('raw', $line), new Twig_Node())),
+            new Twig_Node_Expression_Constant('raw', $line),
+            new Twig_Node(),
             $line
         );
     }
index 4d053b7..b800f10 100644 (file)
@@ -19,20 +19,21 @@ class Twig_TokenParser_Filter extends Twig_TokenParser
      */
     public function parse(Twig_Token $token)
     {
-        $filters = $this->parser->getExpressionParser()->parseFilterExpressionRaw();
+        $name = '_tmp'.rand(10000, 99999);
+        $node = new Twig_Node_Expression_Name($name, $token->getLine());
+
+        $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($node, $this->getTag());
 
         $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
         $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
         $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
 
-        $name = '_tmp'.rand(10000, 99999);
         $ref = new Twig_Node_BlockReference($name, $token->getLine(), $this->getTag());
 
         $block = new Twig_Node_Block($name, $body, $token->getLine());
         $this->parser->setBlock($name, $block);
 
         $set = new Twig_Node_Set(true, new Twig_Node(array(new Twig_Node_Expression_AssignName($name, $token->getLine()))), new Twig_Node(array($ref)), $token->getLine(), $this->getTag());
-        $filter = new Twig_Node_Expression_Filter(new Twig_Node_Expression_Name($name, $token->getLine()), $filters, $token->getLine(), $this->getTag());
         $filter = new Twig_Node_Print($filter, $token->getLine(), $this->getTag());
 
         return new Twig_Node(array($set, $filter));
index 42e58d0..e4c31d0 100644 (file)
@@ -19,110 +19,13 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Tests_Node_TestCase
     public function testConstructor()
     {
         $expr = new Twig_Node_Expression_Constant('foo', 0);
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-        ), array(), 0);
-        $node = new Twig_Node_Expression_Filter($expr, $filters, 0);
+        $name = new Twig_Node_Expression_Constant('upper', 0);
+        $args = new Twig_Node();
+        $node = new Twig_Node_Expression_Filter($expr, $name, $args, 0);
 
         $this->assertEquals($expr, $node->getNode('node'));
-        $this->assertEquals($filters, $node->getNode('filters'));
-    }
-
-    /**
-     * @covers Twig_Node_Expression_Filter::hasFilter
-     */
-    public function testHasFilter()
-    {
-        $expr = new Twig_Node_Expression_Constant('foo', 0);
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-        ), array(), 0);
-        $node = new Twig_Node_Expression_Filter($expr, $filters, 0);
-
-        $this->assertTrue($node->hasFilter('upper'));
-        $this->assertFalse($node->hasFilter('lower'));
-    }
-
-    /**
-     * @covers Twig_Node_Expression_Filter::prependFilter
-     */
-    public function testPrependFilter()
-    {
-        $expr = new Twig_Node_Expression_Constant('foo', 0);
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-        ), array(), 0);
-        $node = new Twig_Node_Expression_Filter($expr, $filters, 0);
-
-        $a = new Twig_Node_Expression_Constant('lower', 0);
-        $b = new Twig_Node_Expression_Constant('foobar', 0);
-        $node->prependFilter($a, $b);
-
-        $filters = new Twig_Node(array(
-            $a,
-            $b,
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-        ), array(), 0);
-
-        $this->assertEquals($filters, $node->getNode('filters'));
-    }
-
-    /**
-     * @covers Twig_Node_Expression_Filter::appendFilter
-     */
-    public function testAppendFilter()
-    {
-        $expr = new Twig_Node_Expression_Constant('foo', 0);
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-        ), array(), 0);
-        $node = new Twig_Node_Expression_Filter($expr, $filters, 0);
-
-        $a = new Twig_Node_Expression_Constant('lower', 0);
-        $b = new Twig_Node_Expression_Constant('foobar', 0);
-        $node->appendFilter($a, $b);
-
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-            $a,
-            $b,
-        ), array(), 0);
-
-        $this->assertEquals($filters, $node->getNode('filters'));
-    }
-
-    /**
-     * @covers Twig_Node_Expression_Filter::appendFilters
-     */
-    public function testAppendFilters()
-    {
-        $expr = new Twig_Node_Expression_Constant('foo', 0);
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-        ), array(), 0);
-        $node = new Twig_Node_Expression_Filter($expr, $filters, 0);
-
-        $others = new Twig_Node(array(
-            $a = new Twig_Node_Expression_Constant('lower', 0),
-            $b = new Twig_Node_Expression_Constant('foobar', 0),
-        ), array(), 0);
-        $node->appendFilters($others);
-
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-            $a,
-            $b,
-        ), array(), 0);
-
-        $this->assertEquals($filters, $node->getNode('filters'));
+        $this->assertEquals($name, $node->getNode('filter'));
+        $this->assertEquals($args, $node->getNode('arguments'));
     }
 
     /**
@@ -134,11 +37,7 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Tests_Node_TestCase
         parent::testCompile($node, $source, $environment);
 
         $expr = new Twig_Node_Expression_Constant('foo', 0);
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('foobar', 0),
-            new Twig_Node(array(new Twig_Node_Expression_Constant('bar', 0), new Twig_Node_Expression_Constant('foobar', 0))),
-        ), array(), 0);
-        $node = new Twig_Node_Expression_Filter($expr, $filters, 0);
+        $node = $this->createFilter($expr, 'foobar', array(new Twig_Node_Expression_Constant('bar', 0), new Twig_Node_Expression_Constant('foobar', 0)));
 
         $tests[] = array($node, '$this->resolveMissingFilter("foobar", array("foo", "bar", "foobar"))');
 
@@ -155,13 +54,8 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Tests_Node_TestCase
         $tests = array();
 
         $expr = new Twig_Node_Expression_Constant('foo', 0);
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('upper', 0),
-            new Twig_Node(),
-            new Twig_Node_Expression_Constant('lower', 0),
-            new Twig_Node(array(new Twig_Node_Expression_Constant('bar', 0), new Twig_Node_Expression_Constant('foobar', 0))),
-        ), array(), 0);
-        $node = new Twig_Node_Expression_Filter($expr, $filters, 0);
+        $node = $this->createFilter($expr, 'upper');
+        $node = $this->createFilter($node, 'lower', array(new Twig_Node_Expression_Constant('bar', 0), new Twig_Node_Expression_Constant('foobar', 0)));
 
         if (function_exists('mb_get_info')) {
             $tests[] = array($node, 'twig_lower_filter($this->env, twig_upper_filter($this->env, "foo"), "bar", "foobar")');
@@ -171,4 +65,11 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Tests_Node_TestCase
 
         return $tests;
     }
+
+    protected function createFilter($node, $name, array $arguments = array())
+    {
+        $name = new Twig_Node_Expression_Constant($name, 0);
+        $arguments = new Twig_Node($arguments);
+        return new Twig_Node_Expression_Filter($node, $name, $arguments, 0);
+    }
 }
index 962b4d3..c1495e4 100644 (file)
@@ -79,14 +79,9 @@ class Twig_Tests_Node_TransTest extends Twig_Tests_Node_TestCase
         $tests[] = array($node, 'echo strtr(ngettext("Hey %name%, I have one apple", "Hey %name%, I have %count% apples", abs(12)), array("%name%" => (isset($context[\'name\']) ? $context[\'name\'] : null), "%name%" => (isset($context[\'name\']) ? $context[\'name\'] : null), "%count%" => abs(12), ));');
 
         // with escaper extension set to on
-        $filters = new Twig_Node(array(
-            new Twig_Node_Expression_Constant('escape', 0),
-            new Twig_Node(),
-        ), array(), 0);
-
         $body = new Twig_Node(array(
             new Twig_Node_Text('J\'ai ', 0),
-            new Twig_Node_Print(new Twig_Node_Expression_Filter(new Twig_Node_Expression_Name('foo', 0), $filters, 0), 0),
+            new Twig_Node_Print(new Twig_Node_Expression_Filter(new Twig_Node_Expression_Name('foo', 0), new Twig_Node_Expression_Constant('escape', 0), new Twig_Node(), 0), 0),
             new Twig_Node_Text(' pommes', 0),
         ), array(), 0);