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