Passed
Push — main ( 855108...fcf978 )
by Michiel
15:08 queued 03:28
created

SmartyTask::getControlTemplate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Task\Ext\Smarty;
22
23
use Phing\Exception\BuildException;
24
use Phing\Io\BufferedReader;
25
use Phing\Io\File;
26
use Phing\Io\FileReader;
27
use Phing\Io\FileWriter;
28
use Phing\Io\IOException;
29
use Phing\Project;
30
use Phing\Task;
31
use Phing\Util\Properties;
32
use Phing\Util\StringHelper;
33
use Smarty\Smarty;
34
35
/**
36
 * A phing task for generating output by using Smarty.
37
 *
38
 * This is based on the TexenTask from Apache's Velocity engine.  This class
39
 * was originally proted in order to provide a template compiling system for
40
 * Torque.
41
 *
42
 * TODO:
43
 *        - Add Path / useClasspath support?
44
 *
45
 * @author  Hans Lellelid <[email protected]> (SmartyTask)
46
 * @author  Jason van Zyl <[email protected]> (TexenTask)
47
 * @author  Robert Burrell Donkin <[email protected]>
48
 * @package phing.tasks.ext
49
 */
50
class SmartyTask extends Task
51
{
52
    /**
53
     * Smarty template engine.
54
     *
55
     * @var Smarty
56
     */
57
    protected $context;
58
59
    /**
60
     * Variables that are assigned to the context on parse/compile.
61
     *
62
     * @var array
63
     */
64
    protected $properties = [];
65
66
    /**
67
     * This is the control template that governs the output.
68
     * It may or may not invoke the services of worker
69
     * templates.
70
     *
71
     * @var string
72
     */
73
    protected $controlTemplate;
74
75
    /**
76
     * This is where Smarty will look for templates
77
     * using the file template loader.
78
     *
79
     * @var string
80
     */
81
    protected $templatePath;
82
83
    /**
84
     * This is where texen will place all the output
85
     * that is a product of the generation process.
86
     *
87
     * @var string
88
     */
89
    protected $outputDirectory;
90
91
    /**
92
     * This is the file where the generated text
93
     * will be placed.
94
     *
95
     * @var string
96
     */
97
    protected $outputFile;
98
99
    /**
100
     * <p>
101
     * These are properties that are fed into the
102
     * initial context from a properties file. This
103
     * is simply a convenient way to set some values
104
     * that you wish to make available in the context.
105
     * </p>
106
     * <p>
107
     * These values are not critical, like the template path
108
     * or output path, but allow a convenient way to
109
     * set a value that may be specific to a particular
110
     * generation task.
111
     * </p>
112
     * <p>
113
     * For example, if you are generating scripts to allow
114
     * user to automatically create a database, then
115
     * you might want the <code>$databaseName</code>
116
     * to be placed
117
     * in the initial context so that it is available
118
     * in a script that might look something like the
119
     * following:
120
     * <code><pre>
121
     * #!bin/sh
122
     *
123
     * echo y | mysqladmin create $databaseName
124
     * </pre></code>
125
     * The value of <code>$databaseName</code> isn't critical to
126
     * output, and you obviously don't want to change
127
     * the ant task to simply take a database name.
128
     * So initial context values can be set with
129
     * properties file.
130
     *
131
     * @var Properties
132
     */
133
    protected $contextProperties;
134
135
    /**
136
     * Smarty compiles templates before parsing / replacing tokens in them.
137
     * By default it will try ./templates_c, but you may wish to override this.
138
     *
139
     * @var string
140
     */
141
    protected $compilePath;
142
143
    /**
144
     * Whether to force Smarty to recompile templates.
145
     * Smarty does check file modification time, but you can set this
146
     * to be *sure* that the template will be compiled (of course it will
147
     * be slower if you do).
148
     *
149
     * @var boolean
150
     */
151
    protected $forceCompile = false;
152
153
    /**
154
     * Smarty can use config files.
155
     * This tells Smarty where to look for the config files.
156
     *
157
     * @var string
158
     */
159
    protected $configPath;
160
161
    /**
162
     * Customize the left delimiter for Smarty tags.
163
     *
164
     * @var string
165
     */
166
    protected $leftDelimiter;
167
168
    /**
169
     * Customize the right delimiter for Smarty tags.
170
     *
171
     * @var string
172
     */
173
    protected $rightDelimiter;
174
175
    // -----------------------------------------------------------------------
176
    // The following getters & setters are used by phing to set properties
177
    // specified in the XML for the smarty task.
178
    // -----------------------------------------------------------------------
179
180 1
    public function init()
181
    {
182 1
        if (!class_exists('Smarty\\Smarty')) {
183
            throw new BuildException("To use SmartyTask, you must have the path to Smarty.class.php on your include_path or your \$PHP_CLASSPATH environment variable.");
184
        }
185
    }
186
187
    /**
188
     * [REQUIRED] Set the control template for the
189
     * generating process.
190
     *
191
     * @param  string $controlTemplate
192
     * @return void
193
     */
194 1
    public function setControlTemplate($controlTemplate)
195
    {
196 1
        $this->controlTemplate = $controlTemplate;
197
    }
198
199
    /**
200
     * Get the control template for the
201
     * generating process.
202
     *
203
     * @return string
204
     */
205
    public function getControlTemplate()
206
    {
207
        return $this->controlTemplate;
208
    }
209
210
    /**
211
     * [REQUIRED] Set the path where Smarty will look
212
     * for templates using the file template
213
     * loader.
214
     *
215
     * @param  $templatePath
216
     * @return void
217
     */
218 1
    public function setTemplatePath($templatePath)
219
    {
220 1
        $resolvedPath = "";
221 1
        $tok = strtok($templatePath, ",");
222 1
        while ($tok) {
223
            // resolve relative path from basedir and leave
224
            // absolute path untouched.
225 1
            $fullPath = $this->project->resolveFile($tok);
226 1
            $cpath = $fullPath->getCanonicalPath();
227 1
            if ($cpath === false) {
228
                $this->log("Template directory does not exist: " . $fullPath->getAbsolutePath());
229
            } else {
230 1
                $resolvedPath .= $cpath;
231
            }
232 1
            $tok = strtok(",");
233 1
            if ($tok) {
234
                $resolvedPath .= ",";
235
            }
236
        }
237 1
        $this->templatePath = $resolvedPath;
238
    }
239
240
    /**
241
     * Get the path where Smarty will look
242
     * for templates using the file template
243
     * loader.
244
     *
245
     * @return string
246
     */
247
    public function getTemplatePath()
248
    {
249
        return $this->templatePath;
250
    }
251
252
    /**
253
     * [REQUIRED] Set the output directory. It will be
254
     * created if it doesn't exist.
255
     *
256
     * @param  File $outputDirectory
257
     * @return void
258
     * @throws \Exception
259
     */
260 1
    public function setOutputDirectory(File $outputDirectory)
261
    {
262
        try {
263 1
            if (!$outputDirectory->exists()) {
264
                $this->log(
265
                    "Output directory does not exist, creating: " . $outputDirectory->getPath(),
266
                    Project::MSG_VERBOSE
267
                );
268
                if (!$outputDirectory->mkdirs()) {
269
                    throw new IOException("Unable to create Ouptut directory: " . $outputDirectory->getAbsolutePath());
270
                }
271
            }
272 1
            $this->outputDirectory = $outputDirectory->getCanonicalPath();
273
        } catch (IOException $ioe) {
274
            throw new BuildException($ioe->getMessage());
275
        }
276
    }
277
278
    /**
279
     * Get the output directory.
280
     *
281
     * @return string
282
     */
283
    public function getOutputDirectory()
284
    {
285
        return $this->outputDirectory;
286
    }
287
288
    /**
289
     * [REQUIRED] Set the output file for the
290
     * generation process.
291
     *
292
     * @param  $outputFile
293
     * @return void
294
     */
295 1
    public function setOutputFile($outputFile)
296
    {
297 1
        $this->outputFile = $outputFile;
298
    }
299
300
    /**
301
     * Get the output file for the
302
     * generation process.
303
     *
304
     * @return string
305
     */
306
    public function getOutputFile()
307
    {
308
        return $this->outputFile;
309
    }
310
311
    /**
312
     * Set the path Smarty uses as a "cache" for compiled templates.
313
     *
314
     * @param string $compilePath
315
     */
316 1
    public function setCompilePath($compilePath)
317
    {
318 1
        $this->compilePath = $compilePath;
319
    }
320
321
    /**
322
     * Get the path Smarty uses for compiling templates.
323
     *
324
     * @return string
325
     */
326
    public function getCompilePath()
327
    {
328
        return $this->compilePath;
329
    }
330
331
    /**
332
     * Set whether Smarty should always recompile templates.
333
     *
334
     * @param  boolean $force
335
     * @return void
336
     */
337
    public function setForceCompile($force)
338
    {
339
        $this->forceCompile = (bool) $force;
340
    }
341
342
    /**
343
     * Get whether Smarty should always recompile template.
344
     *
345
     * @return boolean
346
     */
347
    public function getForceCompile()
348
    {
349
        return $this->forceCompile;
350
    }
351
352
    /**
353
     * Set where Smarty looks for config files.
354
     *
355
     * @param  string $configPath
356
     * @return void
357
     */
358
    public function setConfigPath($configPath)
359
    {
360
        $this->configPath = $configPath;
361
    }
362
363
    /**
364
     * Get the path that Smarty uses for looking for config files.
365
     *
366
     * @return string
367
     */
368
    public function getConfigPath()
369
    {
370
        return $this->configPath;
371
    }
372
373
    /**
374
     * Set Smarty template left delimiter.
375
     *
376
     * @param  string $delim
377
     * @return void
378
     */
379
    public function setLeftDelimiter($delim)
380
    {
381
        $this->leftDelimiter = $delim;
382
    }
383
384
    /**
385
     * Get Smarty template right delimiter
386
     *
387
     * @return string
388
     */
389
    public function getLeftDelimiter()
390
    {
391
        return $this->leftDelimiter;
392
    }
393
394
    /**
395
     * Set Smarty template right delimiter.
396
     *
397
     * @param  string $delim
398
     * @return void
399
     */
400
    public function setRightDelimiter($delim)
401
    {
402
        $this->rightDelimiter = $delim;
403
    }
404
405
    /**
406
     * Get Smarty template right delimiter
407
     *
408
     * @return string
409
     */
410
    public function getRightDelimiter()
411
    {
412
        return $this->rightDelimiter;
413
    }
414
415
    /**
416
     * Set the context properties that will be
417
     * fed into the initial context be the
418
     * generating process starts.
419
     *
420
     * @param  string $file
421
     * @throws BuildException
422
     * @return void
423
     */
424
    public function setContextProperties($file)
425
    {
426
        $sources = explode(",", $file);
427
        $this->contextProperties = new Properties();
428
429
        // Always try to get the context properties resource
430
        // from a file first. Templates may be taken from a JAR
431
        // file but the context properties resource may be a
432
        // resource in the filesystem. If this fails than attempt
433
        // to get the context properties resource from the
434
        // classpath.
435
        for ($i = 0, $sourcesLength = count($sources); $i < $sourcesLength; $i++) {
436
            $source = new Properties();
437
438
            try {
439
                // resolve relative path from basedir and leave
440
                // absolute path untouched.
441
                $fullPath = $this->project->resolveFile($sources[$i]);
442
                $this->log("Using contextProperties file: " . $fullPath->__toString());
443
                $source->load($fullPath);
444
            } catch (\Exception $e) {
445
                throw new BuildException(
446
                    "Context properties file " . $sources[$i] .
447
                    " could not be found in the file system!"
448
                );
449
            }
450
451
            $keys = $source->keys();
452
453
            foreach ($keys as $key) {
454
                $name = $key;
455
                $value = $this->project->replaceProperties($source->getProperty($name));
456
                $this->contextProperties->setProperty($name, $value);
457
            }
458
        }
459
    }
460
461
    /**
462
     * Get the context properties that will be
463
     * fed into the initial context be the
464
     * generating process starts.
465
     *
466
     * @return Properties
467
     */
468
    public function getContextProperties()
469
    {
470
        return $this->contextProperties;
471
    }
472
473
    // ---------------------------------------------------------------
474
    // End of XML setters & getters
475
    // ---------------------------------------------------------------
476
477
    /**
478
     * Creates a Smarty object.
479
     *
480
     * @return Smarty    initialized (cleared) Smarty context.
481
     * @throws \Exception the execute method will catch
482
     *                   and rethrow as a <code>BuildException</code>
483
     */
484 1
    public function initControlContext()
485
    {
486 1
        $this->context->clearAllAssign();
487
488 1
        return $this->context;
489
    }
490
491
    /**
492
     * Execute the input script with Smarty
493
     *
494
     * @throws BuildException
495
     *                        BuildExceptions are thrown when required attributes are missing.
496
     *                        Exceptions thrown by Smarty are rethrown as BuildExceptions.
497
     */
498 1
    public function main()
499
    {
500
501
        // Make sure the template path is set.
502 1
        if (empty($this->templatePath)) {
503
            throw new BuildException("The template path needs to be defined!");
504
        }
505
506
        // Make sure the control template is set.
507 1
        if ($this->controlTemplate === null) {
508
            throw new BuildException("The control template needs to be defined!");
509
        }
510
511
        // Make sure the output directory is set.
512 1
        if ($this->outputDirectory === null) {
513
            throw new BuildException("The output directory needs to be defined!");
514
        }
515
516
        // Make sure there is an output file.
517 1
        if ($this->outputFile === null) {
518
            throw new BuildException("The output file needs to be defined!");
519
        }
520
521
        // Setup Smarty runtime.
522
523 1
        $this->context = new Smarty();
524
525 1
        if ($this->compilePath !== null) {
526 1
            $this->log("Using compilePath: " . $this->compilePath);
527 1
            $this->context->setCompileDir($this->compilePath);
528
        }
529
530 1
        if ($this->configPath !== null) {
531
            $this->log("Using configPath: " . $this->configPath);
532
            $this->context->setConfigDir($this->configPath);
533
        }
534
535 1
        if ($this->forceCompile !== null) {
536 1
            $this->context->setForceCompile($this->forceCompile);
537
        }
538
539 1
        if ($this->leftDelimiter !== null) {
540
            $this->context->setLeftDelimiter($this->leftDelimiter);
541
        }
542
543 1
        if ($this->rightDelimiter !== null) {
544
            $this->context->setRightDelimiter($this->rightDelimiter);
545
        }
546
547 1
        if ($this->templatePath !== null) {
548 1
            $this->log("Using templatePath: " . $this->templatePath);
549 1
            $this->context->setTemplateDir($this->templatePath);
550
        }
551
552 1
        $smartyCompilePath = new File($this->context->getCompileDir());
553 1
        if (!$smartyCompilePath->exists()) {
554
            $this->log(
555
                "Compile directory does not exist, creating: " . $smartyCompilePath->getPath(),
556
                Project::MSG_VERBOSE
557
            );
558
            if (!$smartyCompilePath->mkdirs()) {
559
                throw new BuildException("Smarty needs a place to compile templates; specify a 'compilePath' or create " . $this->context->getCompileDir());
560
            }
561
        }
562
563
        // Make sure the output directory exists, if it doesn't
564
        // then create it.
565 1
        $file = new File($this->outputDirectory);
566 1
        if (!$file->exists()) {
567
            $this->log("Output directory does not exist, creating: " . $file->getAbsolutePath());
568
            $file->mkdirs();
569
        }
570
571 1
        $path = $this->outputDirectory . DIRECTORY_SEPARATOR . $this->outputFile;
572 1
        $this->log("Generating to file " . $path);
573
574 1
        $writer = new FileWriter($path);
575
576
        // The generator and the output path should
577
        // be placed in the init context here and
578
        // not in the generator class itself.
579 1
        $c = $this->initControlContext();
580
581
        // Set any variables that need to always
582
        // be loaded
583 1
        $this->populateInitialContext($c);
584
585
        // Feed all the options into the initial
586
        // control context so they are available
587
        // in the control/worker templates.
588 1
        if ($this->contextProperties !== null) {
589
            foreach ($this->contextProperties->keys() as $property) {
590
                $value = $this->contextProperties->getProperty($property);
591
592
                // Special exception (from Texen)
593
                // for properties ending in file.contents:
594
                // in that case we dump the contents of the file
595
                // as the "value" for the Property.
596
                if (StringHelper::endsWith("file.contents", $property)) {
597
                    // pull in contents of file specified
598
599
                    $property = substr($property, 0, strpos($property, "file.contents") - 1);
600
601
                    // reset value, and then
602
                    // read in the contents of the file into that var
603
                    $value = "";
604
                    $f = new File($this->project->resolveFile($value)->getCanonicalPath());
605
                    if ($f->exists()) {
606
                        try {
607
                            $br = new BufferedReader(new FileReader($f));
608
                            $value = $br->read();
609
                        } catch (\Exception $e) {
610
                            throw $e;
611
                        }
612
                    }
613
                } // if ends with file.contents
614
615
                if (StringHelper::isBoolean($value)) {
616
                    $value = StringHelper::booleanValue($value);
617
                }
618
619
                $c->assign($property, $value);
620
            } // foreach property
621
        } // if contextProperties !== null
622
623
        try {
624
            //$c->display($this->controlTemplate);
625 1
            $writer->write($c->fetch($this->controlTemplate));
626 1
            $writer->close();
627
        } catch (IOException $ioe) {
628
            $writer->close();
629
            throw new BuildException("Cannot write parsed template.");
630
        }
631
632 1
        $this->cleanup();
633
    }
634
635
    /**
636
     * <p>Place useful objects into the initial context.</p>
637
     *
638
     * <p>TexenTask places <code>Date().toString()</code> into the
639
     * context as <code>$now</code>.  Subclasses who want to vary the
640
     * objects in the context should override this method.</p>
641
     *
642
     * <p><code>$generator</code> is not put into the context in this
643
     * method.</p>
644
     *
645
     * @param    Smarty $context context to populate, as retrieved from
646
     * {@link #initControlContext()}.
647
     * @return   void
648
     */
649 1
    protected function populateInitialContext(Smarty $context)
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

649
    protected function populateInitialContext(/** @scrutinizer ignore-unused */ Smarty $context)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
650
    {
651 1
    }
652
653
    /**
654
     * A hook method called at the end of {@link #execute()} which can
655
     * be overridden to perform any necessary cleanup activities (such
656
     * as the release of database connections, etc.).  By default,
657
     * does nothing.
658
     *
659
     * @return void
660
     * @throws \Exception Problem cleaning up.
661
     */
662 1
    protected function cleanup()
663
    {
664 1
    }
665
}
666