vendor/twig/twig/src/TokenParser/ForTokenParser.php line 36

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\TokenParser;
  12. use Twig\Error\SyntaxError;
  13. use Twig\Node\Expression\AssignNameExpression;
  14. use Twig\Node\Expression\ConstantExpression;
  15. use Twig\Node\Expression\GetAttrExpression;
  16. use Twig\Node\Expression\NameExpression;
  17. use Twig\Node\ForNode;
  18. use Twig\Node\Node;
  19. use Twig\Token;
  20. use Twig\TokenStream;
  21. /**
  22. * Loops over each item of a sequence.
  23. *
  24. * <ul>
  25. * {% for user in users %}
  26. * <li>{{ user.username|e }}</li>
  27. * {% endfor %}
  28. * </ul>
  29. */
  30. final class ForTokenParser extends AbstractTokenParser
  31. {
  32. public function parse(Token $token)
  33. {
  34. $lineno = $token->getLine();
  35. $stream = $this->parser->getStream();
  36. $targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
  37. $stream->expect(/* Token::OPERATOR_TYPE */ 8, 'in');
  38. $seq = $this->parser->getExpressionParser()->parseExpression();
  39. $ifexpr = null;
  40. if ($stream->nextIf(/* Token::NAME_TYPE */ 5, 'if')) {
  41. @trigger_error(sprintf('Using an "if" condition on "for" tag in "%s" at line %d is deprecated since Twig 2.10.0, use a "filter" filter or an "if" condition inside the "for" body instead (if your condition depends on a variable updated inside the loop).', $stream->getSourceContext()->getName(), $lineno), \E_USER_DEPRECATED);
  42. $ifexpr = $this->parser->getExpressionParser()->parseExpression();
  43. }
  44. $stream->expect(/* Token::BLOCK_END_TYPE */ 3);
  45. $body = $this->parser->subparse([$this, 'decideForFork']);
  46. if ('else' == $stream->next()->getValue()) {
  47. $stream->expect(/* Token::BLOCK_END_TYPE */ 3);
  48. $else = $this->parser->subparse([$this, 'decideForEnd'], true);
  49. } else {
  50. $else = null;
  51. }
  52. $stream->expect(/* Token::BLOCK_END_TYPE */ 3);
  53. if (\count($targets) > 1) {
  54. $keyTarget = $targets->getNode(0);
  55. $keyTarget = new AssignNameExpression($keyTarget->getAttribute('name'), $keyTarget->getTemplateLine());
  56. $valueTarget = $targets->getNode(1);
  57. $valueTarget = new AssignNameExpression($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine());
  58. } else {
  59. $keyTarget = new AssignNameExpression('_key', $lineno);
  60. $valueTarget = $targets->getNode(0);
  61. $valueTarget = new AssignNameExpression($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine());
  62. }
  63. if ($ifexpr) {
  64. $this->checkLoopUsageCondition($stream, $ifexpr);
  65. $this->checkLoopUsageBody($stream, $body);
  66. }
  67. return new ForNode($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
  68. }
  69. public function decideForFork(Token $token)
  70. {
  71. return $token->test(['else', 'endfor']);
  72. }
  73. public function decideForEnd(Token $token)
  74. {
  75. return $token->test('endfor');
  76. }
  77. // the loop variable cannot be used in the condition
  78. private function checkLoopUsageCondition(TokenStream $stream, Node $node)
  79. {
  80. if ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression && 'loop' == $node->getNode('node')->getAttribute('name')) {
  81. throw new SyntaxError('The "loop" variable cannot be used in a looping condition.', $node->getTemplateLine(), $stream->getSourceContext());
  82. }
  83. foreach ($node as $n) {
  84. if (!$n) {
  85. continue;
  86. }
  87. $this->checkLoopUsageCondition($stream, $n);
  88. }
  89. }
  90. // check usage of non-defined loop-items
  91. // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include)
  92. private function checkLoopUsageBody(TokenStream $stream, Node $node)
  93. {
  94. if ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression && 'loop' == $node->getNode('node')->getAttribute('name')) {
  95. $attribute = $node->getNode('attribute');
  96. if ($attribute instanceof ConstantExpression && \in_array($attribute->getAttribute('value'), ['length', 'revindex0', 'revindex', 'last'])) {
  97. throw new SyntaxError(sprintf('The "loop.%s" variable is not defined when looping with a condition.', $attribute->getAttribute('value')), $node->getTemplateLine(), $stream->getSourceContext());
  98. }
  99. }
  100. // should check for parent.loop.XXX usage
  101. if ($node instanceof ForNode) {
  102. return;
  103. }
  104. foreach ($node as $n) {
  105. if (!$n) {
  106. continue;
  107. }
  108. $this->checkLoopUsageBody($stream, $n);
  109. }
  110. }
  111. public function getTag()
  112. {
  113. return 'for';
  114. }
  115. }
  116. class_alias('Twig\TokenParser\ForTokenParser', 'Twig_TokenParser_For');