62d78cbe27be43146cb89c82401c66ed35e624a8
[shipsimu.git] / inc / classes / main / template / class_TemplateEngine.php
1 <?php
2 /**
3  * The own template engine for loading caching and sending out the web pages
4  * and emails.
5  *
6  * @author              Roland Haeder <webmaster@ship-simu.org>
7  * @version             0.0
8  * @copyright   Copyright(c) 2007, 2008 Roland Haeder, this is free software
9  * @license             GNU GPL 3.0 or any newer version
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 class TemplateEngine extends BaseFrameworkSystem implements CompileableTemplate {
25         /**
26          * The local path name where all templates and sub folders for special
27          * templates are stored. We will internally determine the language plus
28          * "html" for web templates or "emails" for email templates
29          */
30         private $basePath = "";
31
32         /**
33          * The extension for web and email templates (not compiled templates)
34          */
35         private $templateExtension = ".tpl";
36
37         /**
38          * The extension for code templates (not compiled templates)
39          */
40         private $codeExtension = ".ctp";
41
42         /**
43          * Path relative to $basePath and language code for compiled code-templates
44          */
45         private $compileOutputPath = "templates/_compiled";
46
47         /**
48          * The raw (maybe uncompiled) template
49          */
50         private $rawTemplateData = "";
51
52         /**
53          * Template data with compiled-in variables
54          */
55         private $compiledData = "";
56
57         /**
58          * The last loaded template's FQFN for debugging the engine
59          */
60         private $lastTemplate = "";
61
62         /**
63          * The variable stack for the templates. This must be initialized and
64          * shall become an instance of FrameworkArrayObject.
65          */
66         private $varStack = null;
67
68         /**
69          * Configuration variables in a simple array
70          */
71         private $configVariables = array();
72
73         /**
74          * The language instance which should link to an object of LanguageSystem
75          */
76         private $langInstance = null;
77
78         /**
79          * Loaded templates for recursive protection and detection
80          */
81         private $loadedTemplates = array();
82
83         /**
84          * Compiled templates for recursive protection and detection
85          */
86         private $compiledTemplates = array();
87
88         /**
89          * Loaded raw template data
90          */
91         private $loadedRawData = null;
92
93         /**
94          * Raw templates which are linked in code templates
95          */
96         private $rawTemplates = null;
97
98         /**
99          * A regular expression for variable=value pairs
100          */
101         private $regExpVarValue = '/([\w_]+)(="([^"]*)"|=([\w_]+))?/';
102
103         /**
104          * A regular expression for filtering out code tags
105          *
106          * E.g.: {?template:variable=value;var2=value2;[...]?}
107          */
108         private $regExpCodeTags = '/\{\?([a-z_]+)(:("[^"]+"|[^?}]+)+)?\?\}/';
109
110         // Exception codes for the template engine
111         const EXCEPTION_TEMPLATE_TYPE_IS_UNEXPECTED   = 0xa00;
112         const TEMPLATE_CONTAINS_INVALID_VAR_EXCEPTION = 0xa01;
113
114         /**
115          * Private constructor
116          *
117          * @return      void
118          */
119         private final function __construct () {
120                 // Call parent constructor
121                 parent::constructor(__CLASS__);
122
123                 // Set part description
124                 $this->setPartDescr("Template-Engine");
125
126                 // Create unique ID number
127                 $this->createUniqueID();
128
129                 // Clean up a little
130                 $this->removeNumberFormaters();
131                 $this->removeSystemArray();
132         }
133
134         /**
135          * Creates an instance of the class TemplateEngine and prepares it for usage
136          *
137          * @param               $basePath               The local base path for all templates
138          * @param               $langInstance   An instance of LanguageSystem (default)
139          * @param               $ioInstance             An instance of FileIOHandler (default, middleware!)
140          * @return              $tplInstance    An instance of TemplateEngine
141          * @throws              BasePathIsEmptyException                If the provided $basePath is empty
142          * @throws              InvalidBasePathStringException  If $basePath is no string
143          * @throws              BasePathIsNoDirectoryException  If $basePath is no
144          *                                                                                              directory or not found
145          * @throws              BasePathReadProtectedException  If $basePath is
146          *                                                                                              read-protected
147          */
148         public final static function createTemplateEngine ($basePath, ManageableLanguage $langInstance, FileIOHandler $fileIOInstance) {
149                 // Get a new instance
150                 $tplInstance = new TemplateEngine();
151
152                 // Is the base path valid?
153                 if (empty($basePath)) {
154                         // Base path is empty
155                         throw new BasePathIsEmptyException($tplInstance, self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
156                 } elseif (!is_string($basePath)) {
157                         // Is not a string
158                         throw new InvalidBasePathStringException(array($tplInstance, $basePath), self::EXCEPTION_INVALID_STRING);
159                 } elseif (!is_dir($basePath)) {
160                         // Is not a path
161                         throw new BasePathIsNoDirectoryException(array($tplInstance, $basePath), self::EXCEPTION_INVALID_PATH_NAME);
162                 } elseif (!is_readable($basePath)) {
163                         // Is not readable
164                         throw new BasePathReadProtectedException(array($tplInstance, $basePath), self::EXCEPTION_READ_PROTECED_PATH);
165                 }
166
167                 // Get configuration instance
168                 $cfgInstance = $tplInstance->getConfigInstance();
169
170                 // Set the base path
171                 $tplInstance->setBasePath($basePath);
172
173                 // Initialize the variable stack
174                 $tplInstance->initVariableStack();
175
176                 // Set the language and IO instances
177                 $tplInstance->setLanguageInstance($langInstance);
178                 $tplInstance->setIOInstance($fileIOInstance);
179
180                 // Set template extensions
181                 $tplInstance->setRawTemplateExtension($cfgInstance->readConfig("raw_template_extension"));
182                 $tplInstance->setCodeTemplateExtension($cfgInstance->readConfig("code_template_extension"));
183
184                 // Absolute output path for compiled templates
185                 $tplInstance->setCompileOutputPath(PATH . $cfgInstance->readConfig("compile_output_path"));
186
187                 // Return the prepared instance
188                 return $tplInstance;
189         }
190
191         /**
192          * Search for a variable in the stack
193          *
194          * @param               $var            The variable we are looking for
195          * @return      $idx            FALSE means not found, > 0 means found on a specific index
196          */
197         private function isVariableAlreadySet ($var) {
198                 // First everything is not found
199                 $found = false;
200
201                 // Now search for it
202                 for ($idx = $this->varStack->getIterator(); $idx->valid(); $idx->next()) {
203                         // Get current item
204                         $currEntry = $idx->current();
205
206                         // Is the entry found?
207                         if ($currEntry['name'] == $var) {
208                                 // Found!
209                                 $found = $idx->key();
210                                 break;
211                         }
212                 }
213
214                 // Return the current position
215                 return $found;
216         }
217
218         /**
219          * Add a variable to the stack
220          *
221          * @param               $var            The variable we are looking for
222          * @param               $value  The value we want to store in the variable
223          * @return      void
224          */
225         private function addVariable ($var, $value) {
226                 // Add it to the stack
227                 $this->varStack->append(array(
228                         'name'  => $var,
229                         'value' => $value
230                 ));
231         }
232
233         /**
234          * Modify an entry on the stack
235          *
236          * @param               $var            The variable we are looking for
237          * @param               $value  The value we want to store in the variable
238          * @return      void
239          */
240         private function modifyVariable ($var, $value) {
241                 // It should be there so let's look again...
242                 for ($idx = $this->varStack->getIterator(); $idx->valid(); $idx->next()) {
243                         // Get current entry
244                         $currEntry = $idx->current();
245
246                         // Is this the requested variable?
247                         if ($currEntry['name'] == $var) {
248                                 // Change it to the other value
249                                 $this->varStack->offsetSet($idx->key(), array(
250                                         'name'  => $var,
251                                         'value' => $value
252                                 ));
253                         }
254                 }
255         }
256
257         /**
258          * Initialize the variable stack. This holds all variables for later
259          * compilation.
260          *
261          * @return      void
262          */
263         public final function initVariableStack () {
264                 $this->varStack = new FrameworkArrayObject();
265         }
266
267         /**
268          * Setter for language instance which should be LanguageSystem
269          *
270          * @param               $langInstance           The language instance
271          * @return      void
272          */
273         public final function setLanguageInstance (ManageableLanguage $langInstance) {
274                 $this->langInstance = $langInstance;
275         }
276
277         /**
278          * Setter for file I/O instance which should be FileIOHandler
279          *
280          * @param               $ioInstance             The file I/O instance
281          * @return      void
282          */
283         public final function setIOInstance (FileIOHandler $ioInstance) {
284                 $this->ioInstance = $ioInstance;
285         }
286
287         /**
288          * Getter for file I/O instance which should be FileIOHandler
289          *
290          * @return      $ioInstance             The file I/O instance
291          */
292         public final function getIOInstance () {
293                 return $this->ioInstance;
294         }
295
296         /**
297          * Setter for base path
298          *
299          * @param               $basePath               The local base path for all templates
300          * @return      void
301          */
302         public final function setBasePath ($basePath) {
303                 // Cast it
304                 $basePath = (string) $basePath;
305
306                 // And set it
307                 $this->basePath = $basePath;
308         }
309
310         /**
311          * Getter for base path
312          *
313          * @return      $basePath               The local base path for all templates
314          */
315         public final function getBasePath () {
316                 // And set it
317                 return $this->basePath;
318         }
319
320         /**
321          * Setter for template extension
322          *
323          * @param               $templateExtension      The file extension for all uncompiled
324          *                                                      templates
325          * @return      void
326          */
327         public final function setRawTemplateExtension ($templateExtension) {
328                 // Cast it
329                 $templateExtension = (string) $templateExtension;
330
331                 // And set it
332                 $this->templateExtension = $templateExtension;
333         }
334
335         /**
336          * Setter for code template extension
337          *
338          * @param               $codeExtension          The file extension for all uncompiled
339          *                                                      templates
340          * @return      void
341          */
342         public final function setCodeTemplateExtension ($codeExtension) {
343                 // Cast it
344                 $codeExtension = (string) $codeExtension;
345
346                 // And set it
347                 $this->codeExtension = $codeExtension;
348         }
349
350         /**
351          * Getter for template extension
352          *
353          * @return      $templateExtension      The file extension for all uncompiled
354          *                                                      templates
355          */
356         public final function getRawTemplateExtension () {
357                 // And set it
358                 return $this->templateExtension;
359         }
360
361         /**
362          * Getter for code-template extension
363          *
364          * @return      $codeExtension          The file extension for all code-
365          *                                                      templates
366          */
367         public final function getCodeTemplateExtension () {
368                 // And set it
369                 return $this->codeExtension;
370         }
371
372         /**
373          * Setter for path of compiled templates
374          *
375          * @param               $compileOutputPath              The local base path for all
376          *                                                              compiled templates
377          * @return      void
378          */
379         public final function setCompileOutputPath ($compileOutputPath) {
380                 // Cast it
381                 $compileOutputPath = (string) $compileOutputPath;
382
383                 // And set it
384                 $this->compileOutputPath = $compileOutputPath;
385         }
386
387         /**
388          * Setter for template type. Only "html", "emails" and "compiled" should
389          * be sent here
390          *
391          * @param               $templateType   The current template's type
392          * @return      void
393          */
394         private final function setTemplateType ($templateType) {
395                 // Cast it
396                 $templateType = (string) $templateType;
397
398                 // And set it (only 2 letters)
399                 $this->templateType = $templateType;
400         }
401
402         /**
403          * Getter for template type
404          *
405          * @return      $templateType   The current template's type
406          */
407         public final function getTemplateType () {
408                 return $this->templateType;
409         }
410
411         /**
412          * Setter for the last loaded template's FQFN
413          *
414          * @param               $template               The last loaded template
415          * @return      void
416          */
417         private final function setLastTemplate ($template) {
418                 // Cast it to string
419                 $template = (string) $template;
420                 $this->lastTemplate = $template;
421         }
422
423         /**
424          * Getter for the last loaded template's FQFN
425          *
426          * @return      $template               The last loaded template
427          */
428         private final function getLastTemplate () {
429                 return $this->lastTemplate;
430         }
431
432         /**
433          * Assign (add) a given variable with a value
434          *
435          * @param               $var            The variable we are looking for
436          * @param               $value  The value we want to store in the variable
437          * @return      void
438          */
439         public final function assignVariable ($var, $value) {
440                 // First search for the variable if it was already added
441                 $idx = $this->isVariableAlreadySet($var);
442
443                 // Was it found?
444                 if ($idx === false) {
445                         // Add it to the stack
446                         $this->addVariable($var, $value);
447                 } elseif (!empty($value)) {
448                         // Modify the stack entry
449                         $this->modifyVariable($var, $value);
450                 }
451         }
452
453         /**
454          * Assign a given congfiguration variable with a value
455          *
456          * @param               $var            The configuration variable we are looking for
457          * @param               $value  The value we want to store in the variable
458          * @return      void
459          */
460         public final function assignConfigVariable ($var, $value) {
461                 // Sweet and simple...
462                 $this->configVariables[$var] = $value;
463         }
464
465         /**
466          * Removes a given variable
467          *
468          * @param               $var            The variable we are looking for
469          * @return      void
470          */
471         public final function removeVariable ($var) {
472                 // First search for the variable if it was already added
473                 $idx = $this->isVariableAlreadySet($var);
474
475                 // Was it found?
476                 if ($idx !== false) {
477                         // Remove this variable
478                         $this->varStack->offsetUnset($idx);
479                 }
480         }
481
482         /**
483          * Private setter for raw template data
484          *
485          * @param               $rawTemplateData        The raw data from the template
486          * @return              void
487          */
488         private final function setRawTemplateData ($rawTemplateData) {
489                 // Cast it to string
490                 $rawTemplateData = (string) $rawTemplateData;
491
492                 // And store it in this class
493                 $this->rawTemplateData = $rawTemplateData;
494         }
495
496         /**
497          * Private setter for compiled templates
498          */
499         private final function setCompiledData ($compiledData) {
500                 // Cast it to string
501                 $compiledData = (string) $compiledData;
502
503                 // And store it in this class
504                 $this->compiledData = $compiledData;
505         }
506
507         /**
508          * Private loader for all template types
509          *
510          * @param               $template               The template we shall load
511          * @return      void
512          */
513         private final function loadTemplate ($template) {
514                 // Cast it to string
515                 $template = (string) $template;
516
517                 // Get extension for the template
518                 $ext = $this->getRawTemplateExtension();
519
520                 // If we shall load a code-template we need to switch the file extension
521                 if ($this->getTemplateType() == $this->getConfigInstance()->readConfig("code_template_type")) {
522                         // Switch over to the code-template extension
523                         $ext = $this->getCodeTemplateExtension();
524                 }
525
526                 // Construct the FQFN for the template by honoring the current language
527                 $fqfn = sprintf("%s%s/%s/%s%s",
528                         $this->getBasePath(),
529                         $this->langInstance->getLanguageCode(),
530                         $this->getTemplateType(),
531                         $template,
532                         $ext
533                 );
534
535                 // Load the raw template data
536                 $this->loadRawTemplateData($fqfn);
537         }
538
539         /**
540          * A private loader for raw template names
541          *
542          * @param               $fqfn   The full-qualified file name for a template
543          * @return      void
544          * @throws      NullPointerException    If $inputInstance is null
545          * @throws      NoObjectException               If $inputInstance is not an object
546          * @throws      MissingMethodException  If $inputInstance is missing a
547          *                                                              required method
548          */
549         private function loadRawTemplateData ($fqfn) {
550                 // Debug message
551                 if ((defined('DEBUG_TEMPLATE')) && (is_object($this->getDebugInstance()))) $this->getDebugInstance()->output(sprintf("[%s:] Template <strong>%s</strong> vom Typ <strong>%s</strong> wird geladen.<br />\n",
552                         $this->__toString(),
553                         $template,
554                         $this->getTemplateType()
555                 ));
556
557                 // Get a input/output instance from the middleware
558                 $ioInstance = $this->getIOInstance();
559
560                 // Validate the instance
561                 if (is_null($ioInstance)) {
562                         // Throw exception
563                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
564                 } elseif (!is_object($ioInstance)) {
565                         // Throw another exception
566                         throw new NoObjectException($ioInstance, self::EXCEPTION_IS_NO_OBJECT);
567                 } elseif (!method_exists($ioInstance, 'loadFileContents')) {
568                         // Throw yet another exception
569                         throw new MissingMethodException(array($ioInstance, 'loadFileContents'), self::EXCEPTION_MISSING_METHOD);
570                 }
571
572                 // Load the raw template
573                 $rawTemplateData = $ioInstance->loadFileContents($fqfn);
574
575                 // Debug message
576                 if ((defined('DEBUG_TEMPLATE')) && (is_object($this->getDebugInstance()))) $this->getDebugInstance()->output(sprintf("[%s:] <strong>%s</strong> Byte Rohdaten geladen.<br />\n",
577                         $this->__toString(),
578                         strlen($rawTemplateData)
579                 ));
580
581                 // Store the template's contents into this class
582                 $this->setRawTemplateData($rawTemplateData);
583
584                 // Remember the template's FQFN
585                 $this->setLastTemplate($fqfn);
586         }
587
588         /**
589          * Try to assign an extracted template variable as a "content" or "config"
590          * variable.
591          *
592          * @param               $varName                The variable's name (shall be content or
593          *                                              config) by default
594          * @param               $var                    The variable we want to assign
595          */
596         private function assignTemplateVariable ($varName, $var) {
597                 // Is it not a config variable?
598                 if ($varName != "config") {
599                         // Regular template variables
600                         $this->assignVariable($var, "");
601                 } else {
602                         // Configuration variables
603                         $this->assignConfigVariable($var, $this->getConfigInstance()->readConfig($var));
604                 }
605         }
606
607         /**
608          * Extract variables from a given raw data stream
609          *
610          * @param               $rawData                The raw template data we shall analyze
611          * @return      void
612          * @throws      InvalidTemplateVariableNameException    If a variable name
613          *                                                                                      in a template is
614          *                                                                                      invalid
615          */
616         private function extractVariablesFromRawData ($rawData) {
617                 // Cast to string
618                 $rawData = (string) $rawData;
619
620                 // Search for variables
621                 @preg_match_all('/\$(\w+)(\[(\w+)\])?/', $rawData, $variableMatches);
622
623                 // Did we find some variables?
624                 if ((is_array($variableMatches)) && (count($variableMatches) == 4) && (count($variableMatches[0]) > 0)) {
625                         // Initialize all missing variables
626                         foreach ($variableMatches[3] as $key=>$var) {
627                                 // Is the variable name valid?
628                                 if (($variableMatches[1][$key] != $this->getConfigInstance()->readConfig("tpl_valid_var")) && ($variableMatches[1][$key] != "config")) {
629                                         // Invalid variable name
630                                         throw new InvalidTemplateVariableNameException(array($this, $this->getLastTemplate(), $variableMatches[1][$key], $this->getConfigInstance()), self::TEMPLATE_CONTAINS_INVALID_VAR_EXCEPTION);
631                                 }
632
633                                 // Try to assign it, empty strings are being ignored
634                                 $this->assignTemplateVariable($variableMatches[1][$key], $var);
635                         }
636                 }
637         }
638
639         /**
640          * Main analysis of the loaded template
641          *
642          * @param               $templateMatches        Found template place-holders, see below
643          * @return      void
644          *
645          *---------------------------------
646          * Structure of $templateMatches:
647          *---------------------------------
648          * [0] => Array - An array with all full matches
649          * [1] => Array - An array with left part (before the ":") of a match
650          * [2] => Array - An array with right part of a match including ":"
651          * [3] => Array - An array with right part of a match excluding ":"
652          */
653         private function analyzeTemplate ($templateMatches) {
654                 // Backup raw template data
655                 $backup = $this->getRawTemplateData();
656
657                 // Initialize some arrays
658                 if (is_null($this->loadedRawData)) { $this->loadedRawData = array(); $this->rawTemplates = array(); }
659
660                 // Load all requested templates
661                 foreach ($templateMatches[1] as $template) {
662
663                         // Load and compile only templates which we have not yet loaded
664                         // RECURSIVE PROTECTION! BE CAREFUL HERE!
665                         if ((!isset($this->loadedRawData[$template])) && (!in_array($template, $this->loadedTemplates))) {
666
667                                 // Then try to search for code-templates first
668                                 try {
669                                         // Load the code template and remember it's contents
670                                         $this->loadCodeTemplate($template);
671                                         $this->loadedRawData[$template] = $this->getRawTemplateData();
672
673                                         // Remember this template for recursion detection
674                                         // RECURSIVE PROTECTION!
675                                         $this->loadedTemplates[] = $template;
676                                 } catch (FilePointerNotOpenedException $e) {
677                                         // Template not found!
678                                         $this->rawTemplates[] = $template;
679                                 }
680
681                         } // if ((!isset( ...
682
683                 } // for ($templateMatches ...
684
685                 // Restore the raw template data
686                 $this->setRawTemplateData($backup);
687         }
688
689         /**
690          * Compile a given raw template code and remember it for later usage
691          *
692          * @param               $code           The raw template code
693          * @param               $template               The template's name
694          * @return      void
695          */
696         private function compileCode ($code, $template) {
697                 // Is this template already compiled?
698                 if (in_array($template, $this->compiledTemplates)) {
699                         // Abort here...
700                         return;
701                 }
702
703                 // Remember this template being compiled
704                 $this->compiledTemplates[] = $template;
705
706                 // Compile the loaded code in five steps:
707                 //
708                 // 1. Backup current template data
709                 $backup = $this->getRawTemplateData();
710
711                 // 2. Set the current template's raw data as the new content
712                 $this->setRawTemplateData($code);
713
714                 // 3. Compile the template data
715                 $this->compileTemplate();
716
717                 // 4. Remember it's contents
718                 $this->loadedRawData[$template] = $this->getRawTemplateData();
719
720                 // 5. Restore the previous raw content from backup variable
721                 $this->setRawTemplateData($backup);
722         }
723
724         /**
725          * Insert all given and loaded templates by running through all loaded
726          * codes and searching for their place-holder in the main template
727          *
728          * @param               $templateMatches        See method analyzeTemplate()
729          * @return      void
730          */
731         private function insertAllTemplates ($templateMatches) {
732                 // Run through all loaded codes
733                 foreach ($this->loadedRawData as $template => $code) {
734
735                         // Search for the template
736                         $foundIndex = array_search($template, $templateMatches[1]);
737
738                         // Lookup the matching template replacement
739                         if (isset($templateMatches[0][$foundIndex])) {
740
741                                 // Get the current raw template
742                                 $rawData = $this->getRawTemplateData();
743
744                                 // Replace the space holder with the template code
745                                 $rawData = str_replace($templateMatches[0][$foundIndex], $code, $rawData);
746
747                                 // Set the new raw data
748                                 $this->setRawTemplateData($rawData);
749                         }
750                 }
751         }
752
753         /**
754          * Load all extra raw templates
755          *
756          * @return      void
757          */
758         private function loadExtraRawTemplates () {
759                 // Are there some raw templates we need to load?
760                 if (count($this->rawTemplates) > 0) {
761                         // Try to load all raw templates
762                         foreach ($this->rawTemplates as $key => $template) {
763                                 try {
764                                         // Load the template
765                                         $this->loadWebTemplate($template);
766
767                                         // Remember it's contents
768                                         $this->rawTemplates[$template] = $this->getRawTemplateData();
769
770                                         // Remove it from the loader list
771                                         unset($this->rawTemplates[$key]);
772
773                                         // Remember this template for recursion detection
774                                         // RECURSIVE PROTECTION!
775                                         $this->loadedTemplates[] = $template;
776                                 } catch (FilePointerNotOpenedException $e) {
777                                         // This template was never found. We silently ignore it
778                                         unset($this->rawTemplates[$key]);
779                                 }
780                         }
781                 }
782         }
783
784         /**
785          * Assign all found template variables
786          *
787          * @param               $varMatches     An array full of variable/value pairs.
788          * @return      void
789          */
790         private function assignAllVariables ($varMatches) {
791                 // Search for all variables
792                 foreach ($varMatches[1] as $key=>$var) {
793
794                         // Detect leading equals
795                         if (substr($varMatches[2][$key], 0, 1) == "=") {
796                                 // Remove and cast it
797                                 $varMatches[2][$key] = (string) substr($varMatches[2][$key], 1);
798                         }
799
800                         // Do we have some quotes left and right side? Then it is free text
801                         if ((substr($varMatches[2][$key], 0, 1) == "\"") && (substr($varMatches[2][$key], -1, 1) == "\"")) {
802                                 // Free string detected! Which we can assign directly
803                                 $this->assignVariable($var, $varMatches[3][$key]);
804                         } else {
805                                 // Non-string found so we need some deeper analysis...
806                                 die("Deeper analysis not yet implemented!");
807                         }
808
809                 } // for ($varMatches ...
810         }
811         /**
812          * Compiles all loaded raw templates
813          *
814          * @param               $templateMatches        See method analyzeTemplate() for details
815          * @return      void
816          */
817         private function compileRawTemplateData ($templateMatches) {
818                 // Are some code-templates found which we need to compile?
819                 if (count($this->loadedRawData) > 0) {
820
821                         // Then compile all!
822                         foreach ($this->loadedRawData as $template => $code) {
823
824                                 // Search for the template
825                                 $foundIndex = array_search($template, $templateMatches[1]);
826
827                                 // Lookup the matching variable data
828                                 if (isset($templateMatches[3][$foundIndex])) {
829
830                                         // Split it up with another reg. exp. into variable=value pairs
831                                         @preg_match_all($this->regExpVarValue, $templateMatches[3][$foundIndex], $varMatches);
832
833                                         // Assign all variables
834                                         $this->assignAllVariables($varMatches);
835
836                                 } // END - if (isset($templateMatches ...
837
838                                 // Compile the loaded template
839                                 $this->compileCode($code, $template);
840
841                         } // END - foreach ($this->loadedRawData ...
842
843                         // Insert all templates
844                         $this->insertAllTemplates($templateMatches);
845
846                 } // END - if (count($this->loadedRawData) ...
847         }
848
849         /**
850          * Getter for raw template data
851          *
852          * @return      $rawTemplateData        The raw data from the template
853          */
854         public final function getRawTemplateData () {
855                 return $this->rawTemplateData;
856         }
857
858         /**
859          * Getter for compiled templates
860          */
861         public final function getCompiledData () {
862                 return $this->compiledData;
863         }
864
865         /**
866          * Load a specified web template into the engine
867          *
868          * @param               $template               The web template we shall load which is
869          *                                              located in "html" by default
870          * @return      void
871          */
872         public final function loadWebTemplate ($template) {
873                 // Set template type
874                 $this->setTemplateType($this->getConfigInstance()->readConfig("web_template_type"));
875
876                 // Load the special template
877                 $this->loadTemplate($template);
878         }
879
880         /**
881          * Load a specified email template into the engine
882          *
883          * @param               $template               The email template we shall load which is
884          *                                              located in "emails" by default
885          * @return      void
886          */
887         public final function loadEmailTemplate ($template) {
888                 // Set template type
889                 $this->setTemplateType($this->getConfigInstance()->readConfig("email_template_type"));
890
891                 // Load the special template
892                 $this->loadTemplate($template);
893         }
894
895         /**
896          * Load a specified code template into the engine
897          *
898          * @param               $template               The code template we shall load which is
899          *                                              located in "code" by default
900          * @return      void
901          */
902         public final function loadCodeTemplate ($template) {
903                 // Set template type
904                 $this->setTemplateType($this->getConfigInstance()->readConfig("code_template_type"));
905
906                 // Load the special template
907                 $this->loadTemplate($template);
908         }
909
910         /**
911          * Compile all variables by inserting their respective values
912          *
913          * @return      void
914          */
915         public final function compileVariables () {
916                 // Initialize the $content array
917                 $validVar = $this->getConfigInstance()->readConfig("tpl_valid_var");
918                 $dummy = array();
919
920                 // Iterate through all variables
921                 for ($idx = $this->varStack->getIterator(); $idx->valid(); $idx->next()) {
922                         // Get current variable from the stack
923                         $currVariable = $idx->current();
924
925                         // Transfer it's name/value combination to the $content array
926                         $dummy[$currVariable['name']] = $currVariable['value'];
927                 }
928                 $$validVar = $dummy;
929
930                 // Prepare all configuration variables
931                 $config = $this->configVariables;
932
933                 // Remove some variables
934                 unset($idx);
935                 unset($currVariable);
936
937                 // Prepare the eval() command for comiling the template
938                 $eval = sprintf("\$this->setCompiledData(\"%s\");",
939                         addslashes($this->getRawTemplateData())
940                 );
941
942                 // Debug message
943                 if (((defined('DEBUG_EVAL')) || (defined('DEBUG_ALL'))) && (is_object($this->getDebugInstance()))) $this->getDebugInstance()->output(sprintf("[%s:] Konstruierte PHP-Anweisung: <pre><em>%s</em></pre><br />\n",
944                         $this->__toString(),
945                         htmlentities($eval)
946                 ));
947
948                 // Run the constructed command. This will "compile" all variables in
949                 eval($eval);
950         }
951
952         /**
953          * Compile all required templates into the current loaded one
954          *
955          * @return      void
956          * @throws      UnexpectedTemplateTypeException If the template type is
957          *                                                                              not "code"
958          * @throws      InvalidArrayCountException              If an unexpected array
959          *                                                                              count has been found
960          */
961         public final function compileTemplate () {
962                 // We will only work with template type "code" from configuration
963                 if ($this->getTemplateType() != $this->getConfigInstance()->readConfig("code_template_type")) {
964                         // Abort here
965                         throw new UnexpectedTemplateTypeException(array($this, $this->getTemplateType(), $this->getConfigInstance()->readConfig("code_template_type")), self::EXCEPTION_TEMPLATE_TYPE_IS_UNEXPECTED);
966                 }
967
968                 // Get the raw data. Thanks to Flobee(R) for given me a hint using the
969                 // modifier "m" in regular expressions. I had implemented a regex here
970                 // like this: (\n|\r)
971                 $rawData = $this->getRawTemplateData();
972
973                 // Remove double spaces and trim leading/trailing spaces
974                 $rawData = trim(str_replace("  ", " ", $rawData));
975
976                 // Search for raw variables
977                 $this->extractVariablesFromRawData($rawData);
978
979                 // Search for code-tags which are {? ?}
980                 @preg_match_all($this->regExpCodeTags, $rawData, $templateMatches);
981
982                 // Analyze the matches array
983                 if ((is_array($templateMatches)) && (count($templateMatches) == 4) && (count($templateMatches[0]) > 0)) {
984                         // Entries are found:
985                         //
986                         // The main analysis
987                         $this->analyzeTemplate($templateMatches);
988
989                         // Compile raw template data
990                         $this->compileRawTemplateData($templateMatches);
991
992                         // Are there some raw templates left for loading?
993                         $this->loadExtraRawTemplates();
994
995                         // Are some raw templates found and loaded?
996                         if (count($this->rawTemplates) > 0) {
997                                 die("NOT YET IMPLEMENTED");
998                         }
999                 } // END - if($templateMatches ...
1000         }
1001
1002         /**
1003          * Output the compiled page to the outside world. In case of web templates
1004          * this would be vaild (X)HTML code. And in case of email templates this
1005          * would store a prepared email body inside the template engine.
1006          *
1007          * @return      void
1008          */
1009         public final function output () {
1010                 // Check which type of template we have
1011                 switch ($this->getTemplateType()) {
1012                 case "html": // Raw HTML templates can be send to the output buffer
1013                         // Quick-N-Dirty:
1014                         $this->getWebOutputInstance()->output($this->getCompiledData());
1015                         break;
1016
1017                 default: // Unknown type found
1018                         if ((is_object($this->getDebugInstance())) && (method_exists($this->getDebugInstance(), 'output'))) {
1019                                 // Use debug output handler
1020                                 $this->getDebugInstance()->output(sprintf("[%s:] Unbekannter Template-Typ <strong>%s</strong> erkannt.",
1021                                         $this->__toString(),
1022                                         $this->getTemplateType()
1023                                 ));
1024                                 die();
1025                         } else {
1026                                 // Put directly out
1027                                 // DO NOT REWRITE THIS TO app_die() !!!
1028                                 die(sprintf("[%s:] Unbekannter Template-Typ <strong>%s</strong> erkannt.",
1029                                         $this->__toString(),
1030                                         $this->getTemplateType()
1031                                 ));
1032                         }
1033                         break;
1034                 }
1035         }
1036 }
1037
1038 // [EOF]
1039 ?>