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