Project

General

Profile

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, $embeddedTemplates, $filename)
22
    {
23
        // embedded templates are set as attributes so that they are only visited once by the visitors
24
        parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename, 'index' => null, 'embedded_templates' => $embeddedTemplates), 1);
25
    }
26

    
27
    public function setIndex($index)
28
    {
29
        $this->setAttribute('index', $index);
30
    }
31

    
32
    /**
33
     * Compiles the node to PHP.
34
     *
35
     * @param Twig_Compiler A Twig_Compiler instance
36
     */
37
    public function compile(Twig_Compiler $compiler)
38
    {
39
        $this->compileTemplate($compiler);
40

    
41
        foreach ($this->getAttribute('embedded_templates') as $template) {
42
            $compiler->subcompile($template);
43
        }
44
    }
45

    
46
    protected function compileTemplate(Twig_Compiler $compiler)
47
    {
48
        if (!$this->getAttribute('index')) {
49
            $compiler->write('<?php');
50
        }
51

    
52
        $this->compileClassHeader($compiler);
53

    
54
        if (count($this->getNode('blocks')) || count($this->getNode('traits')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
55
            $this->compileConstructor($compiler);
56
        }
57

    
58
        $this->compileGetParent($compiler);
59

    
60
        $this->compileDisplayHeader($compiler);
61

    
62
        $this->compileDisplayBody($compiler);
63

    
64
        $this->compileDisplayFooter($compiler);
65

    
66
        $compiler->subcompile($this->getNode('blocks'));
67

    
68
        $this->compileMacros($compiler);
69

    
70
        $this->compileGetTemplateName($compiler);
71

    
72
        $this->compileIsTraitable($compiler);
73

    
74
        $this->compileDebugInfo($compiler);
75

    
76
        $this->compileClassFooter($compiler);
77
    }
78

    
79
    protected function compileGetParent(Twig_Compiler $compiler)
80
    {
81
        if (null === $this->getNode('parent')) {
82
            return;
83
        }
84

    
85
        $compiler
86
            ->write("protected function doGetParent(array \$context)\n", "{\n")
87
            ->indent()
88
            ->write("return ")
89
        ;
90

    
91
        if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
92
            $compiler->subcompile($this->getNode('parent'));
93
        } else {
94
            $compiler
95
                ->raw("\$this->env->resolveTemplate(")
96
                ->subcompile($this->getNode('parent'))
97
                ->raw(")")
98
            ;
99
        }
100

    
101
        $compiler
102
            ->raw(";\n")
103
            ->outdent()
104
            ->write("}\n\n")
105
        ;
106
    }
107

    
108
    protected function compileDisplayBody(Twig_Compiler $compiler)
109
    {
110
        $compiler->subcompile($this->getNode('body'));
111

    
112
        if (null !== $this->getNode('parent')) {
113
            if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
114
                $compiler->write("\$this->parent");
115
            } else {
116
                $compiler->write("\$this->getParent(\$context)");
117
            }
118
            $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
119
        }
120
    }
121

    
122
    protected function compileClassHeader(Twig_Compiler $compiler)
123
    {
124
        $compiler
125
            ->write("\n\n")
126
            // if the filename contains */, add a blank to avoid a PHP parse error
127
            ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
128
            ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index')))
129
            ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
130
            ->write("{\n")
131
            ->indent()
132
        ;
133
    }
134

    
135
    protected function compileConstructor(Twig_Compiler $compiler)
136
    {
137
        $compiler
138
            ->write("public function __construct(Twig_Environment \$env)\n", "{\n")
139
            ->indent()
140
            ->write("parent::__construct(\$env);\n\n")
141
        ;
142

    
143
        // parent
144
        if (null === $this->getNode('parent')) {
145
            $compiler->write("\$this->parent = false;\n\n");
146
        } elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
147
            $compiler
148
                ->write("\$this->parent = \$this->env->loadTemplate(")
149
                ->subcompile($this->getNode('parent'))
150
                ->raw(");\n\n")
151
            ;
152
        }
153

    
154
        $countTraits = count($this->getNode('traits'));
155
        if ($countTraits) {
156
            // traits
157
            foreach ($this->getNode('traits') as $i => $trait) {
158
                $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
159

    
160
                $compiler
161
                    ->addDebugInfo($trait->getNode('template'))
162
                    ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
163
                    ->indent()
164
                    ->write("throw new Twig_Error_Runtime('Template \"'.")
165
                    ->subcompile($trait->getNode('template'))
166
                    ->raw(".'\" cannot be used as a trait.');\n")
167
                    ->outdent()
168
                    ->write("}\n")
169
                    ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
170
                ;
171

    
172
                foreach ($trait->getNode('targets') as $key => $value) {
173
                    $compiler
174
                        ->write(sprintf("\$_trait_%s_blocks[", $i))
175
                        ->subcompile($value)
176
                        ->raw(sprintf("] = \$_trait_%s_blocks[", $i))
177
                        ->string($key)
178
                        ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i))
179
                        ->string($key)
180
                        ->raw("]);\n\n")
181
                    ;
182
                }
183
            }
184

    
185
            if ($countTraits > 1) {
186
                $compiler
187
                    ->write("\$this->traits = array_merge(\n")
188
                    ->indent()
189
                ;
190

    
191
                for ($i = 0; $i < $countTraits; $i++) {
192
                    $compiler
193
                        ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i))
194
                    ;
195
                }
196

    
197
                $compiler
198
                    ->outdent()
199
                    ->write(");\n\n")
200
                ;
201
            } else {
202
                $compiler
203
                    ->write("\$this->traits = \$_trait_0_blocks;\n\n")
204
                ;
205
            }
206

    
207
            $compiler
208
                ->write("\$this->blocks = array_merge(\n")
209
                ->indent()
210
                ->write("\$this->traits,\n")
211
                ->write("array(\n")
212
            ;
213
        } else {
214
            $compiler
215
                ->write("\$this->blocks = array(\n")
216
            ;
217
        }
218

    
219
        // blocks
220
        $compiler
221
            ->indent()
222
        ;
223

    
224
        foreach ($this->getNode('blocks') as $name => $node) {
225
            $compiler
226
                ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
227
            ;
228
        }
229

    
230
        if ($countTraits) {
231
            $compiler
232
                ->outdent()
233
                ->write(")\n")
234
            ;
235
        }
236

    
237
        $compiler
238
            ->outdent()
239
            ->write(");\n")
240
            ->outdent()
241
            ->write("}\n\n");
242
        ;
243
    }
244

    
245
    protected function compileDisplayHeader(Twig_Compiler $compiler)
246
    {
247
        $compiler
248
            ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n")
249
            ->indent()
250
        ;
251
    }
252

    
253
    protected function compileDisplayFooter(Twig_Compiler $compiler)
254
    {
255
        $compiler
256
            ->outdent()
257
            ->write("}\n\n")
258
        ;
259
    }
260

    
261
    protected function compileClassFooter(Twig_Compiler $compiler)
262
    {
263
        $compiler
264
            ->outdent()
265
            ->write("}\n")
266
        ;
267
    }
268

    
269
    protected function compileMacros(Twig_Compiler $compiler)
270
    {
271
        $compiler->subcompile($this->getNode('macros'));
272
    }
273

    
274
    protected function compileGetTemplateName(Twig_Compiler $compiler)
275
    {
276
        $compiler
277
            ->write("public function getTemplateName()\n", "{\n")
278
            ->indent()
279
            ->write('return ')
280
            ->repr($this->getAttribute('filename'))
281
            ->raw(";\n")
282
            ->outdent()
283
            ->write("}\n\n")
284
        ;
285
    }
286

    
287
    protected function compileIsTraitable(Twig_Compiler $compiler)
288
    {
289
        // A template can be used as a trait if:
290
        //   * it has no parent
291
        //   * it has no macros
292
        //   * it has no body
293
        //
294
        // Put another way, a template can be used as a trait if it
295
        // only contains blocks and use statements.
296
        $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros'));
297
        if ($traitable) {
298
            if ($this->getNode('body') instanceof Twig_Node_Body) {
299
                $nodes = $this->getNode('body')->getNode(0);
300
            } else {
301
                $nodes = $this->getNode('body');
302
            }
303

    
304
            if (!count($nodes)) {
305
                $nodes = new Twig_Node(array($nodes));
306
            }
307

    
308
            foreach ($nodes as $node) {
309
                if (!count($node)) {
310
                    continue;
311
                }
312

    
313
                if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
314
                    continue;
315
                }
316

    
317
                if ($node instanceof Twig_Node_BlockReference) {
318
                    continue;
319
                }
320

    
321
                $traitable = false;
322
                break;
323
            }
324
        }
325

    
326
        if ($traitable) {
327
            return;
328
        }
329

    
330
        $compiler
331
            ->write("public function isTraitable()\n", "{\n")
332
            ->indent()
333
            ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
334
            ->outdent()
335
            ->write("}\n\n")
336
        ;
337
    }
338

    
339
    protected function compileDebugInfo(Twig_Compiler $compiler)
340
    {
341
        $compiler
342
            ->write("public function getDebugInfo()\n", "{\n")
343
            ->indent()
344
            ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
345
            ->outdent()
346
            ->write("}\n")
347
        ;
348
    }
349

    
350
    protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
351
    {
352
        if ($node instanceof Twig_Node_Expression_Constant) {
353
            $compiler
354
                ->write(sprintf("%s = \$this->env->loadTemplate(", $var))
355
                ->subcompile($node)
356
                ->raw(");\n")
357
            ;
358
        } else {
359
            $compiler
360
                ->write(sprintf("%s = ", $var))
361
                ->subcompile($node)
362
                ->raw(";\n")
363
                ->write(sprintf("if (!%s", $var))
364
                ->raw(" instanceof Twig_Template) {\n")
365
                ->indent()
366
                ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var))
367
                ->outdent()
368
                ->write("}\n")
369
            ;
370
        }
371
    }
372
}
(15-15/23)