|
1 <?php |
|
2 |
|
3 /* |
|
4 * This file is part of Twig. |
|
5 * |
|
6 * (c) 2009 Fabien Potencier |
|
7 * (c) 2009 Armin Ronacher |
|
8 * |
|
9 * For the full copyright and license information, please view the LICENSE |
|
10 * file that was distributed with this source code. |
|
11 */ |
|
12 |
|
13 /** |
|
14 * Represents a module node. |
|
15 * |
|
16 * @package twig |
|
17 * @author Fabien Potencier <fabien@symfony.com> |
|
18 */ |
|
19 class Twig_Node_Module extends Twig_Node |
|
20 { |
|
21 public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $filename) |
|
22 { |
|
23 parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename), 1); |
|
24 } |
|
25 |
|
26 /** |
|
27 * Compiles the node to PHP. |
|
28 * |
|
29 * @param Twig_Compiler A Twig_Compiler instance |
|
30 */ |
|
31 public function compile(Twig_Compiler $compiler) |
|
32 { |
|
33 $this->compileTemplate($compiler); |
|
34 } |
|
35 |
|
36 protected function compileTemplate(Twig_Compiler $compiler) |
|
37 { |
|
38 $this->compileClassHeader($compiler); |
|
39 |
|
40 if (count($this->getNode('blocks')) || count($this->getNode('traits'))) { |
|
41 $this->compileConstructor($compiler); |
|
42 } |
|
43 |
|
44 $this->compileGetParent($compiler); |
|
45 |
|
46 $this->compileDisplayHeader($compiler); |
|
47 |
|
48 $this->compileDisplayBody($compiler); |
|
49 |
|
50 $this->compileDisplayFooter($compiler); |
|
51 |
|
52 $compiler->subcompile($this->getNode('blocks')); |
|
53 |
|
54 $this->compileMacros($compiler); |
|
55 |
|
56 $this->compileGetTemplateName($compiler); |
|
57 |
|
58 $this->compileIsTraitable($compiler); |
|
59 |
|
60 $this->compileClassFooter($compiler); |
|
61 } |
|
62 |
|
63 protected function compileGetParent(Twig_Compiler $compiler) |
|
64 { |
|
65 $compiler |
|
66 ->write("protected function doGetParent(array \$context)\n", "{\n") |
|
67 ->indent() |
|
68 ->write("return ") |
|
69 ; |
|
70 |
|
71 if (null === $this->getNode('parent')) { |
|
72 $compiler->raw("false"); |
|
73 } else { |
|
74 if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { |
|
75 $compiler->subcompile($this->getNode('parent')); |
|
76 } else { |
|
77 $compiler |
|
78 ->raw("\$this->env->resolveTemplate(") |
|
79 ->subcompile($this->getNode('parent')) |
|
80 ->raw(")") |
|
81 ; |
|
82 } |
|
83 } |
|
84 |
|
85 $compiler |
|
86 ->raw(";\n") |
|
87 ->outdent() |
|
88 ->write("}\n\n") |
|
89 ; |
|
90 } |
|
91 |
|
92 protected function compileDisplayBody(Twig_Compiler $compiler) |
|
93 { |
|
94 $compiler->write("\$context = array_merge(\$this->env->getGlobals(), \$context);\n\n"); |
|
95 $compiler->subcompile($this->getNode('body')); |
|
96 |
|
97 if (null !== $this->getNode('parent')) { |
|
98 $compiler->write("\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));\n"); |
|
99 } |
|
100 } |
|
101 |
|
102 protected function compileClassHeader(Twig_Compiler $compiler) |
|
103 { |
|
104 $compiler |
|
105 ->write("<?php\n\n") |
|
106 // if the filename contains */, add a blank to avoid a PHP parse error |
|
107 ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n") |
|
108 ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'))) |
|
109 ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass())) |
|
110 ->write("{\n") |
|
111 ->indent() |
|
112 ; |
|
113 } |
|
114 |
|
115 protected function compileConstructor(Twig_Compiler $compiler) |
|
116 { |
|
117 $compiler |
|
118 ->write("public function __construct(Twig_Environment \$env)\n", "{\n") |
|
119 ->indent() |
|
120 ->write("parent::__construct(\$env);\n\n") |
|
121 ; |
|
122 |
|
123 $countTraits = count($this->getNode('traits')); |
|
124 if ($countTraits) { |
|
125 // traits |
|
126 foreach ($this->getNode('traits') as $i => $trait) { |
|
127 $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i)); |
|
128 |
|
129 $compiler |
|
130 ->addDebugInfo($trait->getNode('template')) |
|
131 ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i)) |
|
132 ->indent() |
|
133 ->write("throw new Twig_Error_Runtime('Template \"'.") |
|
134 ->subcompile($trait->getNode('template')) |
|
135 ->raw(".'\" cannot be used as a trait.');\n") |
|
136 ->outdent() |
|
137 ->write("}\n") |
|
138 ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i)) |
|
139 ; |
|
140 |
|
141 foreach ($trait->getNode('targets') as $key => $value) { |
|
142 $compiler |
|
143 ->write(sprintf("\$_trait_%s_blocks[", $i)) |
|
144 ->subcompile($value) |
|
145 ->raw(sprintf("] = \$_trait_%s_blocks[", $i)) |
|
146 ->string($key) |
|
147 ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i)) |
|
148 ->string($key) |
|
149 ->raw("]);\n\n") |
|
150 ; |
|
151 } |
|
152 } |
|
153 |
|
154 $compiler |
|
155 ->write("\$this->traits = array_merge(\n") |
|
156 ->indent() |
|
157 ; |
|
158 |
|
159 for ($i = 0; $i < $countTraits; $i++) { |
|
160 $compiler |
|
161 ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i)) |
|
162 ; |
|
163 } |
|
164 |
|
165 $compiler |
|
166 ->outdent() |
|
167 ->write(");\n\n") |
|
168 ; |
|
169 |
|
170 $compiler |
|
171 ->write("\$this->blocks = array_merge(\n") |
|
172 ->indent() |
|
173 ->write("\$this->traits,\n") |
|
174 ->write("array(\n") |
|
175 ; |
|
176 } else { |
|
177 $compiler |
|
178 ->write("\$this->blocks = array(\n") |
|
179 ; |
|
180 } |
|
181 |
|
182 // blocks |
|
183 $compiler |
|
184 ->indent() |
|
185 ; |
|
186 |
|
187 foreach ($this->getNode('blocks') as $name => $node) { |
|
188 $compiler |
|
189 ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name)) |
|
190 ; |
|
191 } |
|
192 |
|
193 if ($countTraits) { |
|
194 $compiler |
|
195 ->outdent() |
|
196 ->write(")\n") |
|
197 ; |
|
198 } |
|
199 |
|
200 $compiler |
|
201 ->outdent() |
|
202 ->write(");\n") |
|
203 ->outdent() |
|
204 ->write("}\n\n"); |
|
205 ; |
|
206 } |
|
207 |
|
208 protected function compileDisplayHeader(Twig_Compiler $compiler) |
|
209 { |
|
210 $compiler |
|
211 ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n") |
|
212 ->indent() |
|
213 ; |
|
214 } |
|
215 |
|
216 protected function compileDisplayFooter(Twig_Compiler $compiler) |
|
217 { |
|
218 $compiler |
|
219 ->outdent() |
|
220 ->write("}\n\n") |
|
221 ; |
|
222 } |
|
223 |
|
224 protected function compileClassFooter(Twig_Compiler $compiler) |
|
225 { |
|
226 $compiler |
|
227 ->outdent() |
|
228 ->write("}\n") |
|
229 ; |
|
230 } |
|
231 |
|
232 protected function compileMacros(Twig_Compiler $compiler) |
|
233 { |
|
234 $compiler->subcompile($this->getNode('macros')); |
|
235 } |
|
236 |
|
237 protected function compileGetTemplateName(Twig_Compiler $compiler) |
|
238 { |
|
239 $compiler |
|
240 ->write("public function getTemplateName()\n", "{\n") |
|
241 ->indent() |
|
242 ->write('return ') |
|
243 ->repr($this->getAttribute('filename')) |
|
244 ->raw(";\n") |
|
245 ->outdent() |
|
246 ->write("}\n\n") |
|
247 ; |
|
248 } |
|
249 |
|
250 protected function compileIsTraitable(Twig_Compiler $compiler) |
|
251 { |
|
252 // A template can be used as a trait if: |
|
253 // * it has no parent |
|
254 // * it has no macros |
|
255 // * it has no body |
|
256 // |
|
257 // Put another way, a template can be used as a trait if it |
|
258 // only contains blocks and use statements. |
|
259 $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros')); |
|
260 if ($traitable) { |
|
261 if (!count($nodes = $this->getNode('body'))) { |
|
262 $nodes = new Twig_Node(array($this->getNode('body'))); |
|
263 } |
|
264 |
|
265 foreach ($nodes as $node) { |
|
266 if (!count($node)) { |
|
267 continue; |
|
268 } |
|
269 |
|
270 if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) { |
|
271 continue; |
|
272 } |
|
273 |
|
274 if ($node instanceof Twig_Node_BlockReference) { |
|
275 continue; |
|
276 } |
|
277 |
|
278 $traitable = false; |
|
279 break; |
|
280 } |
|
281 } |
|
282 |
|
283 $compiler |
|
284 ->write("public function isTraitable()\n", "{\n") |
|
285 ->indent() |
|
286 ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false')) |
|
287 ->outdent() |
|
288 ->write("}\n") |
|
289 ; |
|
290 } |
|
291 |
|
292 public function compileLoadTemplate(Twig_Compiler $compiler, $node, $var) |
|
293 { |
|
294 if ($node instanceof Twig_Node_Expression_Constant) { |
|
295 $compiler |
|
296 ->write(sprintf("%s = \$this->env->loadTemplate(", $var)) |
|
297 ->subcompile($node) |
|
298 ->raw(");\n") |
|
299 ; |
|
300 } else { |
|
301 $compiler |
|
302 ->write(sprintf("%s = ", $var)) |
|
303 ->subcompile($node) |
|
304 ->raw(";\n") |
|
305 ->write(sprintf("if (!%s", $var)) |
|
306 ->raw(" instanceof Twig_Template) {\n") |
|
307 ->indent() |
|
308 ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var)) |
|
309 ->outdent() |
|
310 ->write("}\n") |
|
311 ; |
|
312 } |
|
313 } |
|
314 } |