Browse code

made a big refactoring of Twig internals (and added a bunch of unit tests and phpdoc, fixes #53)

Fabien Potencier authored on 01/06/2010 17:56:15
Showing 104 changed files
... ...
@@ -1,8 +1,15 @@
1 1
 * 0.9.7-DEV
2 2
 
3 3
 Backward incompatibilities:
4
- * The short notation of the `block` tag changed.
5
-
4
+ * added a 'as' string to the block tag short notation ({% block title "Title" %} must now be {% block title as "Title" %})
5
+ * removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
6
+ * refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
7
+
8
+ * removed the Twig_Resource::resolveMissingFilter() method
9
+ * fixed the filter tag which did not apply filtering to included files
10
+ * added a bunch of unit tests
11
+ * added a bunch of phpdoc
12
+ * added a sandbox tag in the sandbox extension
6 13
  * fixed iterator_to_array() usage
7 14
  * changed the date filter to support any date format supported by DateTime
8 15
  * added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default when debug is false)
... ...
@@ -12,7 +19,6 @@ Backward incompatibilities:
12 19
  * added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface
13 20
  * changed the generated code to match the new coding standards
14 21
  * fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }})
15
- * added a 'as' string to the block tag short notation ({% block title "Title" %} must now be {% block title as "Title" %})
16 22
  * added an exception when a child template has a non-empty body (as it is always ignored when rendering)
17 23
 
18 24
 * 0.9.6 (2010-05-12)
... ...
@@ -641,12 +641,6 @@ rendered contents of that file into the current namespace:
641 641
 
642 642
 Included templates have access to the variables of the active context.
643 643
 
644
-An included file can be evaluated in the sandbox environment by appending
645
-`sandboxed` at the end if the `escaper` extension has been enabled:
646
-
647
-    [twig]
648
-    {% include 'user.html' sandboxed %}
649
-
650 644
 You can also restrict the variables passed to the template by explicitly pass
651 645
 them as an array:
652 646
 
... ...
@@ -656,16 +650,15 @@ them as an array:
656 650
     {% set vars as ['foo': 'bar'] %}
657 651
     {% include 'foo' with vars %}
658 652
 
659
-The most secure way to include a template is to use both the `sandboxed` mode,
660
-and to pass the minimum amount of variables needed for the template to be
661
-rendered correctly:
662
-
663
-    [twig]
664
-    {% include 'foo' sandboxed with vars %}
665
-
666 653
 >**NOTE**
667 654
 >The `with` keyword is supported as of Twig 0.9.5.
668 655
 
656
+-
657
+
658
+>**TIP**
659
+>When including a template created by an end user, you should consider
660
+>sandboxing it. More information in the "Twig for Developers" chapter.
661
+
669 662
 ### Import
670 663
 
671 664
 Twig supports putting often used code into macros. These macros can go into
... ...
@@ -389,10 +389,12 @@ The policy object is the first argument of the sandbox constructor:
389 389
     $twig->addExtension($sandbox);
390 390
 
391 391
 By default, the sandbox mode is disabled and should be enabled when including
392
-untrusted templates:
392
+untrusted template code by using the `sandbox` tag:
393 393
 
394
-    [php]
395
-    {% include "user.html" sandboxed %}
394
+    [twig]
395
+    {% sandbox %}
396
+      {% include 'user.html' %}
397
+    {% endsandbox %}
396 398
 
397 399
 You can sandbox all templates by passing `true` as the second argument of the
398 400
 extension constructor:
... ...
@@ -481,22 +481,16 @@ The `Project_Set_Node` class itself is rather simple:
481 481
     [php]
482 482
     class Project_Set_Node extends Twig_Node
483 483
     {
484
-      protected $name;
485
-      protected $value;
486
-
487 484
       public function __construct($name, Twig_Node_Expression $value, $lineno)
488 485
       {
489
-        parent::__construct($lineno);
490
-
491
-        $this->name = $name;
492
-        $this->value = $value;
486
+        parent::__construct(array('value' => $value), array('name' => $name), $lineno);
493 487
       }
494 488
 
495 489
       public function compile($compiler)
496 490
       {
497 491
         $compiler
498 492
           ->addDebugInfo($this)
499
-          ->write('$context[\''.$this->name.'\'] = ')
493
+          ->write('$context[\''.$this['name'].'\'] = ')
500 494
           ->subcompile($this->value)
501 495
           ->raw(";\n")
502 496
         ;
... ...
@@ -533,7 +527,7 @@ developer generate beautiful and readable PHP code:
533 527
  * `outdent()`: Outdents the generated code (see `Twig_Node_Block` for a usage
534 528
    example).
535 529
 
536
-Creating a Node Transformer
530
+Creating a Node Visitor
531
+-----------------------
537 532
 
538 533
 To be written...
... ...
@@ -37,11 +37,9 @@ class Twig_Autoloader
37 37
     static public function autoload($class)
38 38
     {
39 39
         if (0 !== strpos($class, 'Twig')) {
40
-            return false;
40
+            return;
41 41
         }
42 42
 
43 43
         require dirname(__FILE__).'/../'.str_replace('_', '/', $class).'.php';
44
-
45
-        return true;
46 44
     }
47 45
 }
... ...
@@ -42,6 +42,16 @@ class Twig_Compiler implements Twig_CompilerInterface
42 42
     }
43 43
 
44 44
     /**
45
+     * Returns the environment instance related to this compiler.
46
+     *
47
+     * @return Twig_Environment The environment instance
48
+     */
49
+    public function getEnvironment()
50
+    {
51
+        return $this->env;
52
+    }
53
+
54
+    /**
45 55
      * Gets the current PHP code after compilation.
46 56
      *
47 57
      * @return string The PHP code
... ...
@@ -211,19 +221,4 @@ class Twig_Compiler implements Twig_CompilerInterface
211 221
 
212 222
         return $this;
213 223
     }
214
-
215
-    /**
216
-     * Returns the environment instance related to this compiler.
217
-     *
218
-     * @return Twig_Environment The environment instance
219
-     */
220
-    public function getEnvironment()
221
-    {
222
-        return $this->env;
223
-    }
224
-
225
-    public function getTemplateClass($name)
226
-    {
227
-        return $this->getEnvironment()->getTemplateClass($name);
228
-    }
229 224
 }
... ...
@@ -178,13 +178,14 @@ class Twig_Environment
178 178
     /**
179 179
      * Loads a template by name.
180 180
      *
181
-     * @param  string $name The template name
181
+     * @param  string  $name  The template name
182
+     * @param  Boolean $macro Whether to return the macro object if any, or the template one
182 183
      *
183 184
      * @return Twig_TemplateInterface A template instance representing the given template name
184 185
      */
185
-    public function loadTemplate($name)
186
+    public function loadTemplate($name, $macro = false)
186 187
     {
187
-        $cls = $this->getTemplateClass($name);
188
+        $cls = $this->getTemplateClass($name).($macro ? '_Macro' : '');
188 189
 
189 190
         if (isset($this->loadedTemplates[$cls])) {
190 191
             return $this->loadedTemplates[$cls];
... ...
@@ -309,6 +310,10 @@ class Twig_Environment
309 310
 
310 311
     public function getExtension($name)
311 312
     {
313
+        if (!isset($this->extensions[$name])) {
314
+            throw new LogicException(sprintf('The "%s" extension is not enabled.', $name));
315
+        }
316
+
312 317
         return $this->extensions[$name];
313 318
     }
314 319
 
... ...
@@ -86,14 +86,15 @@ class Twig_ExpressionParser
86 86
             ||
87 87
             $this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'in')
88 88
         ) {
89
-            $ops[] = array($this->parser->getStream()->next()->getValue(), $this->parseAddExpression());
89
+            $ops[] = new Twig_Node_Expression_Constant($this->parser->getStream()->next()->getValue(), $lineno);
90
+            $ops[] = $this->parseAddExpression();
90 91
         }
91 92
 
92 93
         if (empty($ops)) {
93 94
             return $expr;
94 95
         }
95 96
 
96
-        return new Twig_Node_Expression_Compare($expr, $ops, $lineno);
97
+        return new Twig_Node_Expression_Compare($expr, new Twig_Node($ops), $lineno);
97 98
     }
98 99
 
99 100
     public function parseAddExpression()
... ...
@@ -279,6 +280,7 @@ class Twig_ExpressionParser
279 280
                     throw new Twig_SyntaxError(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::getTypeAsString($token->getType()), $token->getValue()), $token->getLine());
280 281
                 }
281 282
         }
283
+
282 284
         if (!$assignment) {
283 285
             $node = $this->parsePostfixExpression($node);
284 286
         }
... ...
@@ -358,14 +360,16 @@ class Twig_ExpressionParser
358 360
 
359 361
         $end = $this->parseExpression();
360 362
 
361
-        return new Twig_Node_Expression_Filter($node, array(array('range', array($end))), $lineno);
363
+        $filters = new Twig_Node(array(new Twig_Node_Expression_Constant('range', $lineno), new Twig_Node(array($end))));
364
+
365
+        return new Twig_Node_Expression_Filter($node, $filters, $lineno);
362 366
     }
363 367
 
364 368
     public function parseSubscriptExpression($node)
365 369
     {
366 370
         $token = $this->parser->getStream()->next();
367 371
         $lineno = $token->getLine();
368
-        $arguments = array();
372
+        $arguments = new Twig_Node();
369 373
         if ($token->getValue() == '.') {
370 374
             $token = $this->parser->getStream()->next();
371 375
             if ($token->getType() == Twig_Token::NAME_TYPE || $token->getType() == Twig_Token::NUMBER_TYPE) {
... ...
@@ -398,7 +402,8 @@ class Twig_ExpressionParser
398 402
         while (true) {
399 403
             $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE);
400 404
 
401
-            $filters[] = array($token->getValue(), $this->parseArguments());
405
+            $filters[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
406
+            $filters[] = $this->parseArguments();
402 407
 
403 408
             if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '|')) {
404 409
                 break;
... ...
@@ -407,13 +412,13 @@ class Twig_ExpressionParser
407 412
             $this->parser->getStream()->next();
408 413
         }
409 414
 
410
-        return $filters;
415
+        return new Twig_Node($filters);
411 416
     }
412 417
 
413 418
     public function parseArguments()
414 419
     {
415 420
         if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '(')) {
416
-            return array();
421
+            return new Twig_Node();
417 422
         }
418 423
 
419 424
         $args = array();
... ...
@@ -426,14 +431,13 @@ class Twig_ExpressionParser
426 431
         }
427 432
         $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ')');
428 433
 
429
-        return $args;
434
+        return new Twig_Node($args);
430 435
     }
431 436
 
432 437
     public function parseAssignmentExpression()
433 438
     {
434 439
         $lineno = $this->parser->getCurrentToken()->getLine();
435 440
         $targets = array();
436
-        $is_multitarget = false;
437 441
         while (true) {
438 442
             if (!empty($targets)) {
439 443
                 $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ',');
... ...
@@ -449,13 +453,9 @@ class Twig_ExpressionParser
449 453
             if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ',')) {
450 454
                 break;
451 455
             }
452
-            $is_multitarget = true;
453
-        }
454
-        if (!$is_multitarget && count($targets) == 1) {
455
-            return array(false, $targets[0]);
456 456
         }
457 457
 
458
-        return array(true, $targets);
458
+        return new Twig_Node($targets);
459 459
     }
460 460
 
461 461
     public function parseMultitargetExpression()
... ...
@@ -479,10 +479,7 @@ class Twig_ExpressionParser
479 479
             }
480 480
             $is_multitarget = true;
481 481
         }
482
-        if (!$is_multitarget && count($targets) == 1) {
483
-            return array(false, $targets[0]);
484
-        }
485 482
 
486
-        return array(true, $targets);
483
+        return array($is_multitarget, new Twig_Node($targets));
487 484
     }
488 485
 }
... ...
@@ -34,16 +34,6 @@ class Twig_Extension_Core extends Twig_Extension
34 34
     }
35 35
 
36 36
     /**
37
-     * Returns the node visitor instances to add to the existing list.
38
-     *
39
-     * @return array An array of Twig_NodeVisitorInterface instances
40
-     */
41
-    public function getNodeVisitors()
42
-    {
43
-        return array(new Twig_NodeVisitor_Filter());
44
-    }
45
-
46
-    /**
47 37
      * Returns a list of filters to add to the existing list.
48 38
      *
49 39
      * @return array An array of filters
... ...
@@ -21,6 +21,16 @@ class Twig_Extension_Sandbox extends Twig_Extension
21 21
     }
22 22
 
23 23
     /**
24
+     * Returns the token parser instance to add to the existing list.
25
+     *
26
+     * @return array An array of Twig_TokenParser instances
27
+     */
28
+    public function getTokenParsers()
29
+    {
30
+        return array(new Twig_TokenParser_Sandbox());
31
+    }
32
+
33
+    /**
24 34
      * Returns the node visitor instances to add to the existing list.
25 35
      *
26 36
      * @return array An array of Twig_NodeVisitorInterface instances
... ...
@@ -17,20 +17,57 @@
17 17
  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
18 18
  * @version    SVN: $Id$
19 19
  */
20
-abstract class Twig_Node implements Twig_NodeInterface
20
+class Twig_Node implements Twig_NodeInterface, ArrayAccess, Countable, Iterator
21 21
 {
22
+    protected $nodes;
23
+    protected $attributes;
22 24
     protected $lineno;
23 25
     protected $tag;
24 26
 
25
-    public function __construct($lineno, $tag = null)
27
+    public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null)
26 28
     {
29
+        $this->nodes = array();
30
+        foreach ($nodes as $name => $node) {
31
+            $this->$name = $node;
32
+        }
33
+        $this->attributes = $attributes;
27 34
         $this->lineno = $lineno;
28 35
         $this->tag = $tag;
29 36
     }
30 37
 
31 38
     public function __toString()
32 39
     {
33
-        return get_class($this).'()';
40
+        $attributes = array();
41
+        foreach ($this->attributes as $name => $value) {
42
+            $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true)));
43
+        }
44
+
45
+        $repr = array(get_class($this).'('.implode(', ', $attributes));
46
+
47
+        if (count($this->nodes)) {
48
+            foreach ($this->nodes as $name => $node) {
49
+                $len = strlen($name) + 4;
50
+                $noderepr = array();
51
+                foreach (explode("\n", (string) $node) as $line) {
52
+                    $noderepr[] = str_repeat(' ', $len).$line;
53
+                }
54
+
55
+                $repr[] = sprintf('  %s: %s', $name, ltrim(implode("\n", $noderepr)));
56
+            }
57
+
58
+            $repr[] = ')';
59
+        } else {
60
+            $repr[0] .= ')';
61
+        }
62
+
63
+        return implode("\n", $repr);
64
+    }
65
+
66
+    public function compile($compiler)
67
+    {
68
+        foreach ($this->nodes as $node) {
69
+            $node->compile($compiler);
70
+        }
34 71
     }
35 72
 
36 73
     public function getLine()
... ...
@@ -42,4 +79,132 @@ abstract class Twig_Node implements Twig_NodeInterface
42 79
     {
43 80
         return $this->tag;
44 81
     }
82
+
83
+    /**
84
+     * Returns true if the attribute is defined.
85
+     *
86
+     * @param  string  The attribute name
87
+     *
88
+     * @return Boolean true if the attribute is defined, false otherwise
89
+     */
90
+    public function offsetExists($name)
91
+    {
92
+        return $this->attributes[$name];
93
+    }
94
+
95
+    /**
96
+     * Gets an attribute.
97
+     *
98
+     * @param  string The attribute name
99
+     *
100
+     * @return mixed  The attribute value
101
+     */
102
+    public function offsetGet($name)
103
+    {
104
+        if (!array_key_exists($name, $this->attributes)) {
105
+            throw new InvalidArgumentException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this)));
106
+        }
107
+
108
+        return $this->attributes[$name];
109
+    }
110
+
111
+    /**
112
+     * Sets an attribute.
113
+     *
114
+     * @param string The attribute name
115
+     * @param mixed  The attribute value
116
+     */
117
+    public function offsetSet($name, $value)
118
+    {
119
+        $this->attributes[$name] = $value;
120
+    }
121
+
122
+    /**
123
+     * Removes an attribute.
124
+     *
125
+     * @param string The attribute name
126
+     */
127
+    public function offsetUnset($name)
128
+    {
129
+        unset($this->attributes[$name]);
130
+    }
131
+
132
+    /**
133
+     * Returns true if the node with the given identifier exists.
134
+     *
135
+     * @param  string  The node name
136
+     *
137
+     * @return Boolean true if the node with the given name exists, false otherwise
138
+     */
139
+    public function __isset($name)
140
+    {
141
+        return array_key_exists($name, $this->nodes);
142
+    }
143
+
144
+    /**
145
+     * Gets a node by name.
146
+     *
147
+     * @param  string The node name
148
+     *
149
+     * @return Twig_Node A Twig_Node instance
150
+     */
151
+    public function __get($name)
152
+    {
153
+        if (!array_key_exists($name, $this->nodes)) {
154
+            throw new InvalidArgumentException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this)));
155
+        }
156
+
157
+        return $this->nodes[$name];
158
+    }
159
+
160
+    /**
161
+     * Sets a node.
162
+     *
163
+     * @param string    The node name
164
+     * @param Twig_Node A Twig_Node instance
165
+     */
166
+    public function __set($name, $node = null)
167
+    {
168
+        $this->nodes[$name] = $node;
169
+    }
170
+
171
+    /**
172
+     * Removes a node by name.
173
+     *
174
+     * @param string The node name
175
+     */
176
+    public function __unset($name)
177
+    {
178
+        unset($this->nodes[$name]);
179
+    }
180
+
181
+    public function count()
182
+    {
183
+        return count($this->nodes);
184
+    }
185
+
186
+    public function rewind()
187
+    {
188
+        reset($this->nodes);
189
+    }
190
+
191
+    public function current()
192
+    {
193
+        return current($this->nodes);
194
+    }
195
+
196
+    public function key()
197
+    {
198
+        return key($this->nodes);
199
+    }
200
+
201
+    public function next()
202
+    {
203
+        return next($this->nodes);
204
+    }
205
+
206
+    public function valid()
207
+    {
208
+        return false !== current($this->nodes);
209
+    }
45 210
 }
... ...
@@ -22,46 +22,15 @@
22 22
  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
23 23
  * @version    SVN: $Id$
24 24
  */
25
-class Twig_Node_AutoEscape extends Twig_Node implements Twig_NodeListInterface
25
+class Twig_Node_AutoEscape extends Twig_Node
26 26
 {
27
-    protected $value;
28
-    protected $body;
29
-
30
-    public function __construct($value, Twig_NodeList $body, $lineno, $tag = null)
31
-    {
32
-        parent::__construct($lineno, $tag);
33
-        $this->value = $value;
34
-        $this->body  = $body;
35
-    }
36
-
37
-    public function __toString()
38
-    {
39
-        $repr = array(get_class($this).'('.($this->value ? 'on' : 'off'));
40
-        foreach (explode("\n", $this->body) as $line) {
41
-            $repr[] = '    '.$line;
42
-        }
43
-        $repr[] = ')';
44
-
45
-        return implode("\n", $repr);
46
-    }
47
-
48
-    public function getNodes()
49
-    {
50
-        return $this->body->getNodes();
51
-    }
52
-
53
-    public function setNodes(array $nodes)
27
+    public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'autoescape')
54 28
     {
55
-        $this->body = new Twig_NodeList($nodes, $this->lineno);
29
+        parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag);
56 30
     }
57 31
 
58 32
     public function compile($compiler)
59 33
     {
60 34
         $compiler->subcompile($this->body);
61 35
     }
62
-
63
-    public function getValue()
64
-    {
65
-        return $this->value;
66
-    }
67 36
 }
... ...
@@ -17,53 +17,18 @@
17 17
  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
18 18
  * @version    SVN: $Id$
19 19
  */
20
-class Twig_Node_Block extends Twig_Node implements Twig_NodeListInterface
20
+class Twig_Node_Block extends Twig_Node
21 21
 {
22
-    protected $name;
23
-    protected $body;
24
-    protected $parent;
25
-
26
-    public function __construct($name, Twig_NodeList $body, $lineno, $parent = null, $tag = null)
27
-    {
28
-        parent::__construct($lineno, $tag);
29
-        $this->name = $name;
30
-        $this->body = $body;
31
-        $this->parent = $parent;
32
-    }
33
-
34
-    public function __toString()
35
-    {
36
-        $repr = array(get_class($this).' '.$this->name.'(');
37
-        foreach ($this->body->getNodes() as $node) {
38
-            foreach (explode("\n", $node->__toString()) as $line) {
39
-                $repr[] = '  '.$line;
40
-            }
41
-        }
42
-        $repr[] = ')';
43
-
44
-        return implode("\n", $repr);
45
-    }
46
-
47
-    public function getNodes()
48
-    {
49
-        return $this->body->getNodes();
50
-    }
51
-
52
-    public function setNodes(array $nodes)
22
+    public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null)
53 23
     {
54
-        $this->body = new Twig_NodeList($nodes, $this->lineno);
55
-    }
56
-
57
-    public function replace($other)
58
-    {
59
-        $this->body = $other->body;
24
+        parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag);
60 25
     }
61 26
 
62 27
     public function compile($compiler)
63 28
     {
64 29
         $compiler
65 30
             ->addDebugInfo($this)
66
-            ->write(sprintf("public function block_%s(\$context)\n", $this->name), "{\n")
31
+            ->write(sprintf("public function block_%s(\$context)\n", $this['name']), "{\n")
67 32
             ->indent()
68 33
         ;
69 34
 
... ...
@@ -73,19 +38,4 @@ class Twig_Node_Block extends Twig_Node implements Twig_NodeListInterface
73 38
             ->write("}\n\n")
74 39
         ;
75 40
     }
76
-
77
-    public function getName()
78
-    {
79
-        return $this->name;
80
-    }
81
-
82
-    public function getParent()
83
-    {
84
-        return $this->parent;
85
-    }
86
-
87
-    public function setParent($parent)
88
-    {
89
-        $this->parent = $parent;
90
-    }
91 41
 }
... ...
@@ -19,29 +19,16 @@
19 19
  */
20 20
 class Twig_Node_BlockReference extends Twig_Node
21 21
 {
22
-    protected $name;
23
-
24 22
     public function __construct($name, $lineno, $tag = null)
25 23
     {
26
-        parent::__construct($lineno, $tag);
27
-        $this->name = $name;
28
-    }
29
-
30
-    public function __toString()
31
-    {
32
-        return get_class($this).'('.$this->name.')';
24
+        parent::__construct(array(), array('name' => $name), $lineno, $tag);
33 25
     }
34 26
 
35 27
     public function compile($compiler)
36 28
     {
37 29
         $compiler
38 30
             ->addDebugInfo($this)
39
-            ->write(sprintf('$this->block_%s($context);'."\n", $this->name))
31
+            ->write(sprintf('$this->block_%s($context);'."\n", $this['name']))
40 32
         ;
41 33
     }
42
-
43
-    public function getName()
44
-    {
45
-        return $this->name;
46
-    }
47 34
 }
... ...
@@ -1,19 +1,26 @@
1 1
 <?php
2 2
 
3
+/*
4
+ * This file is part of Twig.
5
+ *
6
+ * (c) 2010 Fabien Potencier
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+/**
13
+ * Represents a debug node.
14
+ *
15
+ * @package    twig
16
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17
+ * @version    SVN: $Id$
18
+ */
3 19
 class Twig_Node_Debug extends Twig_Node
4 20
 {
5
-    protected $expr;
6
-
7 21
     public function __construct(Twig_Node_Expression $expr = null, $lineno, $tag = null)
8 22
     {
9
-        parent::__construct($lineno, $tag);
10
-
11
-        $this->expr = $expr;
12
-    }
13
-
14
-    public function __toString()
15
-    {
16
-        return get_class($this).'('.$this->expr.')';
23
+        parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
17 24
     }
18 25
 
19 26
     public function compile($compiler)
... ...
@@ -8,45 +8,18 @@
8 8
  * For the full copyright and license information, please view the LICENSE
9 9
  * file that was distributed with this source code.
10 10
  */
11
-class Twig_Node_Expression_Array extends Twig_Node_Expression implements Twig_NodeListInterface
11
+class Twig_Node_Expression_Array extends Twig_Node_Expression
12 12
 {
13
-    protected $elements;
14
-
15
-    public function __construct($elements, $lineno)
13
+    public function __construct(array $elements, $lineno)
16 14
     {
17
-        parent::__construct($lineno);
18
-
19
-        $this->elements = $elements;
20
-    }
21
-
22
-    public function __toString()
23
-    {
24
-        $repr = array(get_class($this).'(');
25
-        foreach ($this->elements as $name => $node) {
26
-            foreach (explode("\n", '\''.$name.'\' => '.$node) as $line) {
27
-                $repr[] = '  '.$line;
28
-            }
29
-        }
30
-        $repr[] = ')';
31
-
32
-        return implode("\n", $repr);
33
-    }
34
-
35
-    public function getNodes()
36
-    {
37
-        return $this->elements;
38
-    }
39
-
40
-    public function setNodes(array $nodes)
41
-    {
42
-        $this->elements = $nodes;
15
+        parent::__construct($elements, array(), $lineno);
43 16
     }
44 17
 
45 18
     public function compile($compiler)
46 19
     {
47 20
         $compiler->raw('array(');
48 21
         $first = true;
49
-        foreach ($this->elements as $name => $node) {
22
+        foreach ($this->nodes as $name => $node) {
50 23
             if (!$first) {
51 24
                 $compiler->raw(', ');
52 25
             }
... ...
@@ -60,9 +33,4 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression implements Twig_No
60 33
         }
61 34
         $compiler->raw(')');
62 35
     }
63
-
64
-    public function getElements()
65
-    {
66
-        return $this->elements;
67
-    }
68 36
 }
... ...
@@ -14,6 +14,6 @@ class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name
14 14
 {
15 15
     public function compile($compiler)
16 16
     {
17
-        $compiler->raw(sprintf('$context[\'%s\']', $this->name));
17
+        $compiler->raw(sprintf('$context[\'%s\']', $this['name']));
18 18
     }
19 19
 }
... ...
@@ -11,33 +11,9 @@
11 11
  */
12 12
 abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression
13 13
 {
14
-    protected $left;
15
-    protected $right;
16
-
17 14
     public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, $lineno)
18 15
     {
19
-        parent::__construct($lineno);
20
-        $this->left = $left;
21
-        $this->right = $right;
22
-    }
23
-
24
-    public function __toString()
25
-    {
26
-        $repr = array(get_class($this).'(');
27
-
28
-        foreach (explode("\n", $this->left->__toString()) as $line) {
29
-            $repr[] = '  '.$line;
30
-        }
31
-
32
-        $repr[] = ', ';
33
-
34
-        foreach (explode("\n", $this->right->__toString()) as $line) {
35
-            $repr[] = '  '.$line;
36
-        }
37
-
38
-        $repr[] = ')';
39
-
40
-        return implode("\n", $repr);
16
+        parent::__construct(array('left' => $left, 'right' => $right), array(), $lineno);
41 17
     }
42 18
 
43 19
     public function compile($compiler)
... ...
@@ -12,17 +12,13 @@ class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary
12 12
 {
13 13
     public function compile($compiler)
14 14
     {
15
-        $compiler
16
-            ->raw('floor(')
17
-            ->subcompile($this->left)
18
-            ->raw(' / ')
19
-            ->subcompile($this->right)
20
-            ->raw(')')
21
-        ;
15
+        $compiler->raw('floor(');
16
+        parent::compile($compiler);
17
+        $compiler->raw(')');
22 18
     }
23 19
 
24 20
     public function operator($compiler)
25 21
     {
26
-        return;
22
+        return $compiler->raw('/');
27 23
     }
28 24
 }
... ...
@@ -11,44 +11,39 @@
11 11
  */
12 12
 class Twig_Node_Expression_Compare extends Twig_Node_Expression
13 13
 {
14
-    protected $expr;
15
-    protected $ops;
16
-
17
-    public function __construct(Twig_Node_Expression $expr, $ops, $lineno)
14
+    public function __construct(Twig_Node_Expression $expr, Twig_NodeInterface $ops, $lineno)
18 15
     {
19
-        parent::__construct($lineno);
20
-        $this->expr = $expr;
21
-        $this->ops = $ops;
16
+        parent::__construct(array('expr' => $expr, 'ops' => $ops), array(), $lineno);
22 17
     }
23 18
 
24 19
     public function compile($compiler)
25 20
     {
26
-        $nbOps = count($this->ops) > 1;
27
-        if ('in' === $this->ops[0][0]) {
21
+        if ('in' === $this->ops->{0}['value']) {
28 22
             return $this->compileIn($compiler);
29 23
         }
30 24
 
31 25
         $this->expr->compile($compiler);
32
-        $i = 0;
33
-        foreach ($this->ops as $op) {
34
-            if ($i) {
35
-                $compiler->raw(' && ($tmp'.$i);
26
+
27
+        $nbOps = count($this->ops);
28
+        for ($i = 0; $i < $nbOps; $i += 2) {
29
+            if ($i > 0) {
30
+                $compiler->raw(' && ($tmp'.($i / 2));
36 31
             }
37
-            list($op, $node) = $op;
38
-            $compiler->raw(' '.$op.' ');
39 32
 
40
-            if ($nbOps) {
33
+            $compiler->raw(' '.$this->ops->{$i}['value'].' ');
34
+
35
+            if ($i != $nbOps - 2) {
41 36
                 $compiler
42
-                    ->raw('($tmp'.++$i.' = ')
43
-                    ->subcompile($node)
37
+                    ->raw('($tmp'.(($i / 2) + 1).' = ')
38
+                    ->subcompile($this->ops->{($i + 1)})
44 39
                     ->raw(')')
45 40
                 ;
46 41
             } else {
47
-                $compiler->subcompile($node);
42
+                $compiler->subcompile($this->ops->{($i + 1)});
48 43
             }
49 44
         }
50 45
 
51
-        for ($j = 1; $j < $i; $j++) {
46
+        for ($j = 1; $j < $i / 2; $j++) {
52 47
             $compiler->raw(')');
53 48
         }
54 49
     }
... ...
@@ -59,7 +54,7 @@ class Twig_Node_Expression_Compare extends Twig_Node_Expression
59 54
             ->raw('twig_in_filter(')
60 55
             ->subcompile($this->expr)
61 56
             ->raw(', ')
62
-            ->subcompile($this->ops[0][1])
57
+            ->subcompile($this->ops->{1})
63 58
             ->raw(')')
64 59
         ;
65 60
     }
... ...
@@ -11,16 +11,9 @@
11 11
  */
12 12
 class Twig_Node_Expression_Conditional extends Twig_Node_Expression
13 13
 {
14
-    protected $expr1;
15
-    protected $expr2;
16
-    protected $expr3;
17
-
18 14
     public function __construct(Twig_Node_Expression $expr1, Twig_Node_Expression $expr2, Twig_Node_Expression $expr3, $lineno)
19 15
     {
20
-        parent::__construct($lineno);
21
-        $this->expr1 = $expr1;
22
-        $this->expr2 = $expr2;
23
-        $this->expr3 = $expr3;
16
+        parent::__construct(array('expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3), array(), $lineno);
24 17
     }
25 18
 
26 19
     public function compile($compiler)
... ...
@@ -11,26 +11,13 @@
11 11
  */
12 12
 class Twig_Node_Expression_Constant extends Twig_Node_Expression
13 13
 {
14
-    protected $value;
15
-
16 14
     public function __construct($value, $lineno)
17 15
     {
18
-        parent::__construct($lineno);
19
-        $this->value = $value;
20
-    }
21
-
22
-    public function __toString()
23
-    {
24
-        return get_class($this).'(\''.$this->value.'\')';
16
+        parent::__construct(array(), array('value' => $value), $lineno);
25 17
     }
26 18
 
27 19
     public function compile($compiler)
28 20
     {
29
-        $compiler->repr($this->value);
30
-    }
31
-
32
-    public function getValue()
33
-    {
34
-        return $this->value;
21
+        $compiler->repr($this['value']);
35 22
     }
36 23
 }
... ...
@@ -9,45 +9,11 @@
9 9
  * For the full copyright and license information, please view the LICENSE
10 10
  * file that was distributed with this source code.
11 11
  */
12
-class Twig_Node_Expression_Filter extends Twig_Node_Expression implements Twig_NodeListInterface
12
+class Twig_Node_Expression_Filter extends Twig_Node_Expression
13 13
 {
14
-    protected $node;
15
-    protected $filters;
16
-
17
-    public function __construct(Twig_NodeInterface $node, array $filters, $lineno, $tag = null)
18
-    {
19
-        parent::__construct($lineno, $tag);
20
-
21
-        $this->node = $node;
22
-        $this->filters = $filters;
23
-    }
24
-
25
-    public function __toString()
26
-    {
27
-        $filters = array();
28
-        foreach ($this->filters as $filter) {
29
-            $filters[] = $filter[0].'('.implode(', ', $filter[1]).')';
30
-        }
31
-
32
-        $repr = array(get_class($this).'('.implode(', ', $filters));
33
-
34
-        foreach (explode("\n", $this->node->__toString()) as $line) {
35
-            $repr[] = '  '.$line;
36
-        }
37
-
38
-        $repr[] = ')';
39
-
40
-        return implode("\n", $repr);
41
-    }
42
-
43
-    public function getNodes()
44
-    {
45
-        return array($this->node);
46
-    }
47
-
48
-    public function setNodes(array $nodes)
14
+    public function __construct(Twig_NodeInterface $node, Twig_NodeInterface $filters, $lineno, $tag = null)
49 15
     {
50
-        $this->node = $nodes[0];
16
+        parent::__construct(array('node' => $node, 'filters' => $filters), array(), $lineno, $tag);
51 17
     }
52 18
 
53 19
     public function compile($compiler)
... ...
@@ -55,20 +21,19 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression implements Twig_N
55 21
         $filterMap = $compiler->getEnvironment()->getFilters();
56 22
 
57 23
         $postponed = array();
58
-        for ($i = count($this->filters) - 1; $i >= 0; --$i) {
59
-            list($name, $attrs) = $this->filters[$i];
24
+        for ($i = count($this->filters) - 1; $i >= 0; $i -= 2) {
25
+            $name = $this->filters->{$i - 1}['value'];
26
+            $attrs = $this->filters->{$i};
60 27
             if (!isset($filterMap[$name])) {
61
-                $compiler
62
-                    ->raw('$this->resolveMissingFilter(')
63
-                    ->repr($name)
64
-                    ->raw(', ')
65
-                ;
28
+                throw new Twig_SyntaxError(sprintf('The filter "%s" does not exist', $name), $this->getLine());
66 29
             } else {
67 30
                 $compiler->raw($filterMap[$name]->compile().($filterMap[$name]->needsEnvironment() ? '($this->getEnvironment(), ' : '('));
68 31
             }
69 32
             $postponed[] = $attrs;
70 33
         }
34
+
71 35
         $this->node->compile($compiler);
36
+
72 37
         foreach (array_reverse($postponed) as $attributes) {
73 38
             foreach ($attributes as $node) {
74 39
                 $compiler
... ...
@@ -80,35 +45,40 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression implements Twig_N
80 45
         }
81 46
     }
82 47
 
83
-    public function getFilters()
48
+    public function prependFilter(Twig_Node_Expression_Constant $name, Twig_Node $end)
84 49
     {
85
-        return $this->filters;
86
-    }
50
+        $filters = array($name, $end);
51
+        foreach ($this->filters as $node) {
52
+            $filters[] = $node;
53
+        }
87 54
 
88
-    public function setFilters(array $filters)
89
-    {
90
-        $this->filters = $filters;
55
+        $this->filters = new Twig_Node($filters, array(), $this->filters->getLine());
91 56
     }
92 57
 
93
-    public function prependFilter($filter)
58
+    public function appendFilter(Twig_Node_Expression_Constant $name, Twig_Node $end)
94 59
     {
95
-        $this->filters = array_merge(array($filter), $this->filters);
96
-    }
60
+        $filters = array();
61
+        foreach ($this->filters as $node) {
62
+            $filters[] = $node;
63
+        }
97 64
 
98
-    public function appendFilter($filter)
99
-    {
100
-        $this->filters[] = $filter;
65
+        $filters[] = $name;
66
+        $filters[] = $end;
67
+
68
+        $this->filters = new Twig_Node($filters, array(), $this->filters->getLine());
101 69
     }
102 70
 
103
-    public function appendFilters(array $filters)
71
+    public function appendFilters(Twig_NodeInterface $filters)
104 72
     {
105
-        $this->filters = array_merge($this->filters, $filters);
73
+        for ($i = 0; $i < count($filters); $i += 2) {
74
+            $this->appendFilter($filters->{$i}, $filters->{$i + 1});
75
+        }
106 76
     }
107 77
 
108 78
     public function hasFilter($name)
109 79
     {
110
-        foreach ($this->filters as $filter) {
111
-            if ($name == $filter[0]) {
80
+        for ($i = 0; $i < count($this->filters); $i += 2) {
81
+            if ($name == $this->filters->{$i}['value']) {
112 82
                 return true;
113 83
             }
114 84
         }
... ...
@@ -9,39 +9,11 @@
9 9
  * For the full copyright and license information, please view the LICENSE
10 10
  * file that was distributed with this source code.
11 11
  */
12
-class Twig_Node_Expression_GetAttr extends Twig_Node_Expression implements Twig_NodeListInterface
12
+class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
13 13
 {
14
-    protected $node;
15
-    protected $attr;
16
-    protected $arguments;
17
-
18
-    public function __construct(Twig_NodeInterface $node, $attr, $arguments, $lineno, $token_value)
19
-    {
20
-        parent::__construct($lineno);
21
-        $this->node = $node;
22
-        $this->attr = $attr;
23
-        $this->arguments = $arguments;
24
-        $this->token_value = $token_value;
25
-    }
26
-
27
-    public function __toString()
28
-    {
29
-        return get_class($this).'('.$this->node.', '.$this->attr.')';
30
-    }
31
-
32
-    public function getNode()
33
-    {
34
-        return $this->node;
35
-    }
36
-
37
-    public function getNodes()
38
-    {
39
-        return array($this->node);
40
-    }
41
-
42
-    public function setNodes(array $nodes)
14
+    public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_NodeInterface $arguments, $lineno, $token_value = null)
43 15
     {
44
-        $this->node = $nodes[0];
16
+        parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('token_value' => $token_value), $lineno);
45 17
     }
46 18
 
47 19
     public function compile($compiler)
... ...
@@ -50,7 +22,7 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression implements Twig_
50 22
             ->raw('$this->getAttribute(')
51 23
             ->subcompile($this->node)
52 24
             ->raw(', ')
53
-            ->subcompile($this->attr)
25
+            ->subcompile($this->attribute)
54 26
             ->raw(', array(')
55 27
         ;
56 28
 
... ...
@@ -64,7 +36,7 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression implements Twig_
64 36
         $compiler->raw(')');
65 37
 
66 38
         // Don't look for functions if they're using foo[bar]
67
-        if ('[' == $this->token_value) {
39
+        if ('[' == $this['token_value']) {
68 40
             $compiler->raw(', true');
69 41
         }
70 42
 
... ...
@@ -11,26 +11,13 @@
11 11
  */
12 12
 class Twig_Node_Expression_Name extends Twig_Node_Expression
13 13
 {
14
-    protected $name;
15
-
16 14
     public function __construct($name, $lineno)
17 15
     {
18
-        parent::__construct($lineno);
19
-        $this->name = $name;
20
-    }
21
-
22
-    public function __toString()
23
-    {
24
-        return get_class($this).'(\''.$this->name.'\')';
16
+        parent::__construct(array(), array('name' => $name), $lineno);
25 17
     }
26 18
 
27 19
     public function compile($compiler)
28 20
     {
29
-        $compiler->raw(sprintf('$this->getContext($context, \'%s\')', $this->name, $this->name));
30
-    }
31
-
32
-    public function getName()
33
-    {
34
-        return $this->name;
21
+        $compiler->raw(sprintf('$this->getContext($context, \'%s\')', $this['name'], $this['name']));
35 22
     }
36 23
 }
... ...
@@ -11,25 +11,9 @@
11 11
  */
12 12
 abstract class Twig_Node_Expression_Unary extends Twig_Node_Expression
13 13
 {
14
-    protected $node;
15
-
16 14
     public function __construct(Twig_NodeInterface $node, $lineno)
17 15
     {
18
-        parent::__construct($lineno);
19
-        $this->node = $node;
20
-    }
21
-
22
-    public function __toString()
23
-    {
24
-        $repr = array(get_class($this).'(');
25
-
26
-        foreach (explode("\n", $this->node->__toString()) as $line) {
27
-            $repr[] = '  '.$line;
28
-        }
29
-
30
-        $repr[] = ')';
31
-
32
-        return implode("\n", $repr);
16
+        parent::__construct(array('node' => $node), array(), $lineno);
33 17
     }
34 18
 
35 19
     public function compile($compiler)
36 20
deleted file mode 100644
... ...
@@ -1,68 +0,0 @@
1
-<?php
2
-
3
-/*
4
- * This file is part of Twig.
5
- *
6
- * (c) 2009 Fabien Potencier
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
-
12
-/**
13
- * Represents a filter node.
14
- *
15
- * @package    twig
16
- * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17
- * @version    SVN: $Id$
18
- */
19
-class Twig_Node_Filter extends Twig_Node implements Twig_NodeListInterface
20
-{
21
-    protected $filters;
22
-    protected $body;
23
-
24
-    public function __construct($filters, Twig_NodeList $body, $lineno, $tag = null)
25
-    {
26
-        parent::__construct($lineno, $tag);
27
-        $this->filters = $filters;
28
-        $this->body  = $body;
29
-    }
30
-
31
-    public function __toString()
32
-    {
33
-        $filters = array();
34
-        foreach ($this->filters as $filter) {
35
-            $filters[] = $filter[0].'('.implode(', ', $filter[1]).')';
36
-        }
37
-
38
-        $repr = array(get_class($this).'('.implode(', ', $filters));
39
-
40
-        foreach (explode("\n", $this->body->__toString()) as $line) {
41
-            $repr[] = '  '.$line;
42
-        }
43
-
44
-        $repr[] = ')';
45
-
46
-        return implode("\n", $repr);
47
-    }
48
-
49
-    public function getNodes()
50
-    {
51
-        return $this->body->getNodes();
52
-    }
53
-
54
-    public function setNodes(array $nodes)
55
-    {
56
-        $this->body = new Twig_NodeList($nodes, $this->lineno);
57
-    }
58
-
59
-    public function compile($compiler)
60
-    {
61
-        $compiler->subcompile($this->body);
62
-    }
63
-
64
-    public function getFilters()
65
-    {
66
-        return $this->filters;
67
-    }
68
-}
... ...
@@ -17,35 +17,11 @@
17 17
  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
18 18
  * @version    SVN: $Id$
19 19
  */
20
-class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface
20
+class Twig_Node_For extends Twig_Node
21 21
 {
22
-    protected $isMultitarget;
23
-    protected $item;
24
-    protected $seq;
25
-    protected $body;
26
-    protected $else;
27
-    protected $withLoop;
28
-
29
-    public function __construct($isMultitarget, $item, $seq, Twig_NodeList $body, Twig_Node $else = null, $withLoop = false, $lineno, $tag = null)
22
+    public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $withLoop = false, $lineno, $tag = null)
30 23
     {
31
-        parent::__construct($lineno, $tag);
32
-        $this->isMultitarget = $isMultitarget;
33
-        $this->item = $item;
34
-        $this->seq = $seq;
35
-        $this->body = $body;
36
-        $this->else = $else;
37
-        $this->withLoop = $withLoop;
38
-        $this->lineno = $lineno;
39
-    }
40
-
41
-    public function getNodes()
42
-    {
43
-        return $this->body->getNodes();
44
-    }
45
-
46
-    public function setNodes(array $nodes)
47
-    {
48
-        $this->body = new Twig_NodeList($nodes, $this->lineno);
24
+        parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => $withLoop), $lineno, $tag);
49 25
     }
50 26
 
51 27
     public function compile($compiler)
... ...
@@ -60,19 +36,13 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface
60 36
             $compiler->write("\$context['_iterated'] = false;\n");
61 37
         }
62 38
 
63
-        if ($this->isMultitarget) {
64
-            $loopVars = array($this->item[0]->getName(), $this->item[1]->getName());
65
-        } else {
66
-            $loopVars = array('_key', $this->item->getName());
67
-        }
68
-
69 39
         $compiler
70 40
             ->write("\$context['_seq'] = twig_iterator_to_array(")
71 41
             ->subcompile($this->seq)
72
-            ->raw(", ".($this->isMultitarget ? 'true' : 'false').");\n")
42
+            ->raw(", ".(null !== $this->key_target ? 'true' : 'false').");\n")
73 43
         ;
74 44
 
75
-        if ($this->withLoop) {
45
+        if ($this['with_loop']) {
76 46
             $compiler
77 47
                 ->write("\$length = count(\$context['_seq']);\n")
78 48
 
... ...
@@ -90,11 +60,11 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface
90 60
         }
91 61
 
92 62
         $compiler
93
-            ->write("foreach (\$context['_seq'] as \$context[")
94
-            ->repr($loopVars[0])
95
-            ->raw("] => \$context[")
96
-            ->repr($loopVars[1])
97
-            ->raw("]) {\n")
63
+            ->write("foreach (\$context['_seq'] as ")
64
+            ->subcompile($this->key_target)
65
+            ->raw(" => ")
66
+            ->subcompile($this->value_target)
67
+            ->raw(") {\n")
98 68
             ->indent()
99 69
         ;
100 70
 
... ...
@@ -104,7 +74,7 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface
104 74
 
105 75
         $compiler->subcompile($this->body);
106 76
 
107
-        if ($this->withLoop) {
77
+        if ($this['with_loop']) {
108 78
             $compiler
109 79
                 ->write("++\$context['loop']['index0'];\n")
110 80
                 ->write("++\$context['loop']['index'];\n")
... ...
@@ -122,8 +92,7 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface
122 92
 
123 93
         if (!is_null($this->else)) {
124 94
             $compiler
125
-                ->write("if (!\$context['_iterated'])\n")
126
-                ->write("{\n")
95
+                ->write("if (!\$context['_iterated']) {\n")
127 96
                 ->indent()
128 97
                 ->subcompile($this->else)
129 98
                 ->outdent()
... ...
@@ -134,14 +103,9 @@ class Twig_Node_For extends Twig_Node implements Twig_NodeListInterface
134 103
         $compiler->write('$_parent = $context[\'_parent\'];'."\n");
135 104
 
136 105
         // remove some "private" loop variables (needed for nested loops)
137
-        $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$loopVars[0].'\'], $context[\''.$loopVars[1].'\'], $context[\'_parent\'], $context[\'loop\']);'."\n");
106
+        $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->key_target['name'].'\'], $context[\''.$this->value_target['name'].'\'], $context[\'_parent\'], $context[\'loop\']);'."\n");
138 107
 
139 108