|
1 <?php |
|
2 |
|
3 class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface |
|
4 { |
|
5 protected $data = array(); |
|
6 |
|
7 public function getSafe(Twig_NodeInterface $node) |
|
8 { |
|
9 $hash = spl_object_hash($node); |
|
10 if (isset($this->data[$hash])) { |
|
11 foreach($this->data[$hash] as $bucket) { |
|
12 if ($bucket['key'] === $node) { |
|
13 return $bucket['value']; |
|
14 } |
|
15 } |
|
16 } |
|
17 return null; |
|
18 } |
|
19 |
|
20 protected function setSafe(Twig_NodeInterface $node, array $safe) |
|
21 { |
|
22 $hash = spl_object_hash($node); |
|
23 if (isset($this->data[$hash])) { |
|
24 foreach($this->data[$hash] as &$bucket) { |
|
25 if ($bucket['key'] === $node) { |
|
26 $bucket['value'] = $safe; |
|
27 return; |
|
28 } |
|
29 } |
|
30 } |
|
31 $this->data[$hash][] = array( |
|
32 'key' => $node, |
|
33 'value' => $safe, |
|
34 ); |
|
35 } |
|
36 |
|
37 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) |
|
38 { |
|
39 return $node; |
|
40 } |
|
41 |
|
42 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) |
|
43 { |
|
44 if ($node instanceof Twig_Node_Expression_Constant) { |
|
45 // constants are marked safe for all |
|
46 $this->setSafe($node, array('all')); |
|
47 } elseif ($node instanceof Twig_Node_Expression_BlockReference) { |
|
48 // blocks are safe by definition |
|
49 $this->setSafe($node, array('all')); |
|
50 } elseif ($node instanceof Twig_Node_Expression_Parent) { |
|
51 // parent block is safe by definition |
|
52 $this->setSafe($node, array('all')); |
|
53 } elseif ($node instanceof Twig_Node_Expression_Conditional) { |
|
54 // intersect safeness of both operands |
|
55 $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3'))); |
|
56 $this->setSafe($node, $safe); |
|
57 } elseif ($node instanceof Twig_Node_Expression_Filter) { |
|
58 // filter expression is safe when the filter is safe |
|
59 $name = $node->getNode('filter')->getAttribute('value'); |
|
60 $args = $node->getNode('arguments'); |
|
61 if (false !== $filter = $env->getFilter($name)) { |
|
62 $this->setSafe($node, $filter->getSafe($args)); |
|
63 } else { |
|
64 $this->setSafe($node, array()); |
|
65 } |
|
66 } elseif ($node instanceof Twig_Node_Expression_Function) { |
|
67 // function expression is safe when the function is safe |
|
68 $name = $node->getAttribute('name'); |
|
69 $args = $node->getNode('arguments'); |
|
70 $function = $env->getFunction($name); |
|
71 if (false !== $function) { |
|
72 $this->setSafe($node, $function->getSafe($args)); |
|
73 } else { |
|
74 $this->setSafe($node, array()); |
|
75 } |
|
76 } else { |
|
77 $this->setSafe($node, array()); |
|
78 } |
|
79 |
|
80 return $node; |
|
81 } |
|
82 |
|
83 protected function intersectSafe(array $a = null, array $b = null) |
|
84 { |
|
85 if (null === $a || null === $b) { |
|
86 return array(); |
|
87 } |
|
88 |
|
89 if (in_array('all', $a)) { |
|
90 return $b; |
|
91 } |
|
92 |
|
93 if (in_array('all', $b)) { |
|
94 return $a; |
|
95 } |
|
96 |
|
97 return array_intersect($a, $b); |
|
98 } |
|
99 |
|
100 /** |
|
101 * {@inheritdoc} |
|
102 */ |
|
103 public function getPriority() |
|
104 { |
|
105 return 0; |
|
106 } |
|
107 } |