diff options
Diffstat (limited to 'inc/Twig/Node/Module.php')
-rw-r--r-- | inc/Twig/Node/Module.php | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/inc/Twig/Node/Module.php b/inc/Twig/Node/Module.php new file mode 100644 index 00000000..585048b8 --- /dev/null +++ b/inc/Twig/Node/Module.php | |||
@@ -0,0 +1,371 @@ | |||
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 | * @author Fabien Potencier <fabien@symfony.com> | ||
17 | */ | ||
18 | class Twig_Node_Module extends Twig_Node | ||
19 | { | ||
20 | public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename) | ||
21 | { | ||
22 | // embedded templates are set as attributes so that they are only visited once by the visitors | ||
23 | parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename, 'index' => null, 'embedded_templates' => $embeddedTemplates), 1); | ||
24 | } | ||
25 | |||
26 | public function setIndex($index) | ||
27 | { | ||
28 | $this->setAttribute('index', $index); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * Compiles the node to PHP. | ||
33 | * | ||
34 | * @param Twig_Compiler A Twig_Compiler instance | ||
35 | */ | ||
36 | public function compile(Twig_Compiler $compiler) | ||
37 | { | ||
38 | $this->compileTemplate($compiler); | ||
39 | |||
40 | foreach ($this->getAttribute('embedded_templates') as $template) { | ||
41 | $compiler->subcompile($template); | ||
42 | } | ||
43 | } | ||
44 | |||
45 | protected function compileTemplate(Twig_Compiler $compiler) | ||
46 | { | ||
47 | if (!$this->getAttribute('index')) { | ||
48 | $compiler->write('<?php'); | ||
49 | } | ||
50 | |||
51 | $this->compileClassHeader($compiler); | ||
52 | |||
53 | if (count($this->getNode('blocks')) || count($this->getNode('traits')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) { | ||
54 | $this->compileConstructor($compiler); | ||
55 | } | ||
56 | |||
57 | $this->compileGetParent($compiler); | ||
58 | |||
59 | $this->compileDisplayHeader($compiler); | ||
60 | |||
61 | $this->compileDisplayBody($compiler); | ||
62 | |||
63 | $this->compileDisplayFooter($compiler); | ||
64 | |||
65 | $compiler->subcompile($this->getNode('blocks')); | ||
66 | |||
67 | $this->compileMacros($compiler); | ||
68 | |||
69 | $this->compileGetTemplateName($compiler); | ||
70 | |||
71 | $this->compileIsTraitable($compiler); | ||
72 | |||
73 | $this->compileDebugInfo($compiler); | ||
74 | |||
75 | $this->compileClassFooter($compiler); | ||
76 | } | ||
77 | |||
78 | protected function compileGetParent(Twig_Compiler $compiler) | ||
79 | { | ||
80 | if (null === $this->getNode('parent')) { | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | $compiler | ||
85 | ->write("protected function doGetParent(array \$context)\n", "{\n") | ||
86 | ->indent() | ||
87 | ->write("return ") | ||
88 | ; | ||
89 | |||
90 | if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { | ||
91 | $compiler->subcompile($this->getNode('parent')); | ||
92 | } else { | ||
93 | $compiler | ||
94 | ->raw("\$this->env->resolveTemplate(") | ||
95 | ->subcompile($this->getNode('parent')) | ||
96 | ->raw(")") | ||
97 | ; | ||
98 | } | ||
99 | |||
100 | $compiler | ||
101 | ->raw(";\n") | ||
102 | ->outdent() | ||
103 | ->write("}\n\n") | ||
104 | ; | ||
105 | } | ||
106 | |||
107 | protected function compileDisplayBody(Twig_Compiler $compiler) | ||
108 | { | ||
109 | $compiler->subcompile($this->getNode('body')); | ||
110 | |||
111 | if (null !== $this->getNode('parent')) { | ||
112 | if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { | ||
113 | $compiler->write("\$this->parent"); | ||
114 | } else { | ||
115 | $compiler->write("\$this->getParent(\$context)"); | ||
116 | } | ||
117 | $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n"); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | protected function compileClassHeader(Twig_Compiler $compiler) | ||
122 | { | ||
123 | $compiler | ||
124 | ->write("\n\n") | ||
125 | // if the filename contains */, add a blank to avoid a PHP parse error | ||
126 | ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n") | ||
127 | ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index'))) | ||
128 | ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass())) | ||
129 | ->write("{\n") | ||
130 | ->indent() | ||
131 | ; | ||
132 | } | ||
133 | |||
134 | protected function compileConstructor(Twig_Compiler $compiler) | ||
135 | { | ||
136 | $compiler | ||
137 | ->write("public function __construct(Twig_Environment \$env)\n", "{\n") | ||
138 | ->indent() | ||
139 | ->write("parent::__construct(\$env);\n\n") | ||
140 | ; | ||
141 | |||
142 | // parent | ||
143 | if (null === $this->getNode('parent')) { | ||
144 | $compiler->write("\$this->parent = false;\n\n"); | ||
145 | } elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) { | ||
146 | $compiler | ||
147 | ->write("\$this->parent = \$this->env->loadTemplate(") | ||
148 | ->subcompile($this->getNode('parent')) | ||
149 | ->raw(");\n\n") | ||
150 | ; | ||
151 | } | ||
152 | |||
153 | $countTraits = count($this->getNode('traits')); | ||
154 | if ($countTraits) { | ||
155 | // traits | ||
156 | foreach ($this->getNode('traits') as $i => $trait) { | ||
157 | $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i)); | ||
158 | |||
159 | $compiler | ||
160 | ->addDebugInfo($trait->getNode('template')) | ||
161 | ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i)) | ||
162 | ->indent() | ||
163 | ->write("throw new Twig_Error_Runtime('Template \"'.") | ||
164 | ->subcompile($trait->getNode('template')) | ||
165 | ->raw(".'\" cannot be used as a trait.');\n") | ||
166 | ->outdent() | ||
167 | ->write("}\n") | ||
168 | ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i)) | ||
169 | ; | ||
170 | |||
171 | foreach ($trait->getNode('targets') as $key => $value) { | ||
172 | $compiler | ||
173 | ->write(sprintf("\$_trait_%s_blocks[", $i)) | ||
174 | ->subcompile($value) | ||
175 | ->raw(sprintf("] = \$_trait_%s_blocks[", $i)) | ||
176 | ->string($key) | ||
177 | ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i)) | ||
178 | ->string($key) | ||
179 | ->raw("]);\n\n") | ||
180 | ; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if ($countTraits > 1) { | ||
185 | $compiler | ||
186 | ->write("\$this->traits = array_merge(\n") | ||
187 | ->indent() | ||
188 | ; | ||
189 | |||
190 | for ($i = 0; $i < $countTraits; $i++) { | ||
191 | $compiler | ||
192 | ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i)) | ||
193 | ; | ||
194 | } | ||
195 | |||
196 | $compiler | ||
197 | ->outdent() | ||
198 | ->write(");\n\n") | ||
199 | ; | ||
200 | } else { | ||
201 | $compiler | ||
202 | ->write("\$this->traits = \$_trait_0_blocks;\n\n") | ||
203 | ; | ||
204 | } | ||
205 | |||
206 | $compiler | ||
207 | ->write("\$this->blocks = array_merge(\n") | ||
208 | ->indent() | ||
209 | ->write("\$this->traits,\n") | ||
210 | ->write("array(\n") | ||
211 | ; | ||
212 | } else { | ||
213 | $compiler | ||
214 | ->write("\$this->blocks = array(\n") | ||
215 | ; | ||
216 | } | ||
217 | |||
218 | // blocks | ||
219 | $compiler | ||
220 | ->indent() | ||
221 | ; | ||
222 | |||
223 | foreach ($this->getNode('blocks') as $name => $node) { | ||
224 | $compiler | ||
225 | ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name)) | ||
226 | ; | ||
227 | } | ||
228 | |||
229 | if ($countTraits) { | ||
230 | $compiler | ||
231 | ->outdent() | ||
232 | ->write(")\n") | ||
233 | ; | ||
234 | } | ||
235 | |||
236 | $compiler | ||
237 | ->outdent() | ||
238 | ->write(");\n") | ||
239 | ->outdent() | ||
240 | ->write("}\n\n"); | ||
241 | ; | ||
242 | } | ||
243 | |||
244 | protected function compileDisplayHeader(Twig_Compiler $compiler) | ||
245 | { | ||
246 | $compiler | ||
247 | ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n") | ||
248 | ->indent() | ||
249 | ; | ||
250 | } | ||
251 | |||
252 | protected function compileDisplayFooter(Twig_Compiler $compiler) | ||
253 | { | ||
254 | $compiler | ||
255 | ->outdent() | ||
256 | ->write("}\n\n") | ||
257 | ; | ||
258 | } | ||
259 | |||
260 | protected function compileClassFooter(Twig_Compiler $compiler) | ||
261 | { | ||
262 | $compiler | ||
263 | ->outdent() | ||
264 | ->write("}\n") | ||
265 | ; | ||
266 | } | ||
267 | |||
268 | protected function compileMacros(Twig_Compiler $compiler) | ||
269 | { | ||
270 | $compiler->subcompile($this->getNode('macros')); | ||
271 | } | ||
272 | |||
273 | protected function compileGetTemplateName(Twig_Compiler $compiler) | ||
274 | { | ||
275 | $compiler | ||
276 | ->write("public function getTemplateName()\n", "{\n") | ||
277 | ->indent() | ||
278 | ->write('return ') | ||
279 | ->repr($this->getAttribute('filename')) | ||
280 | ->raw(";\n") | ||
281 | ->outdent() | ||
282 | ->write("}\n\n") | ||
283 | ; | ||
284 | } | ||
285 | |||
286 | protected function compileIsTraitable(Twig_Compiler $compiler) | ||
287 | { | ||
288 | // A template can be used as a trait if: | ||
289 | // * it has no parent | ||
290 | // * it has no macros | ||
291 | // * it has no body | ||
292 | // | ||
293 | // Put another way, a template can be used as a trait if it | ||
294 | // only contains blocks and use statements. | ||
295 | $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros')); | ||
296 | if ($traitable) { | ||
297 | if ($this->getNode('body') instanceof Twig_Node_Body) { | ||
298 | $nodes = $this->getNode('body')->getNode(0); | ||
299 | } else { | ||
300 | $nodes = $this->getNode('body'); | ||
301 | } | ||
302 | |||
303 | if (!count($nodes)) { | ||
304 | $nodes = new Twig_Node(array($nodes)); | ||
305 | } | ||
306 | |||
307 | foreach ($nodes as $node) { | ||
308 | if (!count($node)) { | ||
309 | continue; | ||
310 | } | ||
311 | |||
312 | if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) { | ||
313 | continue; | ||
314 | } | ||
315 | |||
316 | if ($node instanceof Twig_Node_BlockReference) { | ||
317 | continue; | ||
318 | } | ||
319 | |||
320 | $traitable = false; | ||
321 | break; | ||
322 | } | ||
323 | } | ||
324 | |||
325 | if ($traitable) { | ||
326 | return; | ||
327 | } | ||
328 | |||
329 | $compiler | ||
330 | ->write("public function isTraitable()\n", "{\n") | ||
331 | ->indent() | ||
332 | ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false')) | ||
333 | ->outdent() | ||
334 | ->write("}\n\n") | ||
335 | ; | ||
336 | } | ||
337 | |||
338 | protected function compileDebugInfo(Twig_Compiler $compiler) | ||
339 | { | ||
340 | $compiler | ||
341 | ->write("public function getDebugInfo()\n", "{\n") | ||
342 | ->indent() | ||
343 | ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true)))) | ||
344 | ->outdent() | ||
345 | ->write("}\n") | ||
346 | ; | ||
347 | } | ||
348 | |||
349 | protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var) | ||
350 | { | ||
351 | if ($node instanceof Twig_Node_Expression_Constant) { | ||
352 | $compiler | ||
353 | ->write(sprintf("%s = \$this->env->loadTemplate(", $var)) | ||
354 | ->subcompile($node) | ||
355 | ->raw(");\n") | ||
356 | ; | ||
357 | } else { | ||
358 | $compiler | ||
359 | ->write(sprintf("%s = ", $var)) | ||
360 | ->subcompile($node) | ||
361 | ->raw(";\n") | ||
362 | ->write(sprintf("if (!%s", $var)) | ||
363 | ->raw(" instanceof Twig_Template) {\n") | ||
364 | ->indent() | ||
365 | ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var)) | ||
366 | ->outdent() | ||
367 | ->write("}\n") | ||
368 | ; | ||
369 | } | ||
370 | } | ||
371 | } | ||