diff vendor/twig/twig/src/TokenParser/ForTokenParser.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents c75dbcec494b
children
line wrap: on
line diff
--- a/vendor/twig/twig/src/TokenParser/ForTokenParser.php	Thu Feb 28 13:11:55 2019 +0000
+++ b/vendor/twig/twig/src/TokenParser/ForTokenParser.php	Thu May 09 15:34:47 2019 +0100
@@ -1,11 +1,136 @@
 <?php
 
+/*
+ * This file is part of Twig.
+ *
+ * (c) Fabien Potencier
+ * (c) Armin Ronacher
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 namespace Twig\TokenParser;
 
-class_exists('Twig_TokenParser_For');
+use Twig\Error\SyntaxError;
+use Twig\Node\Expression\AssignNameExpression;
+use Twig\Node\Expression\ConstantExpression;
+use Twig\Node\Expression\GetAttrExpression;
+use Twig\Node\Expression\NameExpression;
+use Twig\Node\ForNode;
+use Twig\Token;
+use Twig\TokenStream;
 
-if (\false) {
-    class ForTokenParser extends \Twig_TokenParser_For
+/**
+ * Loops over each item of a sequence.
+ *
+ *   <ul>
+ *    {% for user in users %}
+ *      <li>{{ user.username|e }}</li>
+ *    {% endfor %}
+ *   </ul>
+ *
+ * @final
+ */
+class ForTokenParser extends AbstractTokenParser
+{
+    public function parse(Token $token)
     {
+        $lineno = $token->getLine();
+        $stream = $this->parser->getStream();
+        $targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
+        $stream->expect(Token::OPERATOR_TYPE, 'in');
+        $seq = $this->parser->getExpressionParser()->parseExpression();
+
+        $ifexpr = null;
+        if ($stream->nextIf(Token::NAME_TYPE, 'if')) {
+            $ifexpr = $this->parser->getExpressionParser()->parseExpression();
+        }
+
+        $stream->expect(Token::BLOCK_END_TYPE);
+        $body = $this->parser->subparse([$this, 'decideForFork']);
+        if ('else' == $stream->next()->getValue()) {
+            $stream->expect(Token::BLOCK_END_TYPE);
+            $else = $this->parser->subparse([$this, 'decideForEnd'], true);
+        } else {
+            $else = null;
+        }
+        $stream->expect(Token::BLOCK_END_TYPE);
+
+        if (\count($targets) > 1) {
+            $keyTarget = $targets->getNode(0);
+            $keyTarget = new AssignNameExpression($keyTarget->getAttribute('name'), $keyTarget->getTemplateLine());
+            $valueTarget = $targets->getNode(1);
+            $valueTarget = new AssignNameExpression($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine());
+        } else {
+            $keyTarget = new AssignNameExpression('_key', $lineno);
+            $valueTarget = $targets->getNode(0);
+            $valueTarget = new AssignNameExpression($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine());
+        }
+
+        if ($ifexpr) {
+            $this->checkLoopUsageCondition($stream, $ifexpr);
+            $this->checkLoopUsageBody($stream, $body);
+        }
+
+        return new ForNode($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
+    }
+
+    public function decideForFork(Token $token)
+    {
+        return $token->test(['else', 'endfor']);
+    }
+
+    public function decideForEnd(Token $token)
+    {
+        return $token->test('endfor');
+    }
+
+    // the loop variable cannot be used in the condition
+    protected function checkLoopUsageCondition(TokenStream $stream, \Twig_NodeInterface $node)
+    {
+        if ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression && 'loop' == $node->getNode('node')->getAttribute('name')) {
+            throw new SyntaxError('The "loop" variable cannot be used in a looping condition.', $node->getTemplateLine(), $stream->getSourceContext());
+        }
+
+        foreach ($node as $n) {
+            if (!$n) {
+                continue;
+            }
+
+            $this->checkLoopUsageCondition($stream, $n);
+        }
+    }
+
+    // check usage of non-defined loop-items
+    // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include)
+    protected function checkLoopUsageBody(TokenStream $stream, \Twig_NodeInterface $node)
+    {
+        if ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression && 'loop' == $node->getNode('node')->getAttribute('name')) {
+            $attribute = $node->getNode('attribute');
+            if ($attribute instanceof ConstantExpression && \in_array($attribute->getAttribute('value'), ['length', 'revindex0', 'revindex', 'last'])) {
+                throw new SyntaxError(sprintf('The "loop.%s" variable is not defined when looping with a condition.', $attribute->getAttribute('value')), $node->getTemplateLine(), $stream->getSourceContext());
+            }
+        }
+
+        // should check for parent.loop.XXX usage
+        if ($node instanceof ForNode) {
+            return;
+        }
+
+        foreach ($node as $n) {
+            if (!$n) {
+                continue;
+            }
+
+            $this->checkLoopUsageBody($stream, $n);
+        }
+    }
+
+    public function getTag()
+    {
+        return 'for';
     }
 }
+
+class_alias('Twig\TokenParser\ForTokenParser', 'Twig_TokenParser_For');