enhanced the for-loop optimizer pass 2 (patch from nikic)
authorFabien Potencier <fabien.potencier@gmail.com>
Mon, 13 Dec 2010 14:46:09 +0000 (15:46 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Mon, 13 Dec 2010 14:46:09 +0000 (15:46 +0100)
lib/Twig/NodeVisitor/Optimizer.php
test/Twig/Tests/NodeVisitor/OptimizerTest.php

index 005dc51..151ba7b 100644 (file)
@@ -86,19 +86,35 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
             // disable the loop variable by default
             $node->setAttribute('with_loop', false);
             array_unshift($this->loops, $node);
-        } elseif ($this->loops
-            // when do we need to add the loop variable back?
-            && (
-                // the loop variable is referenced here
-                ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name'))
-                ||
-                // include without the only attribute
-                ($this->loops && $node instanceof Twig_Node_Include && !$node->getAttribute('only'))
-            )
+        } elseif (!$this->loops) {
+            // we are outside a loop
+            return;
+        }
+
+        // when do we need to add the loop variable back?
+
+        // the loop variable is referenced for the current loop
+        elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
+            $this->addLoopToCurrent();
+        }
+
+        // include without the only attribute
+        elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) {
+            $this->addLoopToAll();
+        }
+
+        // the loop variable is referenced via an attribute
+        elseif ($node instanceof Twig_Node_Expression_GetAttr
+            && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
+                || 'parent' === $node->getNode('attribute')->getAttribute('value')
+               )
+            && (true === $this->loops[0]->getAttribute('with_loop')
+                || ($node->getNode('node') instanceof Twig_Node_Expression_Name
+                    && 'loop' === $node->getNode('node')->getAttribute('name')
+                   )
+               )
         ) {
-            foreach ($this->loops as $loop) {
-                $loop->setAttribute('with_loop', true);
-            }
+            $this->addLoopToAll();
         }
     }
 
@@ -109,6 +125,18 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
         }
     }
 
+    protected function addLoopToCurrent()
+    {
+        $this->loops[0]->setAttribute('with_loop', true);
+    }
+
+    protected function addLoopToAll()
+    {
+        foreach ($this->loops as $loop) {
+            $loop->setAttribute('with_loop', true);
+        }
+    }
+
     /**
      * {@inheritdoc}
      */
index 1501b57..96ccfc7 100644 (file)
@@ -21,7 +21,7 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
         $stream = $env->parse($env->tokenize($template, 'index'));
 
         foreach ($expected as $target => $withLoop) {
-            $this->assertTrue($this->checkForConfiguration($stream, $target, $withLoop));
+            $this->assertTrue($this->checkForConfiguration($stream, $target, $withLoop), sprintf('variable %s is %soptimized', $target, $withLoop ? 'not ' : ''));
         }
     }
 
@@ -38,12 +38,13 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
 
             array('{% for i in foo %}{% include "foo" only %}{% endfor %}', array('i' => false)),
 
-            // "i" should be false
-            array('{% for i in foo %}{% for j in foo %}{{ loop.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => true)),
+            array('{% for i in foo %}{% for j in foo %}{{ loop.index }}{% endfor %}{% endfor %}', array('i' => false, 'j' => true)),
 
             array('{% for i in foo %}{% for j in foo %}{{ loop.parent.loop.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => true)),
 
-            //array('{% for i in foo %}{% for j in foo %}{{ foo.parent.loop.index }}{% endfor %}{% endfor %}', array('i' => false, 'j' => false)),
+            array('{% for i in foo %}{% set l = loop %}{% for j in foo %}{{ l.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => false)),
+
+            array('{% for i in foo %}{% for j in foo %}{{ foo.parent.loop.index }}{% endfor %}{% endfor %}', array('i' => false, 'j' => false)),
 
             array('{% for i in foo %}{% for j in foo %}{{ loop["parent"].loop.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => true)),
         );