vendor/twig/twig/src/Node/ForNode.php line 30

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