5cd8d0502f60c9de753d9eb23532743edd6f5349
[yaffs-website] / vendor / twig / twig / lib / Twig / Node / Module.php
1 <?php
2
3 /*
4  * This file is part of Twig.
5  *
6  * (c) Fabien Potencier
7  * (c) 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  * Consider this class as being final. If you need to customize the behavior of
17  * the generated class, consider adding nodes to the following nodes: display_start,
18  * display_end, constructor_start, constructor_end, and class_end.
19  *
20  * @author Fabien Potencier <fabien@symfony.com>
21  */
22 class Twig_Node_Module extends Twig_Node
23 {
24     private $source;
25
26     public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $name, $source = '')
27     {
28         if (!$name instanceof Twig_Source) {
29             @trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED);
30             $this->source = new Twig_Source($source, $name);
31         } else {
32             $this->source = $name;
33         }
34
35         $nodes = array(
36             'body' => $body,
37             'blocks' => $blocks,
38             'macros' => $macros,
39             'traits' => $traits,
40             'display_start' => new Twig_Node(),
41             'display_end' => new Twig_Node(),
42             'constructor_start' => new Twig_Node(),
43             'constructor_end' => new Twig_Node(),
44             'class_end' => new Twig_Node(),
45         );
46         if (null !== $parent) {
47             $nodes['parent'] = $parent;
48         }
49
50         // embedded templates are set as attributes so that they are only visited once by the visitors
51         parent::__construct($nodes, array(
52             // source to be remove in 2.0
53             'source' => $this->source->getCode(),
54             // filename to be remove in 2.0 (use getTemplateName() instead)
55             'filename' => $this->source->getName(),
56             'index' => null,
57             'embedded_templates' => $embeddedTemplates,
58         ), 1);
59
60         // populate the template name of all node children
61         $this->setTemplateName($this->source->getName());
62     }
63
64     public function setIndex($index)
65     {
66         $this->setAttribute('index', $index);
67     }
68
69     public function compile(Twig_Compiler $compiler)
70     {
71         $this->compileTemplate($compiler);
72
73         foreach ($this->getAttribute('embedded_templates') as $template) {
74             $compiler->subcompile($template);
75         }
76     }
77
78     protected function compileTemplate(Twig_Compiler $compiler)
79     {
80         if (!$this->getAttribute('index')) {
81             $compiler->write('<?php');
82         }
83
84         $this->compileClassHeader($compiler);
85
86         if (
87             count($this->getNode('blocks'))
88             || count($this->getNode('traits'))
89             || !$this->hasNode('parent')
90             || $this->getNode('parent') instanceof Twig_Node_Expression_Constant
91             || count($this->getNode('constructor_start'))
92             || count($this->getNode('constructor_end'))
93         ) {
94             $this->compileConstructor($compiler);
95         }
96
97         $this->compileGetParent($compiler);
98
99         $this->compileDisplay($compiler);
100
101         $compiler->subcompile($this->getNode('blocks'));
102
103         $this->compileMacros($compiler);
104
105         $this->compileGetTemplateName($compiler);
106
107         $this->compileIsTraitable($compiler);
108
109         $this->compileDebugInfo($compiler);
110
111         $this->compileGetSource($compiler);
112
113         $this->compileGetSourceContext($compiler);
114
115         $this->compileClassFooter($compiler);
116     }
117
118     protected function compileGetParent(Twig_Compiler $compiler)
119     {
120         if (!$this->hasNode('parent')) {
121             return;
122         }
123         $parent = $this->getNode('parent');
124
125         $compiler
126             ->write("protected function doGetParent(array \$context)\n", "{\n")
127             ->indent()
128             ->addDebugInfo($parent)
129             ->write('return ')
130         ;
131
132         if ($parent instanceof Twig_Node_Expression_Constant) {
133             $compiler->subcompile($parent);
134         } else {
135             $compiler
136                 ->raw('$this->loadTemplate(')
137                 ->subcompile($parent)
138                 ->raw(', ')
139                 ->repr($this->source->getName())
140                 ->raw(', ')
141                 ->repr($parent->getTemplateLine())
142                 ->raw(')')
143             ;
144         }
145
146         $compiler
147             ->raw(";\n")
148             ->outdent()
149             ->write("}\n\n")
150         ;
151     }
152
153     protected function compileClassHeader(Twig_Compiler $compiler)
154     {
155         $compiler
156             ->write("\n\n")
157             // if the template name contains */, add a blank to avoid a PHP parse error
158             ->write('/* '.str_replace('*/', '* /', $this->source->getName())." */\n")
159             ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->source->getName(), $this->getAttribute('index')))
160             ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
161             ->write("{\n")
162             ->indent()
163         ;
164     }
165
166     protected function compileConstructor(Twig_Compiler $compiler)
167     {
168         $compiler
169             ->write("public function __construct(Twig_Environment \$env)\n", "{\n")
170             ->indent()
171             ->subcompile($this->getNode('constructor_start'))
172             ->write("parent::__construct(\$env);\n\n")
173         ;
174
175         // parent
176         if (!$this->hasNode('parent')) {
177             $compiler->write("\$this->parent = false;\n\n");
178         } elseif (($parent = $this->getNode('parent')) && $parent instanceof Twig_Node_Expression_Constant) {
179             $compiler
180                 ->addDebugInfo($parent)
181                 ->write('$this->parent = $this->loadTemplate(')
182                 ->subcompile($parent)
183                 ->raw(', ')
184                 ->repr($this->source->getName())
185                 ->raw(', ')
186                 ->repr($parent->getTemplateLine())
187                 ->raw(");\n")
188             ;
189         }
190
191         $countTraits = count($this->getNode('traits'));
192         if ($countTraits) {
193             // traits
194             foreach ($this->getNode('traits') as $i => $trait) {
195                 $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
196
197                 $compiler
198                     ->addDebugInfo($trait->getNode('template'))
199                     ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
200                     ->indent()
201                     ->write("throw new Twig_Error_Runtime('Template \"'.")
202                     ->subcompile($trait->getNode('template'))
203                     ->raw(".'\" cannot be used as a trait.');\n")
204                     ->outdent()
205                     ->write("}\n")
206                     ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
207                 ;
208
209                 foreach ($trait->getNode('targets') as $key => $value) {
210                     $compiler
211                         ->write(sprintf('if (!isset($_trait_%s_blocks[', $i))
212                         ->string($key)
213                         ->raw("])) {\n")
214                         ->indent()
215                         ->write("throw new Twig_Error_Runtime(sprintf('Block ")
216                         ->string($key)
217                         ->raw(' is not defined in trait ')
218                         ->subcompile($trait->getNode('template'))
219                         ->raw(".'));\n")
220                         ->outdent()
221                         ->write("}\n\n")
222
223                         ->write(sprintf('$_trait_%s_blocks[', $i))
224                         ->subcompile($value)
225                         ->raw(sprintf('] = $_trait_%s_blocks[', $i))
226                         ->string($key)
227                         ->raw(sprintf(']; unset($_trait_%s_blocks[', $i))
228                         ->string($key)
229                         ->raw("]);\n\n")
230                     ;
231                 }
232             }
233
234             if ($countTraits > 1) {
235                 $compiler
236                     ->write("\$this->traits = array_merge(\n")
237                     ->indent()
238                 ;
239
240                 for ($i = 0; $i < $countTraits; ++$i) {
241                     $compiler
242                         ->write(sprintf('$_trait_%s_blocks'.($i == $countTraits - 1 ? '' : ',')."\n", $i))
243                     ;
244                 }
245
246                 $compiler
247                     ->outdent()
248                     ->write(");\n\n")
249                 ;
250             } else {
251                 $compiler
252                     ->write("\$this->traits = \$_trait_0_blocks;\n\n")
253                 ;
254             }
255
256             $compiler
257                 ->write("\$this->blocks = array_merge(\n")
258                 ->indent()
259                 ->write("\$this->traits,\n")
260                 ->write("array(\n")
261             ;
262         } else {
263             $compiler
264                 ->write("\$this->blocks = array(\n")
265             ;
266         }
267
268         // blocks
269         $compiler
270             ->indent()
271         ;
272
273         foreach ($this->getNode('blocks') as $name => $node) {
274             $compiler
275                 ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
276             ;
277         }
278
279         if ($countTraits) {
280             $compiler
281                 ->outdent()
282                 ->write(")\n")
283             ;
284         }
285
286         $compiler
287             ->outdent()
288             ->write(");\n")
289             ->outdent()
290             ->subcompile($this->getNode('constructor_end'))
291             ->write("}\n\n")
292         ;
293     }
294
295     protected function compileDisplay(Twig_Compiler $compiler)
296     {
297         $compiler
298             ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n")
299             ->indent()
300             ->subcompile($this->getNode('display_start'))
301             ->subcompile($this->getNode('body'))
302         ;
303
304         if ($this->hasNode('parent')) {
305             $parent = $this->getNode('parent');
306             $compiler->addDebugInfo($parent);
307             if ($parent instanceof Twig_Node_Expression_Constant) {
308                 $compiler->write('$this->parent');
309             } else {
310                 $compiler->write('$this->getParent($context)');
311             }
312             $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
313         }
314
315         $compiler
316             ->subcompile($this->getNode('display_end'))
317             ->outdent()
318             ->write("}\n\n")
319         ;
320     }
321
322     protected function compileClassFooter(Twig_Compiler $compiler)
323     {
324         $compiler
325             ->subcompile($this->getNode('class_end'))
326             ->outdent()
327             ->write("}\n")
328         ;
329     }
330
331     protected function compileMacros(Twig_Compiler $compiler)
332     {
333         $compiler->subcompile($this->getNode('macros'));
334     }
335
336     protected function compileGetTemplateName(Twig_Compiler $compiler)
337     {
338         $compiler
339             ->write("public function getTemplateName()\n", "{\n")
340             ->indent()
341             ->write('return ')
342             ->repr($this->source->getName())
343             ->raw(";\n")
344             ->outdent()
345             ->write("}\n\n")
346         ;
347     }
348
349     protected function compileIsTraitable(Twig_Compiler $compiler)
350     {
351         // A template can be used as a trait if:
352         //   * it has no parent
353         //   * it has no macros
354         //   * it has no body
355         //
356         // Put another way, a template can be used as a trait if it
357         // only contains blocks and use statements.
358         $traitable = !$this->hasNode('parent') && 0 === count($this->getNode('macros'));
359         if ($traitable) {
360             if ($this->getNode('body') instanceof Twig_Node_Body) {
361                 $nodes = $this->getNode('body')->getNode(0);
362             } else {
363                 $nodes = $this->getNode('body');
364             }
365
366             if (!count($nodes)) {
367                 $nodes = new Twig_Node(array($nodes));
368             }
369
370             foreach ($nodes as $node) {
371                 if (!count($node)) {
372                     continue;
373                 }
374
375                 if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
376                     continue;
377                 }
378
379                 if ($node instanceof Twig_Node_BlockReference) {
380                     continue;
381                 }
382
383                 $traitable = false;
384                 break;
385             }
386         }
387
388         if ($traitable) {
389             return;
390         }
391
392         $compiler
393             ->write("public function isTraitable()\n", "{\n")
394             ->indent()
395             ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
396             ->outdent()
397             ->write("}\n\n")
398         ;
399     }
400
401     protected function compileDebugInfo(Twig_Compiler $compiler)
402     {
403         $compiler
404             ->write("public function getDebugInfo()\n", "{\n")
405             ->indent()
406             ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
407             ->outdent()
408             ->write("}\n\n")
409         ;
410     }
411
412     protected function compileGetSource(Twig_Compiler $compiler)
413     {
414         $compiler
415             ->write("/** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */\n")
416             ->write("public function getSource()\n", "{\n")
417             ->indent()
418             ->write("@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);\n\n")
419             ->write('return $this->getSourceContext()->getCode();')
420             ->raw("\n")
421             ->outdent()
422             ->write("}\n\n")
423         ;
424     }
425
426     protected function compileGetSourceContext(Twig_Compiler $compiler)
427     {
428         $compiler
429             ->write("public function getSourceContext()\n", "{\n")
430             ->indent()
431             ->write('return new Twig_Source(')
432             ->string($compiler->getEnvironment()->isDebug() ? $this->source->getCode() : '')
433             ->raw(', ')
434             ->string($this->source->getName())
435             ->raw(', ')
436             ->string($this->source->getPath())
437             ->raw(");\n")
438             ->outdent()
439             ->write("}\n")
440         ;
441     }
442
443     protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
444     {
445         if ($node instanceof Twig_Node_Expression_Constant) {
446             $compiler
447                 ->write(sprintf('%s = $this->loadTemplate(', $var))
448                 ->subcompile($node)
449                 ->raw(', ')
450                 ->repr($node->getTemplateName())
451                 ->raw(', ')
452                 ->repr($node->getTemplateLine())
453                 ->raw(");\n")
454             ;
455         } else {
456             throw new LogicException('Trait templates can only be constant nodes.');
457         }
458     }
459 }
460
461 class_alias('Twig_Node_Module', 'Twig\Node\ModuleNode', false);