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