Copyright updated
[core.git] / inc / classes / main / template / class_BaseTemplateEngine.php
1 <?php
2 /**
3  * A generic template engine
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Core Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  */
24 class BaseTemplateEngine extends BaseFrameworkSystem {
25         /**
26          * The local path name where all templates and sub folders for special
27          * templates are stored. We will internally determine the language plus
28          * "html" for web templates or "emails" for email templates
29          */
30         private $templateBasePath = '';
31
32         /**
33          * Template type
34          */
35         private $templateType = 'html';
36
37         /**
38          * The extension for web and email templates (not compiled templates)
39          */
40         private $templateExtension = '.tpl';
41
42         /**
43          * The extension for code templates (not compiled templates)
44          */
45         private $codeExtension = '.ctp';
46
47         /**
48          * Path relative to $templateBasePath and language code for compiled code-templates
49          */
50         private $compileOutputPath = 'templates/_compiled/';
51
52         /**
53          * The path name for all templates
54          */
55         private $genericBasePath = 'templates/';
56
57         /**
58          * The raw (maybe uncompiled) template
59          */
60         private $rawTemplateData = '';
61
62         /**
63          * Template data with compiled-in variables
64          */
65         private $compiledData = '';
66
67         /**
68          * The last loaded template's FQFN for debugging the engine
69          */
70         private $lastTemplate = '';
71
72         /**
73          * The variable stack for the templates
74          */
75         private $varStack = array();
76
77         /**
78          * Loaded templates for recursive protection and detection
79          */
80         private $loadedTemplates = array();
81
82         /**
83          * Compiled templates for recursive protection and detection
84          */
85         private $compiledTemplates = array();
86
87         /**
88          * Loaded raw template data
89          */
90         private $loadedRawData = NULL;
91
92         /**
93          * Raw templates which are linked in code templates
94          */
95         private $rawTemplates = NULL;
96
97         /**
98          * A regular expression for variable=value pairs
99          */
100         private $regExpVarValue = '/([\w_]+)(="([^"]*)"|=([\w_]+))?/';
101
102         /**
103          * A regular expression for filtering out code tags
104          *
105          * E.g.: {?template:variable=value;var2=value2;[...]?}
106          */
107         private $regExpCodeTags = '/\{\?([a-z_]+)(:("[^"]+"|[^?}]+)+)?\?\}/';
108
109         /**
110          * Loaded helpers
111          */
112         private $helpers = array();
113
114         /**
115          * Current variable group
116          */
117         private $currGroup = 'general';
118
119         /**
120          * All template groups except "general"
121          */
122         private $varGroups = array();
123
124         /**
125          * Code begin
126          */
127         private $codeBegin = '<?php';
128
129         /**
130          * Code end
131          */
132         private $codeEnd = '?>';
133
134         /**
135          * Language support is enabled by default
136          */
137         private $languageSupport = true;
138
139         /**
140          * XML compacting is disabled by default
141          */
142         private $xmlCompacting = false;
143
144         // Exception codes for the template engine
145         const EXCEPTION_TEMPLATE_TYPE_IS_UNEXPECTED   = 0x110;
146         const EXCEPTION_TEMPLATE_CONTAINS_INVALID_VAR = 0x111;
147         const EXCEPTION_INVALID_VIEW_HELPER           = 0x112;
148         const EXCEPTION_VARIABLE_IS_MISSING           = 0x113;
149
150         /**
151          * Protected constructor
152          *
153          * @param       $className      Name of the class
154          * @return      void
155          */
156         protected function __construct ($className) {
157                 // Call parent constructor
158                 parent::__construct($className);
159
160                 // Init file I/O instance
161                 $ioInstance = ObjectFactory::createObjectByConfiguredName('file_io_class');
162
163                 // Set it
164                 $this->setFileIoInstance($ioInstance);
165         }
166
167         /**
168          * Search for a variable in the stack
169          *
170          * @param       $var    The variable we are looking for
171          * @param       $stack  Optional variable stack to look in
172          * @return      $index  FALSE means not found, >=0 means found on a specific index
173          */
174         private function getVariableIndex ($var, $stack = NULL) {
175                 // First everything is not found
176                 $found = false;
177
178                 // If the stack is null, use the current group
179                 if (is_null($stack)) {
180                         // Use current group
181                         //* DEBUG: */ print __METHOD__.' currGroup=' . $this->currGroup . ' set as stack!<br />' . chr(10);
182                         $stack = $this->currGroup;
183                 } // END - if
184
185                 // Is the group there?
186                 if ($this->isVarStackSet($stack)) {
187                         // Now search for it
188                         foreach ($this->getVarStack($stack) as $index => $currEntry) {
189                                 //* DEBUG: */ print __METHOD__.":currGroup={$stack},idx={$index},currEntry={$currEntry['name']},var={$var}<br />\n";
190                                 // Is the entry found?
191                                 if ($currEntry['name'] == $var) {
192                                         // Found!
193                                         //* DEBUG: */ print __METHOD__.":FOUND!<br />\n";
194                                         $found = $index;
195                                         break;
196                                 } // END - if
197                         } // END - foreach
198                 } // END - if
199
200                 // Return the current position
201                 return $found;
202         }
203
204         /**
205          * Checks whether the given variable stack is set
206          *
207          * @param       $stack  Variable stack to check
208          * @return      $isSet  Whether the given variable stack is set
209          */
210         protected final function isVarStackSet ($stack) {
211                 // Check it
212                 $isSet = isset($this->varStack[$stack]);
213
214                 // Return result
215                 return $isSet;
216         }
217
218         /**
219          * Getter for given variable stack
220          *
221          * @param       $stack          Variable stack to check
222          * @return      $varStack       Found variable stack
223          */
224         public final function getVarStack ($stack) {
225                 return $this->varStack[$stack];
226         }
227
228         /**
229          * Setter for given variable stack
230          *
231          * @param       $stack          Variable stack to check
232          * @param       $varStack       Variable stack to check
233          * @return      void
234          */
235         protected final function setVarStack ($stack, array $varStack) {
236                 $this->varStack[$stack]  = $varStack;
237         }
238
239         /**
240          * Return a content of a variable or null if not found
241          *
242          * @param       $var            The variable we are looking for
243          * @param       $stack          Optional variable stack to look in
244          * @return      $content        Content of the variable or null if not found
245          */
246         protected function readVariable ($var, $stack = NULL) {
247                 // First everything is not found
248                 $content = NULL;
249
250                 // If the stack is null, use the current group
251                 if (is_null($stack)) {
252                         // Use current group
253                         //* DEBUG: */ print __METHOD__.' currGroup=' . $this->currGroup . ' set as stack!<br />' . chr(10);
254                         $stack = $this->currGroup;
255                 } // END - if
256
257                 // Get variable index
258                 $found = $this->getVariableIndex($var, $stack);
259
260                 // Is the variable found?
261                 if ($found !== false) {
262                         // Read it
263                         $content = $this->getVariableValue($stack, $found);
264                 } // END - if
265
266                 // Return the current position
267                 //* DEBUG: */ print __METHOD__.': group='.$stack.',var='.$var.', content['.gettype($content).']='.$content.'<br />' . chr(10);
268                 return $content;
269         }
270
271         /**
272          * Add a variable to the stack
273          *
274          * @param       $var    The variable we are looking for
275          * @param       $value  The value we want to store in the variable
276          * @return      void
277          */
278         private function addVariable ($var, $value) {
279                 // Set general variable group
280                 $this->setVariableGroup('general');
281
282                 // Add it to the stack
283                 $this->addGroupVariable($var, $value);
284         }
285
286         /**
287          * Returns all variables of current group or empty array
288          *
289          * @return      $result         Whether array of found variables or empty array
290          */
291         private function readCurrentGroup () {
292                 // Default is not found
293                 $result = array();
294
295                 // Is the group there?
296                 if ($this->isVarStackSet($this->currGroup)) {
297                         // Then use it
298                         $result = $this->getVarStack($this->currGroup);
299                 } // END - if
300
301                 // Return result
302                 return $result;
303         }
304
305         /**
306          * Settter for variable group
307          *
308          * @param       $groupName      Name of variable group
309          * @param       $add            Whether add this group
310          * @return      void
311          */
312         public function setVariableGroup ($groupName, $add = true) {
313                 // Set group name
314                 //* DEBIG: */ print __METHOD__.": currGroup=".$groupName."<br />\n";
315                 $this->currGroup = $groupName;
316
317                 // Skip group 'general'
318                 if (($groupName != 'general') && ($add === true)) {
319                         $this->varGroups[$groupName] = 'OK';
320                 } // END - if
321         }
322
323
324         /**
325          * Adds a variable to current group
326          *
327          * @param       $var    Variable to set
328          * @param       $value  Value to store in variable
329          * @return      void
330          */
331         public function addGroupVariable ($var, $value) {
332                 //* DEBUG: */ print __METHOD__.": group=".$this->currGroup.", var=".$var.", value=".$value."<br />\n";
333
334                 // Get current variables in group
335                 $currVars = $this->readCurrentGroup();
336
337                 // Append our variable
338                 $currVars[] = $this->generateVariableArray($var, $value);
339
340                 // Add it to the stack
341                 $this->setVarStack($this->currGroup, $currVars);
342         }
343
344         /**
345          * Getter for variable value, throws a NoVariableException if the variable is not found
346          *
347          * @param       $varGroup       Variable group to use
348          * @param       $index          Index in variable array
349          * @return      $value          Value to set
350          */
351         private function getVariableValue ($varGroup, $index) {
352                 // Return it
353                 return $this->varStack[$varGroup][$index]['value'];
354         }
355
356         /**
357          * Modify an entry on the stack
358          *
359          * @param       $var    The variable we are looking for
360          * @param       $value  The value we want to store in the variable
361          * @return      void
362          * @throws      NoVariableException     If the given variable is not found
363          */
364         private function modifyVariable ($var, $value) {
365                 // Get index for variable
366                 $index = $this->getVariableIndex($var);
367
368                 // Is the variable set?
369                 if ($index === false) {
370                         // Unset variables cannot be modified
371                         throw new NoVariableException(array($this, $var, $value), self::EXCEPTION_VARIABLE_IS_MISSING);
372                 } // END - if
373
374                 // Then modify it
375                 $this->setVariableValue($this->currGroup, $index, $value);
376         }
377
378         /**
379          * Sets a variable value for given variable group and index
380          *
381          * @param       $varGroup       Variable group to use
382          * @param       $index          Index in variable array
383          * @param       $value          Value to set
384          * @return      void
385          */
386         private function setVariableValue ($varGroup, $index, $value) {
387                 $this->varStack[$varGroup][$index]['value'] = $value;
388         }
389
390         /**
391          * Sets a variable within given group. This method does detect if the
392          * variable is already set. If so, the variable got modified, otherwise
393          * added.
394          *
395          * @param       $varGroup       Variable group to use
396          * @param       $var            Variable to set
397          * @param       $value          Value to set
398          * @return      void
399          */
400         protected function setVariable ($varGroup, $var, $value) {
401                 // Get index for variable
402                 $index = $this->getVariableIndex($var);
403
404                 // Is the variable set?
405                 if ($index === false) {
406                         // Not found, add it
407                         $this->varStack[$varGroup][] = $this->generateVariableArray($var, $value);
408                 } else {
409                         // Then modify it
410                         $this->setVariableValue($this->currGroup, $index, $value);
411                 }
412         }
413
414         /**
415          * "Generates" (better returns) an array for all variables for given
416          * variable/value pay.
417          *
418          * @param       $var            Variable to set
419          * @param       $value          Value to set
420          * @return      $varData        Variable data array
421          */
422         private function generateVariableArray ($var, $value) {
423                 // Generate the temporary array
424                 $varData = array(
425                         'name'  => $var,
426                         'value' => $value
427                 );
428
429                 // And return it
430                 return $varData;
431         }
432
433         /**
434          * Setter for template type. Only 'html', 'emails' and 'compiled' should
435          * be sent here
436          *
437          * @param       $templateType   The current template's type
438          * @return      void
439          */
440         protected final function setTemplateType ($templateType) {
441                 $this->templateType = (string) $templateType;
442         }
443
444         /**
445          * Setter for the last loaded template's FQFN
446          *
447          * @param       $template       The last loaded template
448          * @return      void
449          */
450         private final function setLastTemplate ($template) {
451                 $this->lastTemplate = (string) $template;
452         }
453
454         /**
455          * Getter for the last loaded template's FQFN
456          *
457          * @return      $template       The last loaded template
458          */
459         private final function getLastTemplate () {
460                 return $this->lastTemplate;
461         }
462
463         /**
464          * Setter for base path
465          *
466          * @param               $templateBasePath               The relative base path for all templates
467          * @return      void
468          */
469         public final function setTemplateBasePath ($templateBasePath) {
470                 // And set it
471                 $this->templateBasePath = (string) $templateBasePath;
472         }
473
474         /**
475          * Getter for base path
476          *
477          * @return      $templateBasePath               The relative base path for all templates
478          */
479         public final function getTemplateBasePath () {
480                 // And set it
481                 return $this->templateBasePath;
482         }
483
484         /**
485          * Getter for generic base path
486          *
487          * @return      $templateBasePath               The relative base path for all templates
488          */
489         public final function getGenericBasePath () {
490                 // And set it
491                 return $this->genericBasePath;
492         }
493
494         /**
495          * Setter for template extension
496          *
497          * @param               $templateExtension      The file extension for all uncompiled
498          *                                                      templates
499          * @return      void
500          */
501         public final function setRawTemplateExtension ($templateExtension) {
502                 // And set it
503                 $this->templateExtension = (string) $templateExtension;
504         }
505
506         /**
507          * Setter for code template extension
508          *
509          * @param               $codeExtension          The file extension for all uncompiled
510          *                                                      templates
511          * @return      void
512          */
513         public final function setCodeTemplateExtension ($codeExtension) {
514                 // And set it
515                 $this->codeExtension = (string) $codeExtension;
516         }
517
518         /**
519          * Getter for template extension
520          *
521          * @return      $templateExtension      The file extension for all uncompiled
522          *                                                      templates
523          */
524         public final function getRawTemplateExtension () {
525                 // And set it
526                 return $this->templateExtension;
527         }
528
529         /**
530          * Getter for code-template extension
531          *
532          * @return      $codeExtension          The file extension for all code-
533          *                                                      templates
534          */
535         public final function getCodeTemplateExtension () {
536                 // And set it
537                 return $this->codeExtension;
538         }
539
540         /**
541          * Setter for path of compiled templates
542          *
543          * @param       $compileOutputPath      The local base path for all compiled
544          *                                                              templates
545          * @return      void
546          */
547         public final function setCompileOutputPath ($compileOutputPath) {
548                 // And set it
549                 $this->compileOutputPath = (string) $compileOutputPath;
550         }
551
552         /**
553          * Getter for template type
554          *
555          * @return      $templateType   The current template's type
556          */
557         public final function getTemplateType () {
558                 return $this->templateType;
559         }
560
561         /**
562          * Assign (add) a given variable with a value
563          *
564          * @param       $var    The variable we are looking for
565          * @param       $value  The value we want to store in the variable
566          * @return      void
567          * @throws      EmptyVariableException  If the variable name is left empty
568          */
569         public final function assignVariable ($var, $value) {
570                 // Trim spaces of variable name
571                 $var = trim($var);
572
573                 // Empty variable found?
574                 if (empty($var)) {
575                         // Throw an exception
576                         throw new EmptyVariableException(array($this, 'var'), self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
577                 } // END - if
578
579                 // First search for the variable if it was already added
580                 $index = $this->getVariableIndex($var);
581
582                 // Was it found?
583                 if ($index === false) {
584                         // Add it to the stack
585                         //* DEBUG: */ echo "ADD: ".$var."<br />\n";
586                         $this->addVariable($var, $value);
587                 } elseif (!empty($value)) {
588                         // Modify the stack entry
589                         //* DEBUG: */ echo "MOD: ".$var."<br />\n";
590                         $this->modifyVariable($var, $value);
591                 }
592         }
593
594         /**
595          * Removes a given variable
596          *
597          * @param       $var    The variable we are looking for
598          * @return      void
599          */
600         public final function removeVariable ($var) {
601                 // First search for the variable if it was already added
602                 $index = $this->getVariableIndex($var);
603
604                 // Was it found?
605                 if ($index !== false) {
606                         // Remove this variable
607                         $this->unsetVariableStackOffset($index);
608                 } // END - if
609         }
610
611         /**
612          * Unsets the given offset in the variable stack
613          *
614          * @param       $index  Index to unset
615          * @return      void
616          */
617         protected final function unsetVariableStackOffset ($index) {
618                 // Is the entry there?
619                 if (!isset($this->varStack[$this->currGroup][$index])) {
620                         // Abort here, we need fixing!
621                         $this->debugInstance();
622                 } // END - if
623
624                 // Remove it
625                 unset($this->varStack[$this->currGroup][$index]);
626         }
627
628         /**
629          * Private setter for raw template data
630          *
631          * @param       $rawTemplateData        The raw data from the template
632          * @return      void
633          */
634         protected final function setRawTemplateData ($rawTemplateData) {
635                 // And store it in this class
636                 //* DEBUG: */ print __METHOD__.': '.strlen($rawTemplateData).' Bytes set.' . chr(10);
637                 //* DEBUG: */ print $this->currGroup.' variables: '.count($this->getVarStack($this->currGroup)).', groups='.count($this->varStack).'' . chr(10);
638                 $this->rawTemplateData = (string) $rawTemplateData;
639         }
640
641         /**
642          * Getter for raw template data
643          *
644          * @return      $rawTemplateData        The raw data from the template
645          */
646         public final function getRawTemplateData () {
647                 //* DEBUG: */ print __METHOD__.': '.strlen($this->rawTemplateData).' Bytes read.' . chr(10);
648                 return $this->rawTemplateData;
649         }
650
651         /**
652          * Private setter for compiled templates
653          *
654          * @return      void
655          */
656         private final function setCompiledData ($compiledData) {
657                 // And store it in this class
658                 //* DEBUG: */ print __METHOD__.': '.strlen($compiledData).' Bytes set.' . chr(10);
659                 $this->compiledData = (string) $compiledData;
660         }
661
662         /**
663          * Getter for compiled templates
664          *
665          * @return      $compiledData   Compiled template data
666          */
667         public final function getCompiledData () {
668                 //* DEBUG: */ print __METHOD__.': '.strlen($this->compiledData).' Bytes read.' . chr(10);
669                 return $this->compiledData;
670         }
671
672         /**
673          * Private loader for all template types
674          *
675          * @param       $template       The template we shall load
676          * @param       $extOther       An other extension to use
677          * @return      void
678          * @throws      FileIoException If the template was not found
679          */
680         protected function loadTemplate ($template, $extOther = '') {
681                 // Get extension for the template if empty
682                 if (empty($extOther)) {
683                         // None provided, so get the raw one
684                         $ext = $this->getRawTemplateExtension();
685                 } else {
686                         // Then use it!
687                         $ext = (string) $extOther;
688                 }
689
690                 // Is language support enabled?
691                 if ($this->isLanguageSupportEnabled()) {
692                         // Construct the FQFN for the template by honoring the current language
693                         $fqfn = sprintf("%s%s%s%s/%s/%s%s",
694                                 $this->getConfigInstance()->getConfigEntry('base_path'),
695                                 $this->getTemplateBasePath(),
696                                 $this->getGenericBasePath(),
697                                 $this->getLanguageInstance()->getLanguageCode(),
698                                 $this->getTemplateType(),
699                                 (string) $template,
700                                 $ext
701                         );
702                 } else {
703                         // Construct the FQFN for the template without language
704                         $fqfn = sprintf("%s%s%s%s/%s%s",
705                                 $this->getConfigInstance()->getConfigEntry('base_path'),
706                                 $this->getTemplateBasePath(),
707                                 $this->getGenericBasePath(),
708                                 $this->getTemplateType(),
709                                 (string) $template,
710                                 $ext
711                         );
712                 }
713
714                 // First try this
715                 try {
716                         // Load the raw template data
717                         $this->loadRawTemplateData($fqfn);
718                 } catch (FileIoException $e) {
719                         // If we shall load a code-template we need to switch the file extension
720                         if (($this->getTemplateType() != $this->getConfigInstance()->getConfigEntry('web_template_type')) && (empty($extOther))) {
721                                 // Switch over to the code-template extension and try it again
722                                 $ext = $this->getCodeTemplateExtension();
723
724                                 // Try it again...
725                                 $this->loadTemplate($template, $ext);
726                         } else {
727                                 // Throw it again
728                                 throw new FileIoException($fqfn, FrameworkFileInputPointer::EXCEPTION_FILE_NOT_FOUND);
729                         }
730                 }
731
732         }
733
734         /**
735          * A private loader for raw template names
736          *
737          * @param       $fqfn   The full-qualified file name for a template
738          * @return      void
739          */
740         private function loadRawTemplateData ($fqfn) {
741                 // Get a input/output instance from the middleware
742                 $ioInstance = $this->getFileIoInstance();
743
744                 // Some debug code to look on the file which is being loaded
745                 //* DEBUG: */ print __METHOD__.": FQFN=".$fqfn."<br />\n";
746
747                 // Load the raw template
748                 $rawTemplateData = $ioInstance->loadFileContents($fqfn);
749
750                 // Store the template's contents into this class
751                 $this->setRawTemplateData($rawTemplateData);
752
753                 // Remember the template's FQFN
754                 $this->setLastTemplate($fqfn);
755         }
756
757         /**
758          * Try to assign an extracted template variable as a "content" or 'config'
759          * variable.
760          *
761          * @param       $varName        The variable's name (shall be content orconfig) by
762          *                                              default
763          * @param       $var            The variable we want to assign
764          */
765         private function assignTemplateVariable ($varName, $var) {
766                 // Is it not a config variable?
767                 if ($varName != 'config') {
768                         // Regular template variables
769                         $this->assignVariable($var, '');
770                 } else {
771                         // Configuration variables
772                         $this->assignConfigVariable($var);
773                 }
774         }
775
776         /**
777          * Extract variables from a given raw data stream
778          *
779          * @param       $rawData        The raw template data we shall analyze
780          * @return      void
781          */
782         private function extractVariablesFromRawData ($rawData) {
783                 // Cast to string
784                 $rawData = (string) $rawData;
785
786                 // Search for variables
787                 @preg_match_all('/\$(\w+)(\[(\w+)\])?/', $rawData, $variableMatches);
788
789                 // Did we find some variables?
790                 if ((is_array($variableMatches)) && (count($variableMatches) == 4) && (count($variableMatches[0]) > 0)) {
791                         // Initialize all missing variables
792                         foreach ($variableMatches[3] as $key => $var) {
793                                 // Variable name
794                                 $varName = $variableMatches[1][$key];
795
796                                 // Workarround: Do not assign empty variables
797                                 if (!empty($var)) {
798                                         // Try to assign it, empty strings are being ignored
799                                         $this->assignTemplateVariable($varName, $var);
800                                 } // END - if
801                         } // END - foreach
802                 } // END - if
803         }
804
805         /**
806          * Main analysis of the loaded template
807          *
808          * @param       $templateMatches        Found template place-holders, see below
809          * @return      void
810          *
811          *---------------------------------
812          * Structure of $templateMatches:
813          *---------------------------------
814          * [0] => Array - An array with all full matches
815          * [1] => Array - An array with left part (before the ':') of a match
816          * [2] => Array - An array with right part of a match including ':'
817          * [3] => Array - An array with right part of a match excluding ':'
818          */
819         private function analyzeTemplate (array $templateMatches) {
820                 // Backup raw template data
821                 $backup = $this->getRawTemplateData();
822
823                 // Initialize some arrays
824                 if (is_null($this->loadedRawData)) {
825                         // Initialize both
826                         $this->loadedRawData = array();
827                         $this->rawTemplates = array();
828                 } // END - if
829
830                 // Load all requested templates
831                 foreach ($templateMatches[1] as $template) {
832
833                         // Load and compile only templates which we have not yet loaded
834                         // RECURSIVE PROTECTION! BE CAREFUL HERE!
835                         if ((!isset($this->loadedRawData[$template])) && (!in_array($template, $this->loadedTemplates))) {
836
837                                 // Template not found, but maybe variable assigned?
838                                 //* DEBUG: */ print __METHOD__.":template={$template}<br />\n";
839                                 if ($this->getVariableIndex($template, 'config') !== false) {
840                                         // Use that content here
841                                         $this->loadedRawData[$template] = $this->readVariable($template, 'config');
842
843                                         // Recursive protection:
844                                         $this->loadedTemplates[] = $template;
845                                 } elseif ($this->getVariableIndex($template) !== false) {
846                                         // Use that content here
847                                         $this->loadedRawData[$template] = $this->readVariable($template);
848
849                                         // Recursive protection:
850                                         $this->loadedTemplates[] = $template;
851                                 } else {
852                                         // Then try to search for code-templates
853                                         try {
854                                                 // Load the code template and remember it's contents
855                                                 $this->loadCodeTemplate($template);
856                                                 $this->loadedRawData[$template] = $this->getRawTemplateData();
857
858                                                 // Remember this template for recursion detection
859                                                 // RECURSIVE PROTECTION!
860                                                 $this->loadedTemplates[] = $template;
861                                         } catch (FileIoException $e) {
862                                                 // Even this is not done... :/
863                                                 $this->rawTemplates[] = $template;
864                                         }
865                                 }
866                         } // END - if
867                 } // END - foreach
868
869                 // Restore the raw template data
870                 $this->setRawTemplateData($backup);
871         }
872
873         /**
874          * Compile a given raw template code and remember it for later usage
875          *
876          * @param       $code           The raw template code
877          * @param       $template       The template's name
878          * @return      void
879          */
880         private function compileCode ($code, $template) {
881                 // Is this template already compiled?
882                 if (in_array($template, $this->compiledTemplates)) {
883                         // Abort here...
884                         return;
885                 } // END - if
886
887                 // Remember this template being compiled
888                 $this->compiledTemplates[] = $template;
889
890                 // Compile the loaded code in five steps:
891                 //
892                 // 1. Backup current template data
893                 $backup = $this->getRawTemplateData();
894
895                 // 2. Set the current template's raw data as the new content
896                 $this->setRawTemplateData($code);
897
898                 // 3. Compile the template data
899                 $this->compileTemplate();
900
901                 // 4. Remember it's contents
902                 $this->loadedRawData[$template] = $this->getRawTemplateData();
903
904                 // 5. Restore the previous raw content from backup variable
905                 $this->setRawTemplateData($backup);
906         }
907
908         /**
909          * Insert all given and loaded templates by running through all loaded
910          * codes and searching for their place-holder in the main template
911          *
912          * @param       $templateMatches        See method analyzeTemplate()
913          * @return      void
914          */
915         private function insertAllTemplates (array $templateMatches) {
916                 // Run through all loaded codes
917                 foreach ($this->loadedRawData as $template => $code) {
918
919                         // Search for the template
920                         $foundIndex = array_search($template, $templateMatches[1]);
921
922                         // Lookup the matching template replacement
923                         if (($foundIndex !== false) && (isset($templateMatches[0][$foundIndex]))) {
924
925                                 // Get the current raw template
926                                 $rawData = $this->getRawTemplateData();
927
928                                 // Replace the space holder with the template code
929                                 $rawData = str_replace($templateMatches[0][$foundIndex], $code, $rawData);
930
931                                 // Set the new raw data
932                                 $this->setRawTemplateData($rawData);
933                         } // END - if
934                 } // END - foreach
935         }
936
937         /**
938          * Load all extra raw templates
939          *
940          * @return      void
941          */
942         private function loadExtraRawTemplates () {
943                 // Are there some raw templates we need to load?
944                 if (count($this->rawTemplates) > 0) {
945                         // Try to load all raw templates
946                         foreach ($this->rawTemplates as $key => $template) {
947                                 try {
948                                         // Load the template
949                                         $this->loadWebTemplate($template);
950
951                                         // Remember it's contents
952                                         $this->rawTemplates[$template] = $this->getRawTemplateData();
953
954                                         // Remove it from the loader list
955                                         unset($this->rawTemplates[$key]);
956
957                                         // Remember this template for recursion detection
958                                         // RECURSIVE PROTECTION!
959                                         $this->loadedTemplates[] = $template;
960                                 } catch (FileIoException $e) {
961                                         // This template was never found. We silently ignore it
962                                         unset($this->rawTemplates[$key]);
963                                 }
964                         } // END - foreach
965                 } // END - if
966         }
967
968         /**
969          * Assign all found template variables
970          *
971          * @param       $varMatches             An array full of variable/value pairs.
972          * @return      void
973          * @todo        Unfinished work or don't die here.
974          */
975         private function assignAllVariables (array $varMatches) {
976                 // Search for all variables
977                 foreach ($varMatches[1] as $key => $var) {
978
979                         // Detect leading equals
980                         if (substr($varMatches[2][$key], 0, 1) == '=') {
981                                 // Remove and cast it
982                                 $varMatches[2][$key] = (string) substr($varMatches[2][$key], 1);
983                         } // END - if
984
985                         // Do we have some quotes left and right side? Then it is free text
986                         if ((substr($varMatches[2][$key], 0, 1) == "\"") && (substr($varMatches[2][$key], -1, 1) == "\"")) {
987                                 // Free string detected! Which we can assign directly
988                                 $this->assignVariable($var, $varMatches[3][$key]);
989                         } elseif (!empty($varMatches[2][$key])) {
990                                 // @TODO Non-string found so we need some deeper analysis...
991                                 ApplicationEntryPoint::app_die('Deeper analysis not yet implemented!');
992                         }
993
994                 } // for ($varMatches ...
995         }
996
997         /**
998          * Compiles all loaded raw templates
999          *
1000          * @param       $templateMatches        See method analyzeTemplate() for details
1001          * @return      void
1002          */
1003         private function compileRawTemplateData (array $templateMatches) {
1004                 // Are some code-templates found which we need to compile?
1005                 if (count($this->loadedRawData) > 0) {
1006
1007                         // Then compile all!
1008                         foreach ($this->loadedRawData as $template => $code) {
1009
1010                                 // Is this template already compiled?
1011                                 if (in_array($template, $this->compiledTemplates)) {
1012                                         // Then skip it
1013                                         continue;
1014                                 } // END - if
1015
1016                                 // Search for the template
1017                                 $foundIndex = array_search($template, $templateMatches[1]);
1018
1019                                 // Lookup the matching variable data
1020                                 if (($foundIndex !== false) && (isset($templateMatches[3][$foundIndex]))) {
1021
1022                                         // Split it up with another reg. exp. into variable=value pairs
1023                                         preg_match_all($this->regExpVarValue, $templateMatches[3][$foundIndex], $varMatches);
1024
1025                                         // Assign all variables
1026                                         $this->assignAllVariables($varMatches);
1027
1028                                 } // END - if (isset($templateMatches ...
1029
1030                                 // Compile the loaded template
1031                                 $this->compileCode($code, $template);
1032
1033                         } // END - foreach ($this->loadedRawData ...
1034
1035                         // Insert all templates
1036                         $this->insertAllTemplates($templateMatches);
1037
1038                 } // END - if (count($this->loadedRawData) ...
1039         }
1040
1041         /**
1042          * Inserts all raw templates into their respective variables
1043          *
1044          * @return      void
1045          */
1046         private function insertRawTemplates () {
1047                 // Load all templates
1048                 foreach ($this->rawTemplates as $template => $content) {
1049                         // Set the template as a variable with the content
1050                         $this->assignVariable($template, $content);
1051                 }
1052         }
1053
1054         /**
1055          * Finalizes the compilation of all template variables
1056          *
1057          * @return      void
1058          */
1059         private function finalizeVariableCompilation () {
1060                 // Get the content
1061                 $content = $this->getRawTemplateData();
1062                 //* DEBUG: */ print __METHOD__.': content before='.strlen($content).' ('.md5($content).')' . chr(10);
1063
1064                 // Do we have the stack?
1065                 if (!$this->isVarStackSet('general')) {
1066                         // Abort here silently
1067                         // @TODO This silent abort should be logged, maybe.
1068                         return;
1069                 } // END - if
1070
1071                 // Walk through all variables
1072                 foreach ($this->getVarStack('general') as $currEntry) {
1073                         //* DEBUG: */ print __METHOD__.': name='.$currEntry['name'].', value=<pre>'.htmlentities($currEntry['value']).'</pre>\n';
1074                         // Replace all [$var] or {?$var?} with the content
1075                         // @TODO Old behaviour, will become obsolete!
1076                         $content = str_replace('$content[' . $currEntry['name'] . ']', $currEntry['value'], $content);
1077
1078                         // @TODO Yet another old way
1079                         $content = str_replace('[' . $currEntry['name'] . ']', $currEntry['value'], $content);
1080
1081                         // The new behaviour
1082                         $content = str_replace('{?' . $currEntry['name'] . '?}', $currEntry['value'], $content);
1083                 } // END - for
1084
1085                 //* DEBUG: */ print __METHOD__.': content after='.strlen($content).' ('.md5($content).')' . chr(10);
1086
1087                 // Set the content back
1088                 $this->setRawTemplateData($content);
1089         }
1090
1091         /**
1092          * Load a specified web template into the engine
1093          *
1094          * @param       $template       The web template we shall load which is located in
1095          *                                              'html' by default
1096          * @return      void
1097          */
1098         public function loadWebTemplate ($template) {
1099                 // Set template type
1100                 $this->setTemplateType($this->getConfigInstance()->getConfigEntry('web_template_type'));
1101
1102                 // Load the special template
1103                 $this->loadTemplate($template);
1104         }
1105
1106         /**
1107          * Assign a given congfiguration variable with a value
1108          *
1109          * @param       $var    The configuration variable we want to assign
1110          * @return      void
1111          */
1112         public function assignConfigVariable ($var) {
1113                 // Sweet and simple...
1114                 //* DEBUG: */ print __METHOD__.': var=' . $var . ',getConfigEntry()=' . $this->getConfigInstance()->getConfigEntry($var) . chr(10);
1115                 $this->setVariable('config', $var, $this->getConfigInstance()->getConfigEntry($var));
1116         }
1117
1118         /**
1119          * Load a specified code template into the engine
1120          *
1121          * @param       $template       The code template we shall load which is
1122          *                                              located in 'code' by default
1123          * @return      void
1124          */
1125         public function loadCodeTemplate ($template) {
1126                 // Set template type
1127                 $this->setTemplateType($this->getConfigInstance()->getConfigEntry('code_template_type'));
1128
1129                 // Load the special template
1130                 $this->loadTemplate($template);
1131         }
1132
1133         /**
1134          * Compiles configuration place-holders in all variables. This 'walks'
1135          * through the variable stack 'general'. It interprets all values from that
1136          * variables as configuration entries after compiling them.
1137          *
1138          * @return      void
1139          */
1140         public final function compileConfigInVariables () {
1141                 // Do we have the stack?
1142                 if (!$this->isVarStackSet('general')) {
1143                         // Abort here silently
1144                         // @TODO This silent abort should be logged, maybe.
1145                         return;
1146                 } // END - if
1147
1148                 // Iterate through all general variables
1149                 foreach ($this->getVarStack('general') as $index => $currVariable) {
1150                         // Compile the value
1151                         $value = $this->compileRawCode($this->readVariable($currVariable['name']), true);
1152
1153                         // Remove it from stack
1154                         $this->removeVariable($index, 'general');
1155
1156                         // Re-assign the variable
1157                         //* DEBUG: */ print __METHOD__.': value='. $value . ',name=' . $currVariable['name'] . ',index=' . $index . chr(10);
1158                         $this->assignConfigVariable($value);
1159                 } // END - foreach
1160         }
1161
1162         /**
1163          * Compile all variables by inserting their respective values
1164          *
1165          * @return      void
1166          * @todo        Make this code some nicer...
1167          */
1168         public final function compileVariables () {
1169                 // Initialize the $content array
1170                 $validVar = $this->getConfigInstance()->getConfigEntry('tpl_valid_var');
1171                 $dummy = array();
1172
1173                 // Iterate through all general variables
1174                 foreach ($this->getVarStack('general') as $currVariable) {
1175                         // Transfer it's name/value combination to the $content array
1176                         //* DEBUG: */ echo $currVariable['name'].'=<pre>'.htmlentities($currVariable['value']).'</pre>\n';
1177                         $dummy[$currVariable['name']] = $currVariable['value'];
1178                 }// END - if
1179
1180                 // Set the new variable (don't remove the second dollar!)
1181                 $$validVar = $dummy;
1182
1183                 // Prepare all configuration variables
1184                 $config = NULL;
1185                 if ($this->isVarStackSet('config')) {
1186                         $config = $this->getVarStack('config');
1187                 } // END - if
1188
1189                 // Remove some variables
1190                 unset($index);
1191                 unset($currVariable);
1192
1193                 // Run the compilation three times to get content from helper classes in
1194                 $cnt = 0;
1195                 while ($cnt < 3) {
1196                         // Finalize the compilation of template variables
1197                         $this->finalizeVariableCompilation();
1198
1199                         // Prepare the eval() command for comiling the template
1200                         $eval = sprintf("\$result = \"%s\";",
1201                                 addslashes($this->getRawTemplateData())
1202                         );
1203
1204                         // This loop does remove the backslashes (\) in PHP parameters
1205                         while (strpos($eval, $this->codeBegin) !== false) {
1206                                 // Get left part before "<?"
1207                                 $evalLeft = substr($eval, 0, strpos($eval, $this->codeBegin));
1208
1209                                 // Get all from right of "<?"
1210                                 $evalRight = substr($eval, (strpos($eval, $this->codeBegin) + 5));
1211
1212                                 // Cut middle part out and remove escapes
1213                                 $evalMiddle = trim(substr($evalRight, 0, strpos($evalRight, $this->codeEnd)));
1214                                 $evalMiddle = stripslashes($evalMiddle);
1215
1216                                 // Remove the middle part from right one
1217                                 $evalRight = substr($evalRight, (strpos($evalRight, $this->codeEnd) + 2));
1218
1219                                 // And put all together
1220                                 $eval = sprintf("%s<%%php %s %%>%s", $evalLeft, $evalMiddle, $evalRight);
1221                         } // END - while
1222
1223                         // Prepare PHP code for eval() command
1224                         $eval = str_replace(
1225                                 "<%php", "\";",
1226                                 str_replace(
1227                                         "%>",
1228                                         "\n\$result .= \"",
1229                                         $eval
1230                                 )
1231                         );
1232
1233                         // Run the constructed command. This will "compile" all variables in
1234                         eval($eval);
1235
1236                         // Goes something wrong?
1237                         if ((!isset($result)) || (empty($result))) {
1238                                 // Output eval command
1239                                 $this->debugOutput(sprintf("Failed eval() code: <pre>%s</pre>", $this->markupCode($eval, true)), true);
1240
1241                                 // Output backtrace here
1242                                 $this->debugBackTrace();
1243                         } // END - if
1244
1245                         // Set raw template data
1246                         $this->setRawTemplateData($result);
1247                         $cnt++;
1248                 } // END - while
1249
1250                 // Final variable assignment
1251                 $this->finalizeVariableCompilation();
1252
1253                 // Set the new content
1254                 $this->setCompiledData($this->getRawTemplateData());
1255         }
1256
1257         /**
1258          * Compile all required templates into the current loaded one
1259          *
1260          * @return      void
1261          * @throws      UnexpectedTemplateTypeException If the template type is
1262          *                                                                                      not "code"
1263          * @throws      InvalidArrayCountException              If an unexpected array
1264          *                                                                                      count has been found
1265          */
1266         public function compileTemplate () {
1267                 // Get code type to make things shorter
1268                 $codeType = $this->getConfigInstance()->getConfigEntry('code_template_type');
1269
1270                 // We will only work with template type "code" from configuration
1271                 if (substr($this->getTemplateType(), 0, strlen($codeType)) != $codeType) {
1272                         // Abort here
1273                         throw new UnexpectedTemplateTypeException(array($this, $this->getTemplateType(), $this->getConfigInstance()->getConfigEntry('code_template_type')), self::EXCEPTION_TEMPLATE_TYPE_IS_UNEXPECTED);
1274                 } // END - if
1275
1276                 // Get the raw data.
1277                 $rawData = $this->getRawTemplateData();
1278
1279                 // Remove double spaces and trim leading/trailing spaces
1280                 $rawData = trim(str_replace('  ', ' ', $rawData));
1281
1282                 // Search for raw variables
1283                 $this->extractVariablesFromRawData($rawData);
1284
1285                 // Search for code-tags which are {? ?}
1286                 preg_match_all($this->regExpCodeTags, $rawData, $templateMatches);
1287
1288                 // Analyze the matches array
1289                 if ((is_array($templateMatches)) && (count($templateMatches) == 4) && (count($templateMatches[0]) > 0)) {
1290                         // Entries are found:
1291                         //
1292                         // The main analysis
1293                         $this->analyzeTemplate($templateMatches);
1294
1295                         // Compile raw template data
1296                         $this->compileRawTemplateData($templateMatches);
1297
1298                         // Are there some raw templates left for loading?
1299                         $this->loadExtraRawTemplates();
1300
1301                         // Are some raw templates found and loaded?
1302                         if (count($this->rawTemplates) > 0) {
1303
1304                                 // Insert all raw templates
1305                                 $this->insertRawTemplates();
1306
1307                                 // Remove the raw template content as well
1308                                 $this->setRawTemplateData('');
1309
1310                         } // END - if
1311
1312                 } // END - if($templateMatches ...
1313         }
1314
1315         /**
1316          * Loads a given view helper (by name)
1317          *
1318          * @param       $helperName             The helper's name
1319          * @return      void
1320          */
1321         protected function loadViewHelper ($helperName) {
1322                 // Make first character upper case, rest low
1323                 $helperName = $this->convertToClassName($helperName);
1324
1325                 // Is this view helper loaded?
1326                 if (!isset($this->helpers[$helperName])) {
1327                         // Create a class name
1328                         $className = $helperName . 'ViewHelper';
1329
1330                         // Generate new instance
1331                         $this->helpers[$helperName] = ObjectFactory::createObjectByName($className);
1332                 } // END - if
1333
1334                 // Return the requested instance
1335                 return $this->helpers[$helperName];
1336         }
1337
1338         /**
1339          * Assigns the last loaded raw template content with a given variable
1340          *
1341          * @param       $templateName   Name of the template we want to assign
1342          * @param       $variableName   Name of the variable we want to assign
1343          * @return      void
1344          */
1345         public function assignTemplateWithVariable ($templateName, $variableName) {
1346                 // Get the content from last loaded raw template
1347                 $content = $this->getRawTemplateData();
1348
1349                 // Assign the variable
1350                 $this->assignVariable($variableName, $content);
1351
1352                 // Purge raw content
1353                 $this->setRawTemplateData('');
1354         }
1355
1356         /**
1357          * Transfers the content of this template engine to a given response instance
1358          *
1359          * @param       $responseInstance       An instance of a response class
1360          * @return      void
1361          */
1362         public function transferToResponse (Responseable $responseInstance) {
1363                 // Get the content and set it in response class
1364                 $responseInstance->writeToBody($this->getCompiledData());
1365         }
1366
1367         /**
1368          * Assigns all the application data with template variables
1369          *
1370          * @param       $applicationInstance    A manageable application instance
1371          * @return      void
1372          */
1373         public function assignApplicationData (ManageableApplication $applicationInstance) {
1374                 // Get long name and assign it
1375                 $this->assignVariable('app_full_name' , $applicationInstance->getAppName());
1376
1377                 // Get short name and assign it
1378                 $this->assignVariable('app_short_name', $applicationInstance->getAppShortName());
1379
1380                 // Get version number and assign it
1381                 $this->assignVariable('app_version'   , $applicationInstance->getAppVersion());
1382
1383                 // Assign extra application-depending data
1384                 $applicationInstance->assignExtraTemplateData($this);
1385         }
1386
1387         /**
1388          * "Compiles" a variable by replacing {?var?} with it's content
1389          *
1390          * @param       $rawCode                        Raw code to compile
1391          * @param       $setMatchAsCode         Sets $match if readVariable() returns empty result
1392          * @return      $rawCode        Compile code with inserted variable value
1393          */
1394         public function compileRawCode ($rawCode, $setMatchAsCode=false) {
1395                 // Find the variables
1396                 //* DEBUG: */ print __METHOD__.":rawCode=<pre>".htmlentities($rawCode)."</pre>\n";
1397                 preg_match_all($this->regExpVarValue, $rawCode, $varMatches);
1398
1399                 // Compile all variables
1400                 //* DEBUG: */ print __METHOD__.":<pre>".print_r($varMatches, true)."</pre>\n";
1401                 foreach ($varMatches[0] as $match) {
1402                         // Add variable tags around it
1403                         $varCode = '{?' . $match . '?}';
1404
1405                         // Is the variable found in code? (safes some calls)
1406                         if (strpos($rawCode, $varCode) !== false) {
1407                                 // Replace the variable with it's value, if found
1408                                 $value = $this->readVariable($match);
1409                                 //* DEBUG: */ print __METHOD__.": match=".$match.",value[".gettype($value)."]=".$value."<br />\n";
1410                                 if (($setMatchAsCode === true) && (is_null($value))) {
1411                                         // Insert match
1412                                         $rawCode = str_replace($varCode, $match, $rawCode);
1413                                 } else {
1414                                         // Insert value
1415                                         $rawCode = str_replace($varCode, $value, $rawCode);
1416                                 }
1417                         } // END - if
1418                 } // END - foreach
1419
1420                 // Return the compiled data
1421                 //* DEBUG: */ print __METHOD__.":rawCode=<pre>".htmlentities($rawCode)."</pre>\n";
1422                 return $rawCode;
1423         }
1424
1425         /**
1426          * Getter for variable group array
1427          *
1428          * @return      $vargroups      All variable groups
1429          */
1430         public final function getVariableGroups () {
1431                 return $this->varGroups;
1432         }
1433
1434         /**
1435          * Renames a variable in code and in stack
1436          *
1437          * @param       $oldName        Old name of variable
1438          * @param       $newName        New name of variable
1439          * @return      void
1440          */
1441         public function renameVariable ($oldName, $newName) {
1442                 //* DEBUG: */ print __METHOD__.": oldName={$oldName}, newName={$newName}<br />\n";
1443                 // Get raw template code
1444                 $rawData = $this->getRawTemplateData();
1445
1446                 // Replace it
1447                 $rawData = str_replace($oldName, $newName, $rawData);
1448
1449                 // Set the code back
1450                 $this->setRawTemplateData($rawData);
1451         }
1452
1453         /**
1454          * Renders the given XML content
1455          *
1456          * @param       $content        Valid XML content or if not set the current loaded raw content
1457          * @return      void
1458          * @throws      XmlParserException      If an XML error was found
1459          */
1460         public function renderXmlContent ($content = NULL) {
1461                 // Is the content set?
1462                 if (is_null($content)) {
1463                         // Get current content
1464                         $content = $this->getRawTemplateData();
1465                 } // END - if
1466
1467                 // Get a XmlParser instance
1468                 $parserInstance = ObjectFactory::createObjectByConfiguredName('xml_parser_class', array($this));
1469
1470                 // Check if we have XML compacting enabled
1471                 if ($this->isXmlCompactingEnabled()) {
1472                         // Yes, so get a decorator class for transparent compacting
1473                         $parserInstance = ObjectFactory::createObjectByConfiguredName('deco_compacting_xml_parser_class', array($parserInstance));
1474                 } // END - if
1475
1476                 // Parse the XML document
1477                 $parserInstance->parseXmlContent($content);
1478         }
1479
1480         /**
1481          * Enables or disables language support
1482          *
1483          * @param       $languageSupport        New language support setting
1484          * @return      void
1485          */
1486         public final function enableLanguageSupport ($languageSupport = true) {
1487                 $this->languageSupport = (bool) $languageSupport;
1488         }
1489
1490         /**
1491          * Checks whether language support is enabled
1492          *
1493          * @return      $languageSupport        Whether language support is enabled or disabled
1494          */
1495         public final function isLanguageSupportEnabled () {
1496                 return $this->languageSupport;
1497         }
1498
1499         /**
1500          * Enables or disables XML compacting
1501          *
1502          * @param       $xmlCompacting  New XML compacting setting
1503          * @return      void
1504          */
1505         public final function enableXmlCompacting ($xmlCompacting = true) {
1506                 $this->xmlCompacting = (bool) $xmlCompacting;
1507         }
1508
1509         /**
1510          * Checks whether XML compacting is enabled
1511          *
1512          * @return      $xmlCompacting  Whether XML compacting is enabled or disabled
1513          */
1514         public final function isXmlCompactingEnabled () {
1515                 return $this->xmlCompacting;
1516         }
1517
1518         /**
1519          * Removes all commentd, tabs and new-line characters to compact the content
1520          *
1521          * @param       $uncompactedContent             The uncompacted content
1522          * @return      $compactedContent               The compacted content
1523          */
1524         public function compactContent ($uncompactedContent) {
1525                 // First, remove all tab/new-line/revert characters
1526                 $compactedContent = str_replace(chr(9), '', str_replace(chr(10), '', str_replace(chr(13), '', $uncompactedContent)));
1527
1528                 // Then regex all comments like <!-- //--> away
1529                 preg_match_all('/<!--[\w\W]*?(\/\/){0,1}-->/', $compactedContent, $matches);
1530
1531                 // Do we have entries?
1532                 if (isset($matches[0][0])) {
1533                         // Remove all
1534                         foreach ($matches[0] as $match) {
1535                                 // Remove the match
1536                                 $compactedContent = str_replace($match, '', $compactedContent);
1537                         } // END - foreach
1538                 } // END - if
1539
1540                 // Set the content again
1541                 $this->setRawTemplateData($compactedContent);
1542
1543                 // Return compacted content
1544                 return $compactedContent;
1545         }
1546 }
1547
1548 // [EOF]
1549 ?>