Security update to Drupal 8.4.6
[yaffs-website] / vendor / twig / twig / lib / Twig / NodeVisitor / Optimizer.php
1 <?php
2
3 /*
4  * This file is part of Twig.
5  *
6  * (c) 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  * @final
21  *
22  * @author Fabien Potencier <fabien@symfony.com>
23  */
24 class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
25 {
26     const OPTIMIZE_ALL = -1;
27     const OPTIMIZE_NONE = 0;
28     const OPTIMIZE_FOR = 2;
29     const OPTIMIZE_RAW_FILTER = 4;
30     const OPTIMIZE_VAR_ACCESS = 8;
31
32     protected $loops = array();
33     protected $loopsTargets = array();
34     protected $optimizers;
35     protected $prependedNodes = array();
36     protected $inABody = false;
37
38     /**
39      * @param int $optimizers The optimizer mode
40      */
41     public function __construct($optimizers = -1)
42     {
43         if (!is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) {
44             throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
45         }
46
47         $this->optimizers = $optimizers;
48     }
49
50     protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
51     {
52         if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
53             $this->enterOptimizeFor($node, $env);
54         }
55
56         if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
57             if ($this->inABody) {
58                 if (!$node instanceof Twig_Node_Expression) {
59                     if ('Twig_Node' !== get_class($node)) {
60                         array_unshift($this->prependedNodes, array());
61                     }
62                 } else {
63                     $node = $this->optimizeVariables($node, $env);
64                 }
65             } elseif ($node instanceof Twig_Node_Body) {
66                 $this->inABody = true;
67             }
68         }
69
70         return $node;
71     }
72
73     protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
74     {
75         $expression = $node instanceof Twig_Node_Expression;
76
77         if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
78             $this->leaveOptimizeFor($node, $env);
79         }
80
81         if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) {
82             $node = $this->optimizeRawFilter($node, $env);
83         }
84
85         $node = $this->optimizePrintNode($node, $env);
86
87         if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
88             if ($node instanceof Twig_Node_Body) {
89                 $this->inABody = false;
90             } elseif ($this->inABody) {
91                 if (!$expression && 'Twig_Node' !== get_class($node) && $prependedNodes = array_shift($this->prependedNodes)) {
92                     $nodes = array();
93                     foreach (array_unique($prependedNodes) as $name) {
94                         $nodes[] = new Twig_Node_SetTemp($name, $node->getTemplateLine());
95                     }
96
97                     $nodes[] = $node;
98                     $node = new Twig_Node($nodes);
99                 }
100             }
101         }
102
103         return $node;
104     }
105
106     protected function optimizeVariables(Twig_NodeInterface $node, Twig_Environment $env)
107     {
108         if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
109             $this->prependedNodes[0][] = $node->getAttribute('name');
110
111             return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getTemplateLine());
112         }
113
114         return $node;
115     }
116
117     /**
118      * Optimizes print nodes.
119      *
120      * It replaces:
121      *
122      *   * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
123      *
124      * @return Twig_NodeInterface
125      */
126     protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env)
127     {
128         if (!$node instanceof Twig_Node_Print) {
129             return $node;
130         }
131
132         $exprNode = $node->getNode('expr');
133         if (
134             $exprNode instanceof Twig_Node_Expression_BlockReference ||
135             $exprNode instanceof Twig_Node_Expression_Parent
136         ) {
137             $exprNode->setAttribute('output', true);
138
139             return $exprNode;
140         }
141
142         return $node;
143     }
144
145     /**
146      * Removes "raw" filters.
147      *
148      * @return Twig_NodeInterface
149      */
150     protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env)
151     {
152         if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
153             return $node->getNode('node');
154         }
155
156         return $node;
157     }
158
159     /**
160      * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
161      */
162     protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
163     {
164         if ($node instanceof Twig_Node_For) {
165             // disable the loop variable by default
166             $node->setAttribute('with_loop', false);
167             array_unshift($this->loops, $node);
168             array_unshift($this->loopsTargets, $node->getNode('value_target')->getAttribute('name'));
169             array_unshift($this->loopsTargets, $node->getNode('key_target')->getAttribute('name'));
170         } elseif (!$this->loops) {
171             // we are outside a loop
172             return;
173         }
174
175         // when do we need to add the loop variable back?
176
177         // the loop variable is referenced for the current loop
178         elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
179             $node->setAttribute('always_defined', true);
180             $this->addLoopToCurrent();
181         }
182
183         // optimize access to loop targets
184         elseif ($node instanceof Twig_Node_Expression_Name && in_array($node->getAttribute('name'), $this->loopsTargets)) {
185             $node->setAttribute('always_defined', true);
186         }
187
188         // block reference
189         elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) {
190             $this->addLoopToCurrent();
191         }
192
193         // include without the only attribute
194         elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) {
195             $this->addLoopToAll();
196         }
197
198         // include function without the with_context=false parameter
199         elseif ($node instanceof Twig_Node_Expression_Function
200             && 'include' === $node->getAttribute('name')
201             && (!$node->getNode('arguments')->hasNode('with_context')
202                  || false !== $node->getNode('arguments')->getNode('with_context')->getAttribute('value')
203                )
204         ) {
205             $this->addLoopToAll();
206         }
207
208         // the loop variable is referenced via an attribute
209         elseif ($node instanceof Twig_Node_Expression_GetAttr
210             && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
211                 || 'parent' === $node->getNode('attribute')->getAttribute('value')
212                )
213             && (true === $this->loops[0]->getAttribute('with_loop')
214                 || ($node->getNode('node') instanceof Twig_Node_Expression_Name
215                     && 'loop' === $node->getNode('node')->getAttribute('name')
216                    )
217                )
218         ) {
219             $this->addLoopToAll();
220         }
221     }
222
223     /**
224      * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
225      */
226     protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
227     {
228         if ($node instanceof Twig_Node_For) {
229             array_shift($this->loops);
230             array_shift($this->loopsTargets);
231             array_shift($this->loopsTargets);
232         }
233     }
234
235     protected function addLoopToCurrent()
236     {
237         $this->loops[0]->setAttribute('with_loop', true);
238     }
239
240     protected function addLoopToAll()
241     {
242         foreach ($this->loops as $loop) {
243             $loop->setAttribute('with_loop', true);
244         }
245     }
246
247     public function getPriority()
248     {
249         return 255;
250     }
251 }
252
253 class_alias('Twig_NodeVisitor_Optimizer', 'Twig\NodeVisitor\OptimizerNodeVisitor', false);