Project

General

Profile

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
 */
24
class Twig_TokenParser_For extends Twig_TokenParser
25
{
26
    public function parse(Twig_Token $token)
27
    {
28
        $lineno = $token->getLine();
29
        $stream = $this->parser->getStream();
30
        $targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
31
        $stream->expect(Twig_Token::OPERATOR_TYPE, 'in');
32
        $seq = $this->parser->getExpressionParser()->parseExpression();
33

    
34
        $ifexpr = null;
35
        if ($stream->nextIf(Twig_Token::NAME_TYPE, 'if')) {
36
            $ifexpr = $this->parser->getExpressionParser()->parseExpression();
37
        }
38

    
39
        $stream->expect(Twig_Token::BLOCK_END_TYPE);
40
        $body = $this->parser->subparse(array($this, 'decideForFork'));
41
        if ($stream->next()->getValue() == 'else') {
42
            $stream->expect(Twig_Token::BLOCK_END_TYPE);
43
            $else = $this->parser->subparse(array($this, 'decideForEnd'), true);
44
        } else {
45
            $else = null;
46
        }
47
        $stream->expect(Twig_Token::BLOCK_END_TYPE);
48

    
49
        if (count($targets) > 1) {
50
            $keyTarget = $targets->getNode(0);
51
            $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine());
52
            $valueTarget = $targets->getNode(1);
53
            $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
54
        } else {
55
            $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno);
56
            $valueTarget = $targets->getNode(0);
57
            $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
58
        }
59

    
60
        if ($ifexpr) {
61
            $this->checkLoopUsageCondition($stream, $ifexpr);
62
            $this->checkLoopUsageBody($stream, $body);
63
        }
64

    
65
        return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
66
    }
67

    
68
    public function decideForFork(Twig_Token $token)
69
    {
70
        return $token->test(array('else', 'endfor'));
71
    }
72

    
73
    public function decideForEnd(Twig_Token $token)
74
    {
75
        return $token->test('endfor');
76
    }
77

    
78
    // the loop variable cannot be used in the condition
79
    protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node)
80
    {
81
        if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
82
            throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition.', $node->getLine(), $stream->getFilename());
83
        }
84

    
85
        foreach ($node as $n) {
86
            if (!$n) {
87
                continue;
88
            }
89

    
90
            $this->checkLoopUsageCondition($stream, $n);
91
        }
92
    }
93

    
94
    // check usage of non-defined loop-items
95
    // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include)
96
    protected function checkLoopUsageBody(Twig_TokenStream $stream, Twig_NodeInterface $node)
97
    {
98
        if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
99
            $attribute = $node->getNode('attribute');
100
            if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) {
101
                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());
102
            }
103
        }
104

    
105
        // should check for parent.loop.XXX usage
106
        if ($node instanceof Twig_Node_For) {
107
            return;
108
        }
109

    
110
        foreach ($node as $n) {
111
            if (!$n) {
112
                continue;
113
            }
114

    
115
            $this->checkLoopUsageBody($stream, $n);
116
        }
117
    }
118

    
119
    public function getTag()
120
    {
121
        return 'for';
122
    }
123
}
(8-8/17)