Dotclear

source: inc/libs/twig/TokenParser/For.php @ 1101:7273894e61b8

Revision 1101:7273894e61b8, 4.7 KB checked in by Dsls <dsls@…>, 13 years ago (diff)

Twig 1.12.2

Line 
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 * Loops over each item of a sequence.
15 *
16 * <pre>
17 * <ul>
18 *  {% for user in users %}
19 *    <li>{{ user.username|e }}</li>
20 *  {% endfor %}
21 * </ul>
22 * </pre>
23 */
24class Twig_TokenParser_For extends Twig_TokenParser
25{
26    /**
27     * Parses a token and returns a node.
28     *
29     * @param Twig_Token $token A Twig_Token instance
30     *
31     * @return Twig_NodeInterface A Twig_NodeInterface instance
32     */
33    public function parse(Twig_Token $token)
34    {
35        $lineno = $token->getLine();
36        $stream = $this->parser->getStream();
37        $targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
38        $stream->expect(Twig_Token::OPERATOR_TYPE, 'in');
39        $seq = $this->parser->getExpressionParser()->parseExpression();
40
41        $ifexpr = null;
42        if ($stream->test(Twig_Token::NAME_TYPE, 'if')) {
43            $stream->next();
44            $ifexpr = $this->parser->getExpressionParser()->parseExpression();
45        }
46
47        $stream->expect(Twig_Token::BLOCK_END_TYPE);
48        $body = $this->parser->subparse(array($this, 'decideForFork'));
49        if ($stream->next()->getValue() == 'else') {
50            $stream->expect(Twig_Token::BLOCK_END_TYPE);
51            $else = $this->parser->subparse(array($this, 'decideForEnd'), true);
52        } else {
53            $else = null;
54        }
55        $stream->expect(Twig_Token::BLOCK_END_TYPE);
56
57        if (count($targets) > 1) {
58            $keyTarget = $targets->getNode(0);
59            $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine());
60            $valueTarget = $targets->getNode(1);
61            $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
62        } else {
63            $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno);
64            $valueTarget = $targets->getNode(0);
65            $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
66        }
67
68        if ($ifexpr) {
69            $this->checkLoopUsageCondition($stream, $ifexpr);
70            $this->checkLoopUsageBody($stream, $body);
71        }
72
73        return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
74    }
75
76    public function decideForFork(Twig_Token $token)
77    {
78        return $token->test(array('else', 'endfor'));
79    }
80
81    public function decideForEnd(Twig_Token $token)
82    {
83        return $token->test('endfor');
84    }
85
86    // the loop variable cannot be used in the condition
87    protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node)
88    {
89        if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
90            throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition', $node->getLine(), $stream->getFilename());
91        }
92
93        foreach ($node as $n) {
94            if (!$n) {
95                continue;
96            }
97
98            $this->checkLoopUsageCondition($stream, $n);
99        }
100    }
101
102    // check usage of non-defined loop-items
103    // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include)
104    protected function checkLoopUsageBody(Twig_TokenStream $stream, Twig_NodeInterface $node)
105    {
106        if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
107            $attribute = $node->getNode('attribute');
108            if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) {
109                throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition', $attribute->getAttribute('value')), $node->getLine(), $stream->getFilename());
110            }
111        }
112
113        // should check for parent.loop.XXX usage
114        if ($node instanceof Twig_Node_For) {
115            return;
116        }
117
118        foreach ($node as $n) {
119            if (!$n) {
120                continue;
121            }
122
123            $this->checkLoopUsageBody($stream, $n);
124        }
125    }
126
127    /**
128     * Gets the tag name associated with this token parser.
129     *
130     * @return string The tag name
131     */
132    public function getTag()
133    {
134        return 'for';
135    }
136}
Note: See TracBrowser for help on using the repository browser.

Sites map