Dotclear

source: inc/libs/Twig/NodeVisitor/Optimizer.php @ 1149:1657e862089c

Revision 1149:1657e862089c, 7.6 KB checked in by dsls, 13 years ago (diff)

Fixed unix case-sensitive twig directory

Line 
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Twig_NodeVisitor_Optimizer tries to optimizes the AST.
14 *
15 * This visitor is always the last registered one.
16 *
17 * You can configure which optimizations you want to activate via the
18 * optimizer mode.
19 *
20 * @package twig
21 * @author  Fabien Potencier <fabien@symfony.com>
22 */
23class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
24{
25    const OPTIMIZE_ALL         = -1;
26    const OPTIMIZE_NONE        = 0;
27    const OPTIMIZE_FOR         = 2;
28    const OPTIMIZE_RAW_FILTER  = 4;
29    const OPTIMIZE_VAR_ACCESS  = 8;
30
31    protected $loops = array();
32    protected $optimizers;
33    protected $prependedNodes = array();
34    protected $inABody = false;
35
36    /**
37     * Constructor.
38     *
39     * @param integer $optimizers The optimizer mode
40     */
41    public function __construct($optimizers = -1)
42    {
43        if (!is_int($optimizers) || $optimizers > 2) {
44            throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
45        }
46
47        $this->optimizers = $optimizers;
48    }
49
50    /**
51     * {@inheritdoc}
52     */
53    public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
54    {
55        if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
56            $this->enterOptimizeFor($node, $env);
57        }
58
59        if (!version_compare(phpversion(), '5.4.0RC1', '>=') && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
60            if ($this->inABody) {
61                if (!$node instanceof Twig_Node_Expression) {
62                    if (get_class($node) !== 'Twig_Node') {
63                        array_unshift($this->prependedNodes, array());
64                    }
65                } else {
66                    $node = $this->optimizeVariables($node, $env);
67                }
68            } elseif ($node instanceof Twig_Node_Body) {
69                $this->inABody = true;
70            }
71        }
72
73        return $node;
74    }
75
76    /**
77     * {@inheritdoc}
78     */
79    public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
80    {
81        $expression = $node instanceof Twig_Node_Expression;
82
83        if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
84            $this->leaveOptimizeFor($node, $env);
85        }
86
87        if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) {
88            $node = $this->optimizeRawFilter($node, $env);
89        }
90
91        $node = $this->optimizePrintNode($node, $env);
92
93        if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
94            if ($node instanceof Twig_Node_Body) {
95                $this->inABody = false;
96            } elseif ($this->inABody) {
97                if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
98                    $nodes = array();
99                    foreach (array_unique($prependedNodes) as $name) {
100                        $nodes[] = new Twig_Node_SetTemp($name, $node->getLine());
101                    }
102
103                    $nodes[] = $node;
104                    $node = new Twig_Node($nodes);
105                }
106            }
107        }
108
109        return $node;
110    }
111
112    protected function optimizeVariables($node, $env)
113    {
114        if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
115            $this->prependedNodes[0][] = $node->getAttribute('name');
116
117            return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine());
118        }
119
120        return $node;
121    }
122
123    /**
124     * Optimizes print nodes.
125     *
126     * It replaces:
127     *
128     *   * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
129     *
130     * @param Twig_NodeInterface $node A Node
131     * @param Twig_Environment   $env  The current Twig environment
132     */
133    protected function optimizePrintNode($node, $env)
134    {
135        if (!$node instanceof Twig_Node_Print) {
136            return $node;
137        }
138
139        if (
140            $node->getNode('expr') instanceof Twig_Node_Expression_BlockReference ||
141            $node->getNode('expr') instanceof Twig_Node_Expression_Parent
142        ) {
143            $node->getNode('expr')->setAttribute('output', true);
144
145            return $node->getNode('expr');
146        }
147
148        return $node;
149    }
150
151    /**
152     * Removes "raw" filters.
153     *
154     * @param Twig_NodeInterface $node A Node
155     * @param Twig_Environment   $env  The current Twig environment
156     */
157    protected function optimizeRawFilter($node, $env)
158    {
159        if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
160            return $node->getNode('node');
161        }
162
163        return $node;
164    }
165
166    /**
167     * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
168     *
169     * @param Twig_NodeInterface $node A Node
170     * @param Twig_Environment   $env  The current Twig environment
171     */
172    protected function enterOptimizeFor($node, $env)
173    {
174        if ($node instanceof Twig_Node_For) {
175            // disable the loop variable by default
176            $node->setAttribute('with_loop', false);
177            array_unshift($this->loops, $node);
178        } elseif (!$this->loops) {
179            // we are outside a loop
180            return;
181        }
182
183        // when do we need to add the loop variable back?
184
185        // the loop variable is referenced for the current loop
186        elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
187            $this->addLoopToCurrent();
188        }
189
190        // block reference
191        elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) {
192            $this->addLoopToCurrent();
193        }
194
195        // include without the only attribute
196        elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) {
197            $this->addLoopToAll();
198        }
199
200        // the loop variable is referenced via an attribute
201        elseif ($node instanceof Twig_Node_Expression_GetAttr
202            && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
203                || 'parent' === $node->getNode('attribute')->getAttribute('value')
204               )
205            && (true === $this->loops[0]->getAttribute('with_loop')
206                || ($node->getNode('node') instanceof Twig_Node_Expression_Name
207                    && 'loop' === $node->getNode('node')->getAttribute('name')
208                   )
209               )
210        ) {
211            $this->addLoopToAll();
212        }
213    }
214
215    /**
216     * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
217     *
218     * @param Twig_NodeInterface $node A Node
219     * @param Twig_Environment   $env  The current Twig environment
220     */
221    protected function leaveOptimizeFor($node, $env)
222    {
223        if ($node instanceof Twig_Node_For) {
224            array_shift($this->loops);
225        }
226    }
227
228    protected function addLoopToCurrent()
229    {
230        $this->loops[0]->setAttribute('with_loop', true);
231    }
232
233    protected function addLoopToAll()
234    {
235        foreach ($this->loops as $loop) {
236            $loop->setAttribute('with_loop', true);
237        }
238    }
239
240    /**
241     * {@inheritdoc}
242     */
243    public function getPriority()
244    {
245        return 255;
246    }
247}
Note: See TracBrowser for help on using the repository browser.

Sites map