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 $streamNode $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 $streamNode $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');