aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/Twig/Node/Module.php
diff options
context:
space:
mode:
Diffstat (limited to 'inc/Twig/Node/Module.php')
-rw-r--r--inc/Twig/Node/Module.php371
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 */
18class 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}