|
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 if (null === $this->getNode('parent')) { |
|
66 return; |
|
67 } |
|
68 |
|
69 $compiler |
|
70 ->write("public function getParent(array \$context)\n", "{\n") |
|
71 ->indent() |
|
72 ->write("\$parent = ") |
|
73 ->subcompile($this->getNode('parent')) |
|
74 ->raw(";\n") |
|
75 ->write("if (\$parent instanceof Twig_Template) {\n") |
|
76 ->indent() |
|
77 ->write("\$name = \$parent->getTemplateName();\n") |
|
78 ->write("\$this->parent[\$name] = \$parent;\n") |
|
79 ->write("\$parent = \$name;\n") |
|
80 ->outdent() |
|
81 ->write("} elseif (!isset(\$this->parent[\$parent])) {\n") |
|
82 ->indent() |
|
83 ->write("\$this->parent[\$parent] = \$this->env->loadTemplate(\$parent);\n") |
|
84 ->outdent() |
|
85 ->write("}\n\n") |
|
86 ->write("return \$this->parent[\$parent];\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 if (null !== $this->getNode('parent')) { |
|
115 $compiler->write("protected \$parent;\n\n"); |
|
116 } |
|
117 } |
|
118 |
|
119 protected function compileConstructor(Twig_Compiler $compiler) |
|
120 { |
|
121 $compiler |
|
122 ->write("public function __construct(Twig_Environment \$env)\n", "{\n") |
|
123 ->indent() |
|
124 ->write("parent::__construct(\$env);\n\n") |
|
125 ->write("\$this->parent = array();\n") |
|
126 ; |
|
127 |
|
128 $countTraits = count($this->getNode('traits')); |
|
129 if ($countTraits) { |
|
130 // traits |
|
131 foreach ($this->getNode('traits') as $i => $trait) { |
|
132 $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i)); |
|
133 |
|
134 $compiler |
|
135 ->addDebugInfo($trait->getNode('template')) |
|
136 ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i)) |
|
137 ->indent() |
|
138 ->write("throw new Twig_Error_Runtime('Template \"'.") |
|
139 ->subcompile($trait->getNode('template')) |
|
140 ->raw(".'\" cannot be used as a trait.');\n") |
|
141 ->outdent() |
|
142 ->write("}\n") |
|
143 ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i)) |
|
144 ; |
|
145 |
|
146 foreach ($trait->getNode('targets') as $key => $value) { |
|
147 $compiler |
|
148 ->write(sprintf("\$_trait_%s_blocks[", $i)) |
|
149 ->subcompile($value) |
|
150 ->raw(sprintf("] = \$_trait_%s_blocks[", $i)) |
|
151 ->string($key) |
|
152 ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i)) |
|
153 ->string($key) |
|
154 ->raw("]);\n\n") |
|
155 ; |
|
156 } |
|
157 } |
|
158 |
|
159 $compiler |
|
160 ->write("\$this->blocks = array_replace(\n") |
|
161 ->indent() |
|
162 ; |
|
163 |
|
164 for ($i = 0; $i < $countTraits; $i++) { |
|
165 $compiler |
|
166 ->write(sprintf("\$_trait_%s_blocks,\n", $i)) |
|
167 ; |
|
168 } |
|
169 |
|
170 $compiler |
|
171 ->write("array(\n") |
|
172 ; |
|
173 } else { |
|
174 $compiler |
|
175 ->write("\$this->blocks = array(\n") |
|
176 ; |
|
177 } |
|
178 |
|
179 // blocks |
|
180 $compiler |
|
181 ->indent() |
|
182 ; |
|
183 |
|
184 foreach ($this->getNode('blocks') as $name => $node) { |
|
185 $compiler |
|
186 ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name)) |
|
187 ; |
|
188 } |
|
189 |
|
190 if ($countTraits) { |
|
191 $compiler |
|
192 ->outdent() |
|
193 ->write(")\n") |
|
194 ; |
|
195 } |
|
196 |
|
197 $compiler |
|
198 ->outdent() |
|
199 ->write(");\n") |
|
200 ->outdent() |
|
201 ->write("}\n\n"); |
|
202 ; |
|
203 } |
|
204 |
|
205 protected function compileDisplayHeader(Twig_Compiler $compiler) |
|
206 { |
|
207 $compiler |
|
208 ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n") |
|
209 ->indent() |
|
210 ; |
|
211 } |
|
212 |
|
213 protected function compileDisplayFooter(Twig_Compiler $compiler) |
|
214 { |
|
215 $compiler |
|
216 ->outdent() |
|
217 ->write("}\n\n") |
|
218 ; |
|
219 } |
|
220 |
|
221 protected function compileClassFooter(Twig_Compiler $compiler) |
|
222 { |
|
223 $compiler |
|
224 ->outdent() |
|
225 ->write("}\n") |
|
226 ; |
|
227 } |
|
228 |
|
229 protected function compileMacros(Twig_Compiler $compiler) |
|
230 { |
|
231 $compiler->subcompile($this->getNode('macros')); |
|
232 } |
|
233 |
|
234 protected function compileGetTemplateName(Twig_Compiler $compiler) |
|
235 { |
|
236 $compiler |
|
237 ->write("public function getTemplateName()\n", "{\n") |
|
238 ->indent() |
|
239 ->write('return ') |
|
240 ->repr($this->getAttribute('filename')) |
|
241 ->raw(";\n") |
|
242 ->outdent() |
|
243 ->write("}\n\n") |
|
244 ; |
|
245 } |
|
246 |
|
247 protected function compileIsTraitable(Twig_Compiler $compiler) |
|
248 { |
|
249 // A template can be used as a trait if: |
|
250 // * it has no parent |
|
251 // * it has no macros |
|
252 // * it has no body |
|
253 // |
|
254 // Put another way, a template can be used as a trait if it |
|
255 // only contains blocks and use statements. |
|
256 $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros')); |
|
257 if ($traitable) { |
|
258 if (!count($nodes = $this->getNode('body'))) { |
|
259 $nodes = new Twig_Node(array($this->getNode('body'))); |
|
260 } |
|
261 |
|
262 foreach ($nodes as $node) { |
|
263 if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) { |
|
264 continue; |
|
265 } |
|
266 |
|
267 if ($node instanceof Twig_Node_BlockReference) { |
|
268 continue; |
|
269 } |
|
270 |
|
271 $traitable = false; |
|
272 break; |
|
273 } |
|
274 } |
|
275 |
|
276 $compiler |
|
277 ->write("public function isTraitable()\n", "{\n") |
|
278 ->indent() |
|
279 ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false')) |
|
280 ->outdent() |
|
281 ->write("}\n") |
|
282 ; |
|
283 } |
|
284 |
|
285 public function compileLoadTemplate(Twig_Compiler $compiler, $node, $var) |
|
286 { |
|
287 if ($node instanceof Twig_Node_Expression_Constant) { |
|
288 $compiler |
|
289 ->write(sprintf("%s = \$this->env->loadTemplate(", $var)) |
|
290 ->subcompile($node) |
|
291 ->raw(");\n") |
|
292 ; |
|
293 } else { |
|
294 $compiler |
|
295 ->write(sprintf("%s = ", $var)) |
|
296 ->subcompile($node) |
|
297 ->raw(";\n") |
|
298 ->write(sprintf("if (!%s", $var)) |
|
299 ->raw(" instanceof Twig_Template) {\n") |
|
300 ->indent() |
|
301 ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var)) |
|
302 ->outdent() |
|
303 ->write("}\n") |
|
304 ; |
|
305 } |
|
306 } |
|
307 } |