diff options
Diffstat (limited to 'inc/Twig/NodeVisitor/SafeAnalysis.php')
-rw-r--r-- | inc/Twig/NodeVisitor/SafeAnalysis.php | 131 |
1 files changed, 131 insertions, 0 deletions
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 | } | ||