]>
Commit | Line | Data |
---|---|---|
a4565e88 NL |
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 | } |