aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/Twig/NodeVisitor/Optimizer.php
diff options
context:
space:
mode:
authorNicolas LÅ“uillet <nicolas.loeuillet@gmail.com>2013-08-02 22:40:51 +0200
committerNicolas LÅ“uillet <nicolas.loeuillet@gmail.com>2013-08-02 22:40:51 +0200
commita4565e88edbc8e3bd092a475469769c86a4c350c (patch)
treea6a3c935b03a23ff87575c8c315cf8ba78fe68c2 /inc/Twig/NodeVisitor/Optimizer.php
parentf6c9baab3efeec1d0efa151e276fc08d5b58f9e9 (diff)
downloadwallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.tar.gz
wallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.tar.zst
wallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.zip
add Twig & refactor poche
Diffstat (limited to 'inc/Twig/NodeVisitor/Optimizer.php')
-rw-r--r--inc/Twig/NodeVisitor/Optimizer.php246
1 files changed, 246 insertions, 0 deletions
diff --git a/inc/Twig/NodeVisitor/Optimizer.php b/inc/Twig/NodeVisitor/Optimizer.php
new file mode 100644
index 00000000..a254def7
--- /dev/null
+++ b/inc/Twig/NodeVisitor/Optimizer.php
@@ -0,0 +1,246 @@
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 * @author Fabien Potencier <fabien@symfony.com>
21 */
22class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
23{
24 const OPTIMIZE_ALL = -1;
25 const OPTIMIZE_NONE = 0;
26 const OPTIMIZE_FOR = 2;
27 const OPTIMIZE_RAW_FILTER = 4;
28 const OPTIMIZE_VAR_ACCESS = 8;
29
30 protected $loops = array();
31 protected $optimizers;
32 protected $prependedNodes = array();
33 protected $inABody = false;
34
35 /**
36 * Constructor.
37 *
38 * @param integer $optimizers The optimizer mode
39 */
40 public function __construct($optimizers = -1)
41 {
42 if (!is_int($optimizers) || $optimizers > 2) {
43 throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
44 }
45
46 $this->optimizers = $optimizers;
47 }
48
49 /**
50 * {@inheritdoc}
51 */
52 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
53 {
54 if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
55 $this->enterOptimizeFor($node, $env);
56 }
57
58 if (!version_compare(phpversion(), '5.4.0RC1', '>=') && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
59 if ($this->inABody) {
60 if (!$node instanceof Twig_Node_Expression) {
61 if (get_class($node) !== 'Twig_Node') {
62 array_unshift($this->prependedNodes, array());
63 }
64 } else {
65 $node = $this->optimizeVariables($node, $env);
66 }
67 } elseif ($node instanceof Twig_Node_Body) {
68 $this->inABody = true;
69 }
70 }
71
72 return $node;
73 }
74
75 /**
76 * {@inheritdoc}
77 */
78 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
79 {
80 $expression = $node instanceof Twig_Node_Expression;
81
82 if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
83 $this->leaveOptimizeFor($node, $env);
84 }
85
86 if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) {
87 $node = $this->optimizeRawFilter($node, $env);
88 }
89
90 $node = $this->optimizePrintNode($node, $env);
91
92 if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
93 if ($node instanceof Twig_Node_Body) {
94 $this->inABody = false;
95 } elseif ($this->inABody) {
96 if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
97 $nodes = array();
98 foreach (array_unique($prependedNodes) as $name) {
99 $nodes[] = new Twig_Node_SetTemp($name, $node->getLine());
100 }
101
102 $nodes[] = $node;
103 $node = new Twig_Node($nodes);
104 }
105 }
106 }
107
108 return $node;
109 }
110
111 protected function optimizeVariables($node, $env)
112 {
113 if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
114 $this->prependedNodes[0][] = $node->getAttribute('name');
115
116 return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine());
117 }
118
119 return $node;
120 }
121
122 /**
123 * Optimizes print nodes.
124 *
125 * It replaces:
126 *
127 * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
128 *
129 * @param Twig_NodeInterface $node A Node
130 * @param Twig_Environment $env The current Twig environment
131 */
132 protected function optimizePrintNode($node, $env)
133 {
134 if (!$node instanceof Twig_Node_Print) {
135 return $node;
136 }
137
138 if (
139 $node->getNode('expr') instanceof Twig_Node_Expression_BlockReference ||
140 $node->getNode('expr') instanceof Twig_Node_Expression_Parent
141 ) {
142 $node->getNode('expr')->setAttribute('output', true);
143
144 return $node->getNode('expr');
145 }
146
147 return $node;
148 }
149
150 /**
151 * Removes "raw" filters.
152 *
153 * @param Twig_NodeInterface $node A Node
154 * @param Twig_Environment $env The current Twig environment
155 */
156 protected function optimizeRawFilter($node, $env)
157 {
158 if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
159 return $node->getNode('node');
160 }
161
162 return $node;
163 }
164
165 /**
166 * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
167 *
168 * @param Twig_NodeInterface $node A Node
169 * @param Twig_Environment $env The current Twig environment
170 */
171 protected function enterOptimizeFor($node, $env)
172 {
173 if ($node instanceof Twig_Node_For) {
174 // disable the loop variable by default
175 $node->setAttribute('with_loop', false);
176 array_unshift($this->loops, $node);
177 } elseif (!$this->loops) {
178 // we are outside a loop
179 return;
180 }
181
182 // when do we need to add the loop variable back?
183
184 // the loop variable is referenced for the current loop
185 elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
186 $this->addLoopToCurrent();
187 }
188
189 // block reference
190 elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) {
191 $this->addLoopToCurrent();
192 }
193
194 // include without the only attribute
195 elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) {
196 $this->addLoopToAll();
197 }
198
199 // the loop variable is referenced via an attribute
200 elseif ($node instanceof Twig_Node_Expression_GetAttr
201 && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
202 || 'parent' === $node->getNode('attribute')->getAttribute('value')
203 )
204 && (true === $this->loops[0]->getAttribute('with_loop')
205 || ($node->getNode('node') instanceof Twig_Node_Expression_Name
206 && 'loop' === $node->getNode('node')->getAttribute('name')
207 )
208 )
209 ) {
210 $this->addLoopToAll();
211 }
212 }
213
214 /**
215 * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
216 *
217 * @param Twig_NodeInterface $node A Node
218 * @param Twig_Environment $env The current Twig environment
219 */
220 protected function leaveOptimizeFor($node, $env)
221 {
222 if ($node instanceof Twig_Node_For) {
223 array_shift($this->loops);
224 }
225 }
226
227 protected function addLoopToCurrent()
228 {
229 $this->loops[0]->setAttribute('with_loop', true);
230 }
231
232 protected function addLoopToAll()
233 {
234 foreach ($this->loops as $loop) {
235 $loop->setAttribute('with_loop', true);
236 }
237 }
238
239 /**
240 * {@inheritdoc}
241 */
242 public function getPriority()
243 {
244 return 255;
245 }
246}