source: classes/phing/tasks/ext/SmartyTask.php @ c0fa2c7

Last change on this file since c0fa2c7 was c0fa2c7, checked in by Benjamin Schultz <bschultz@…>, 2 years ago

fixed "reference" to project instance (undefined variable)

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