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, $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')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
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->compileDebugInfo($compiler);
61

    
62
        $this->compileClassFooter($compiler);
63
    }
64

    
65
    protected function compileGetParent(Twig_Compiler $compiler)
66
    {
67
        if (null === $this->getNode('parent')) {
68
            return;
69
        }
70

    
71
        $compiler
72
            ->write("protected function doGetParent(array \$context)\n", "{\n")
73
            ->indent()
74
            ->write("return ")
75
        ;
76

    
77
        if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
78
            $compiler->subcompile($this->getNode('parent'));
79
        } else {
80
            $compiler
81
                ->raw("\$this->env->resolveTemplate(")
82
                ->subcompile($this->getNode('parent'))
83
                ->raw(")")
84
            ;
85
        }
86

    
87
        $compiler
88
            ->raw(";\n")
89
            ->outdent()
90
            ->write("}\n\n")
91
        ;
92
    }
93

    
94
    protected function compileDisplayBody(Twig_Compiler $compiler)
95
    {
96
        $compiler->subcompile($this->getNode('body'));
97

    
98
        if (null !== $this->getNode('parent')) {
99
            if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
100
                $compiler->write("\$this->parent");
101
            } else {
102
                $compiler->write("\$this->getParent(\$context)");
103
            }
104
            $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
105
        }
106
    }
107

    
108
    protected function compileClassHeader(Twig_Compiler $compiler)
109
    {
110
        $compiler
111
            ->write("<?php\n\n")
112
            // if the filename contains */, add a blank to avoid a PHP parse error
113
            ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
114
            ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename')))
115
            ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
116
            ->write("{\n")
117
            ->indent()
118
        ;
119
    }
120

    
121
    protected function compileConstructor(Twig_Compiler $compiler)
122
    {
123
        $compiler
124
            ->write("public function __construct(Twig_Environment \$env)\n", "{\n")
125
            ->indent()
126
            ->write("parent::__construct(\$env);\n\n")
127
        ;
128

    
129
        // parent
130
        if (null === $this->getNode('parent')) {
131
            $compiler->write("\$this->parent = false;\n\n");
132
        } elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
133
            $compiler
134
                ->write("\$this->parent = \$this->env->loadTemplate(")
135
                ->subcompile($this->getNode('parent'))
136
                ->raw(");\n\n")
137
            ;
138
        }
139

    
140
        $countTraits = count($this->getNode('traits'));
141
        if ($countTraits) {
142
            // traits
143
            foreach ($this->getNode('traits') as $i => $trait) {
144
                $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
145

    
146
                $compiler
147
                    ->addDebugInfo($trait->getNode('template'))
148
                    ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
149
                    ->indent()
150
                    ->write("throw new Twig_Error_Runtime('Template \"'.")
151
                    ->subcompile($trait->getNode('template'))
152
                    ->raw(".'\" cannot be used as a trait.');\n")
153
                    ->outdent()
154
                    ->write("}\n")
155
                    ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
156
                ;
157

    
158
                foreach ($trait->getNode('targets') as $key => $value) {
159
                    $compiler
160
                        ->write(sprintf("\$_trait_%s_blocks[", $i))
161
                        ->subcompile($value)
162
                        ->raw(sprintf("] = \$_trait_%s_blocks[", $i))
163
                        ->string($key)
164
                        ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i))
165
                        ->string($key)
166
                        ->raw("]);\n\n")
167
                    ;
168
                }
169
            }
170

    
171
            if ($countTraits > 1) {
172
                $compiler
173
                    ->write("\$this->traits = array_merge(\n")
174
                    ->indent()
175
                ;
176

    
177
                for ($i = 0; $i < $countTraits; $i++) {
178
                    $compiler
179
                        ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i))
180
                    ;
181
                }
182

    
183
                $compiler
184
                    ->outdent()
185
                    ->write(");\n\n")
186
                ;
187
            } else {
188
                $compiler
189
                    ->write("\$this->traits = \$_trait_0_blocks;\n\n")
190
                ;
191
            }
192

    
193
            $compiler
194
                ->write("\$this->blocks = array_merge(\n")
195
                ->indent()
196
                ->write("\$this->traits,\n")
197
                ->write("array(\n")
198
            ;
199
        } else {
200
            $compiler
201
                ->write("\$this->blocks = array(\n")
202
            ;
203
        }
204

    
205
        // blocks
206
        $compiler
207
            ->indent()
208
        ;
209

    
210
        foreach ($this->getNode('blocks') as $name => $node) {
211
            $compiler
212
                ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
213
            ;
214
        }
215

    
216
        if ($countTraits) {
217
            $compiler
218
                ->outdent()
219
                ->write(")\n")
220
            ;
221
        }
222

    
223
        $compiler
224
            ->outdent()
225
            ->write(");\n")
226
            ->outdent()
227
            ->write("}\n\n");
228
        ;
229
    }
230

    
231
    protected function compileDisplayHeader(Twig_Compiler $compiler)
232
    {
233
        $compiler
234
            ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n")
235
            ->indent()
236
        ;
237
    }
238

    
239
    protected function compileDisplayFooter(Twig_Compiler $compiler)
240
    {
241
        $compiler
242
            ->outdent()
243
            ->write("}\n\n")
244
        ;
245
    }
246

    
247
    protected function compileClassFooter(Twig_Compiler $compiler)
248
    {
249
        $compiler
250
            ->outdent()
251
            ->write("}\n")
252
        ;
253
    }
254

    
255
    protected function compileMacros(Twig_Compiler $compiler)
256
    {
257
        $compiler->subcompile($this->getNode('macros'));
258
    }
259

    
260
    protected function compileGetTemplateName(Twig_Compiler $compiler)
261
    {
262
        $compiler
263
            ->write("public function getTemplateName()\n", "{\n")
264
            ->indent()
265
            ->write('return ')
266
            ->repr($this->getAttribute('filename'))
267
            ->raw(";\n")
268
            ->outdent()
269
            ->write("}\n\n")
270
        ;
271
    }
272

    
273
    protected function compileIsTraitable(Twig_Compiler $compiler)
274
    {
275
        // A template can be used as a trait if:
276
        //   * it has no parent
277
        //   * it has no macros
278
        //   * it has no body
279
        //
280
        // Put another way, a template can be used as a trait if it
281
        // only contains blocks and use statements.
282
        $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros'));
283
        if ($traitable) {
284
            if ($this->getNode('body') instanceof Twig_Node_Body) {
285
                $nodes = $this->getNode('body')->getNode(0);
286
            } else {
287
                $nodes = $this->getNode('body');
288
            }
289

    
290
            if (!count($nodes)) {
291
                $nodes = new Twig_Node(array($nodes));
292
            }
293

    
294
            foreach ($nodes as $node) {
295
                if (!count($node)) {
296
                    continue;
297
                }
298

    
299
                if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
300
                    continue;
301
                }
302

    
303
                if ($node instanceof Twig_Node_BlockReference) {
304
                    continue;
305
                }
306

    
307
                $traitable = false;
308
                break;
309
            }
310
        }
311

    
312
        if ($traitable) {
313
            return;
314
        }
315

    
316
        $compiler
317
            ->write("public function isTraitable()\n", "{\n")
318
            ->indent()
319
            ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
320
            ->outdent()
321
            ->write("}\n\n")
322
        ;
323
    }
324

    
325
    protected function compileDebugInfo(Twig_Compiler $compiler)
326
    {
327
        $compiler
328
            ->write("public function getDebugInfo()\n", "{\n")
329
            ->indent()
330
            ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
331
            ->outdent()
332
            ->write("}\n")
333
        ;
334
    }
335

    
336
    protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
337
    {
338
        if ($node instanceof Twig_Node_Expression_Constant) {
339
            $compiler
340
                ->write(sprintf("%s = \$this->env->loadTemplate(", $var))
341
                ->subcompile($node)
342
                ->raw(");\n")
343
            ;
344
        } else {
345
            $compiler
346
                ->write(sprintf("%s = ", $var))
347
                ->subcompile($node)
348
                ->raw(";\n")
349
                ->write(sprintf("if (!%s", $var))
350
                ->raw(" instanceof Twig_Template) {\n")
351
                ->indent()
352
                ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var))
353
                ->outdent()
354
                ->write("}\n")
355
            ;
356
        }
357
    }
358
}
(14-14/22)