For.php (4299B)
1 <?php 2 3 /* 4 * This file is part of Twig. 5 * 6 * (c) 2009 Fabien Potencier 7 * (c) 2009 Armin Ronacher 8 * 9 * For the full copyright and license information, please view the LICENSE 10 * file that was distributed with this source code. 11 */ 12 13 /** 14 * Represents a for node. 15 * 16 * @author Fabien Potencier <fabien@symfony.com> 17 */ 18 class Twig_Node_For extends Twig_Node 19 { 20 protected $loop; 21 22 public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_Node_Expression $ifexpr = null, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null) 23 { 24 $body = new Twig_Node(array($body, $this->loop = new Twig_Node_ForLoop($lineno, $tag))); 25 26 if (null !== $ifexpr) { 27 $body = new Twig_Node_If(new Twig_Node(array($ifexpr, $body)), null, $lineno, $tag); 28 } 29 30 parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag); 31 } 32 33 public function compile(Twig_Compiler $compiler) 34 { 35 $compiler 36 ->addDebugInfo($this) 37 ->write("\$context['_parent'] = \$context;\n") 38 ->write("\$context['_seq'] = twig_ensure_traversable(") 39 ->subcompile($this->getNode('seq')) 40 ->raw(");\n") 41 ; 42 43 if (null !== $this->getNode('else')) { 44 $compiler->write("\$context['_iterated'] = false;\n"); 45 } 46 47 if ($this->getAttribute('with_loop')) { 48 $compiler 49 ->write("\$context['loop'] = array(\n") 50 ->write(" 'parent' => \$context['_parent'],\n") 51 ->write(" 'index0' => 0,\n") 52 ->write(" 'index' => 1,\n") 53 ->write(" 'first' => true,\n") 54 ->write(");\n") 55 ; 56 57 if (!$this->getAttribute('ifexpr')) { 58 $compiler 59 ->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {\n") 60 ->indent() 61 ->write("\$length = count(\$context['_seq']);\n") 62 ->write("\$context['loop']['revindex0'] = \$length - 1;\n") 63 ->write("\$context['loop']['revindex'] = \$length;\n") 64 ->write("\$context['loop']['length'] = \$length;\n") 65 ->write("\$context['loop']['last'] = 1 === \$length;\n") 66 ->outdent() 67 ->write("}\n") 68 ; 69 } 70 } 71 72 $this->loop->setAttribute('else', null !== $this->getNode('else')); 73 $this->loop->setAttribute('with_loop', $this->getAttribute('with_loop')); 74 $this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr')); 75 76 $compiler 77 ->write("foreach (\$context['_seq'] as ") 78 ->subcompile($this->getNode('key_target')) 79 ->raw(' => ') 80 ->subcompile($this->getNode('value_target')) 81 ->raw(") {\n") 82 ->indent() 83 ->subcompile($this->getNode('body')) 84 ->outdent() 85 ->write("}\n") 86 ; 87 88 if (null !== $this->getNode('else')) { 89 $compiler 90 ->write("if (!\$context['_iterated']) {\n") 91 ->indent() 92 ->subcompile($this->getNode('else')) 93 ->outdent() 94 ->write("}\n") 95 ; 96 } 97 98 $compiler->write("\$_parent = \$context['_parent'];\n"); 99 100 // remove some "private" loop variables (needed for nested loops) 101 $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n"); 102 103 // keep the values set in the inner context for variables defined in the outer context 104 $compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n"); 105 } 106 }