diff options
author | Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com> | 2013-08-02 22:40:51 +0200 |
---|---|---|
committer | Nicolas LÅ“uillet <nicolas.loeuillet@gmail.com> | 2013-08-02 22:40:51 +0200 |
commit | a4565e88edbc8e3bd092a475469769c86a4c350c (patch) | |
tree | a6a3c935b03a23ff87575c8c315cf8ba78fe68c2 /inc/Twig/NodeVisitor | |
parent | f6c9baab3efeec1d0efa151e276fc08d5b58f9e9 (diff) | |
download | wallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.tar.gz wallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.tar.zst wallabag-a4565e88edbc8e3bd092a475469769c86a4c350c.zip |
add Twig & refactor poche
Diffstat (limited to 'inc/Twig/NodeVisitor')
-rw-r--r-- | inc/Twig/NodeVisitor/Escaper.php | 167 | ||||
-rw-r--r-- | inc/Twig/NodeVisitor/Optimizer.php | 246 | ||||
-rw-r--r-- | inc/Twig/NodeVisitor/SafeAnalysis.php | 131 | ||||
-rw-r--r-- | inc/Twig/NodeVisitor/Sandbox.php | 92 |
4 files changed, 636 insertions, 0 deletions
diff --git a/inc/Twig/NodeVisitor/Escaper.php b/inc/Twig/NodeVisitor/Escaper.php new file mode 100644 index 00000000..cc4b3d71 --- /dev/null +++ b/inc/Twig/NodeVisitor/Escaper.php | |||
@@ -0,0 +1,167 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2009 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_Escaper implements output escaping. | ||
14 | * | ||
15 | * @author Fabien Potencier <fabien@symfony.com> | ||
16 | */ | ||
17 | class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface | ||
18 | { | ||
19 | protected $statusStack = array(); | ||
20 | protected $blocks = array(); | ||
21 | protected $safeAnalysis; | ||
22 | protected $traverser; | ||
23 | protected $defaultStrategy = false; | ||
24 | protected $safeVars = array(); | ||
25 | |||
26 | public function __construct() | ||
27 | { | ||
28 | $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis(); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * Called before child nodes are visited. | ||
33 | * | ||
34 | * @param Twig_NodeInterface $node The node to visit | ||
35 | * @param Twig_Environment $env The Twig environment instance | ||
36 | * | ||
37 | * @return Twig_NodeInterface The modified node | ||
38 | */ | ||
39 | public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) | ||
40 | { | ||
41 | if ($node instanceof Twig_Node_Module) { | ||
42 | if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) { | ||
43 | $this->defaultStrategy = $defaultStrategy; | ||
44 | } | ||
45 | $this->safeVars = array(); | ||
46 | } elseif ($node instanceof Twig_Node_AutoEscape) { | ||
47 | $this->statusStack[] = $node->getAttribute('value'); | ||
48 | } elseif ($node instanceof Twig_Node_Block) { | ||
49 | $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env); | ||
50 | } elseif ($node instanceof Twig_Node_Import) { | ||
51 | $this->safeVars[] = $node->getNode('var')->getAttribute('name'); | ||
52 | } | ||
53 | |||
54 | return $node; | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Called after child nodes are visited. | ||
59 | * | ||
60 | * @param Twig_NodeInterface $node The node to visit | ||
61 | * @param Twig_Environment $env The Twig environment instance | ||
62 | * | ||
63 | * @return Twig_NodeInterface The modified node | ||
64 | */ | ||
65 | public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) | ||
66 | { | ||
67 | if ($node instanceof Twig_Node_Module) { | ||
68 | $this->defaultStrategy = false; | ||
69 | $this->safeVars = array(); | ||
70 | } elseif ($node instanceof Twig_Node_Expression_Filter) { | ||
71 | return $this->preEscapeFilterNode($node, $env); | ||
72 | } elseif ($node instanceof Twig_Node_Print) { | ||
73 | return $this->escapePrintNode($node, $env, $this->needEscaping($env)); | ||
74 | } | ||
75 | |||
76 | if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) { | ||
77 | array_pop($this->statusStack); | ||
78 | } elseif ($node instanceof Twig_Node_BlockReference) { | ||
79 | $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env); | ||
80 | } | ||
81 | |||
82 | return $node; | ||
83 | } | ||
84 | |||
85 | protected function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type) | ||
86 | { | ||
87 | if (false === $type) { | ||
88 | return $node; | ||
89 | } | ||
90 | |||
91 | $expression = $node->getNode('expr'); | ||
92 | |||
93 | if ($this->isSafeFor($type, $expression, $env)) { | ||
94 | return $node; | ||
95 | } | ||
96 | |||
97 | $class = get_class($node); | ||
98 | |||
99 | return new $class( | ||
100 | $this->getEscaperFilter($type, $expression), | ||
101 | $node->getLine() | ||
102 | ); | ||
103 | } | ||
104 | |||
105 | protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env) | ||
106 | { | ||
107 | $name = $filter->getNode('filter')->getAttribute('value'); | ||
108 | |||
109 | $type = $env->getFilter($name)->getPreEscape(); | ||
110 | if (null === $type) { | ||
111 | return $filter; | ||
112 | } | ||
113 | |||
114 | $node = $filter->getNode('node'); | ||
115 | if ($this->isSafeFor($type, $node, $env)) { | ||
116 | return $filter; | ||
117 | } | ||
118 | |||
119 | $filter->setNode('node', $this->getEscaperFilter($type, $node)); | ||
120 | |||
121 | return $filter; | ||
122 | } | ||
123 | |||
124 | protected function isSafeFor($type, Twig_NodeInterface $expression, $env) | ||
125 | { | ||
126 | $safe = $this->safeAnalysis->getSafe($expression); | ||
127 | |||
128 | if (null === $safe) { | ||
129 | if (null === $this->traverser) { | ||
130 | $this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis)); | ||
131 | } | ||
132 | |||
133 | $this->safeAnalysis->setSafeVars($this->safeVars); | ||
134 | |||
135 | $this->traverser->traverse($expression); | ||
136 | $safe = $this->safeAnalysis->getSafe($expression); | ||
137 | } | ||
138 | |||
139 | return in_array($type, $safe) || in_array('all', $safe); | ||
140 | } | ||
141 | |||
142 | protected function needEscaping(Twig_Environment $env) | ||
143 | { | ||
144 | if (count($this->statusStack)) { | ||
145 | return $this->statusStack[count($this->statusStack) - 1]; | ||
146 | } | ||
147 | |||
148 | return $this->defaultStrategy ? $this->defaultStrategy : false; | ||
149 | } | ||
150 | |||
151 | protected function getEscaperFilter($type, Twig_NodeInterface $node) | ||
152 | { | ||
153 | $line = $node->getLine(); | ||
154 | $name = new Twig_Node_Expression_Constant('escape', $line); | ||
155 | $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line))); | ||
156 | |||
157 | return new Twig_Node_Expression_Filter($node, $name, $args, $line); | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * {@inheritdoc} | ||
162 | */ | ||
163 | public function getPriority() | ||
164 | { | ||
165 | return 0; | ||
166 | } | ||
167 | } | ||
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 | */ | ||
22 | class 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 | } | ||
diff --git a/inc/Twig/NodeVisitor/SafeAnalysis.php b/inc/Twig/NodeVisitor/SafeAnalysis.php new file mode 100644 index 00000000..c4bbd812 --- /dev/null +++ b/inc/Twig/NodeVisitor/SafeAnalysis.php | |||
@@ -0,0 +1,131 @@ | |||
1 | <?php | ||
2 | |||
3 | class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface | ||
4 | { | ||
5 | protected $data = array(); | ||
6 | protected $safeVars = array(); | ||
7 | |||
8 | public function setSafeVars($safeVars) | ||
9 | { | ||
10 | $this->safeVars = $safeVars; | ||
11 | } | ||
12 | |||
13 | public function getSafe(Twig_NodeInterface $node) | ||
14 | { | ||
15 | $hash = spl_object_hash($node); | ||
16 | if (isset($this->data[$hash])) { | ||
17 | foreach ($this->data[$hash] as $bucket) { | ||
18 | if ($bucket['key'] === $node) { | ||
19 | return $bucket['value']; | ||
20 | } | ||
21 | } | ||
22 | } | ||
23 | } | ||
24 | |||
25 | protected function setSafe(Twig_NodeInterface $node, array $safe) | ||
26 | { | ||
27 | $hash = spl_object_hash($node); | ||
28 | if (isset($this->data[$hash])) { | ||
29 | foreach ($this->data[$hash] as &$bucket) { | ||
30 | if ($bucket['key'] === $node) { | ||
31 | $bucket['value'] = $safe; | ||
32 | |||
33 | return; | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | $this->data[$hash][] = array( | ||
38 | 'key' => $node, | ||
39 | 'value' => $safe, | ||
40 | ); | ||
41 | } | ||
42 | |||
43 | public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) | ||
44 | { | ||
45 | return $node; | ||
46 | } | ||
47 | |||
48 | public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) | ||
49 | { | ||
50 | if ($node instanceof Twig_Node_Expression_Constant) { | ||
51 | // constants are marked safe for all | ||
52 | $this->setSafe($node, array('all')); | ||
53 | } elseif ($node instanceof Twig_Node_Expression_BlockReference) { | ||
54 | // blocks are safe by definition | ||
55 | $this->setSafe($node, array('all')); | ||
56 | } elseif ($node instanceof Twig_Node_Expression_Parent) { | ||
57 | // parent block is safe by definition | ||
58 | $this->setSafe($node, array('all')); | ||
59 | } elseif ($node instanceof Twig_Node_Expression_Conditional) { | ||
60 | // intersect safeness of both operands | ||
61 | $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3'))); | ||
62 | $this->setSafe($node, $safe); | ||
63 | } elseif ($node instanceof Twig_Node_Expression_Filter) { | ||
64 | // filter expression is safe when the filter is safe | ||
65 | $name = $node->getNode('filter')->getAttribute('value'); | ||
66 | $args = $node->getNode('arguments'); | ||
67 | if (false !== $filter = $env->getFilter($name)) { | ||
68 | $safe = $filter->getSafe($args); | ||
69 | if (null === $safe) { | ||
70 | $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety()); | ||
71 | } | ||
72 | $this->setSafe($node, $safe); | ||
73 | } else { | ||
74 | $this->setSafe($node, array()); | ||
75 | } | ||
76 | } elseif ($node instanceof Twig_Node_Expression_Function) { | ||
77 | // function expression is safe when the function is safe | ||
78 | $name = $node->getAttribute('name'); | ||
79 | $args = $node->getNode('arguments'); | ||
80 | $function = $env->getFunction($name); | ||
81 | if (false !== $function) { | ||
82 | $this->setSafe($node, $function->getSafe($args)); | ||
83 | } else { | ||
84 | $this->setSafe($node, array()); | ||
85 | } | ||
86 | } elseif ($node instanceof Twig_Node_Expression_MethodCall) { | ||
87 | if ($node->getAttribute('safe')) { | ||
88 | $this->setSafe($node, array('all')); | ||
89 | } else { | ||
90 | $this->setSafe($node, array()); | ||
91 | } | ||
92 | } elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) { | ||
93 | $name = $node->getNode('node')->getAttribute('name'); | ||
94 | // attributes on template instances are safe | ||
95 | if ('_self' == $name || in_array($name, $this->safeVars)) { | ||
96 | $this->setSafe($node, array('all')); | ||
97 | } else { | ||
98 | $this->setSafe($node, array()); | ||
99 | } | ||
100 | } else { | ||
101 | $this->setSafe($node, array()); | ||
102 | } | ||
103 | |||
104 | return $node; | ||
105 | } | ||
106 | |||
107 | protected function intersectSafe(array $a = null, array $b = null) | ||
108 | { | ||
109 | if (null === $a || null === $b) { | ||
110 | return array(); | ||
111 | } | ||
112 | |||
113 | if (in_array('all', $a)) { | ||
114 | return $b; | ||
115 | } | ||
116 | |||
117 | if (in_array('all', $b)) { | ||
118 | return $a; | ||
119 | } | ||
120 | |||
121 | return array_intersect($a, $b); | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * {@inheritdoc} | ||
126 | */ | ||
127 | public function getPriority() | ||
128 | { | ||
129 | return 0; | ||
130 | } | ||
131 | } | ||
diff --git a/inc/Twig/NodeVisitor/Sandbox.php b/inc/Twig/NodeVisitor/Sandbox.php new file mode 100644 index 00000000..fb27045b --- /dev/null +++ b/inc/Twig/NodeVisitor/Sandbox.php | |||
@@ -0,0 +1,92 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of Twig. | ||
5 | * | ||
6 | * (c) 2009 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_Sandbox implements sandboxing. | ||
14 | * | ||
15 | * @author Fabien Potencier <fabien@symfony.com> | ||
16 | */ | ||
17 | class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface | ||
18 | { | ||
19 | protected $inAModule = false; | ||
20 | protected $tags; | ||
21 | protected $filters; | ||
22 | protected $functions; | ||
23 | |||
24 | /** | ||
25 | * Called before child nodes are visited. | ||
26 | * | ||
27 | * @param Twig_NodeInterface $node The node to visit | ||
28 | * @param Twig_Environment $env The Twig environment instance | ||
29 | * | ||
30 | * @return Twig_NodeInterface The modified node | ||
31 | */ | ||
32 | public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) | ||
33 | { | ||
34 | if ($node instanceof Twig_Node_Module) { | ||
35 | $this->inAModule = true; | ||
36 | $this->tags = array(); | ||
37 | $this->filters = array(); | ||
38 | $this->functions = array(); | ||
39 | |||
40 | return $node; | ||
41 | } elseif ($this->inAModule) { | ||
42 | // look for tags | ||
43 | if ($node->getNodeTag()) { | ||
44 | $this->tags[] = $node->getNodeTag(); | ||
45 | } | ||
46 | |||
47 | // look for filters | ||
48 | if ($node instanceof Twig_Node_Expression_Filter) { | ||
49 | $this->filters[] = $node->getNode('filter')->getAttribute('value'); | ||
50 | } | ||
51 | |||
52 | // look for functions | ||
53 | if ($node instanceof Twig_Node_Expression_Function) { | ||
54 | $this->functions[] = $node->getAttribute('name'); | ||
55 | } | ||
56 | |||
57 | // wrap print to check __toString() calls | ||
58 | if ($node instanceof Twig_Node_Print) { | ||
59 | return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getLine(), $node->getNodeTag()); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | return $node; | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * Called after child nodes are visited. | ||
68 | * | ||
69 | * @param Twig_NodeInterface $node The node to visit | ||
70 | * @param Twig_Environment $env The Twig environment instance | ||
71 | * | ||
72 | * @return Twig_NodeInterface The modified node | ||
73 | */ | ||
74 | public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) | ||
75 | { | ||
76 | if ($node instanceof Twig_Node_Module) { | ||
77 | $this->inAModule = false; | ||
78 | |||
79 | return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags), array_unique($this->functions)); | ||
80 | } | ||
81 | |||
82 | return $node; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * {@inheritdoc} | ||
87 | */ | ||
88 | public function getPriority() | ||
89 | { | ||
90 | return 0; | ||
91 | } | ||
92 | } | ||