Code

Updated smarty
[gosa.git] / gosa-core / include / smarty / sysplugins / smarty_internal_templatecompilerbase.php
1 <?php\r
2 /**\r
3  * Smarty Internal Plugin Smarty Template Compiler Base\r
4  * \r
5  * This file contains the basic classes and methodes for compiling Smarty templates with lexer/parser\r
6  * \r
7  * @package Smarty\r
8  * @subpackage Compiler\r
9  * @author Uwe Tews \r
10  */\r
11 /**\r
12  * Main compiler class\r
13  */\r
14 class Smarty_Internal_TemplateCompilerBase {\r
15     // hash for nocache sections\r
16     private $nocache_hash = null; \r
17     // suppress generation of nocache code\r
18     public $suppressNocacheProcessing = false; \r
19     // compile tag objects\r
20     static $_tag_objects = array(); \r
21     // tag stack\r
22     public $_tag_stack = array(); \r
23     // current template\r
24     public $template = null;\r
25 \r
26     /**\r
27      * Initialize compiler\r
28      */\r
29     public function __construct()\r
30     {\r
31         $this->nocache_hash = str_replace('.', '-', uniqid(rand(), true));\r
32     } \r
33     // abstract function doCompile($_content);\r
34     /**\r
35      * Methode to compile a Smarty template\r
36      * \r
37      * @param  $template template object to compile\r
38      * @return bool true if compiling succeeded, false if it failed\r
39      */\r
40     public function compileTemplate($template)\r
41     {\r
42         if (empty($template->properties['nocache_hash'])) {\r
43             $template->properties['nocache_hash'] = $this->nocache_hash;\r
44         } else {\r
45             $this->nocache_hash = $template->properties['nocache_hash'];\r
46         } \r
47         /* here is where the compiling takes place. Smarty\r
48        tags in the templates are replaces with PHP code,\r
49        then written to compiled files. */\r
50         // flag for nochache sections\r
51         $this->nocache = false;\r
52         $this->tag_nocache = false; \r
53         // assume successfull compiling\r
54         $this->compile_error = false; \r
55         // save template object in compiler class\r
56         $this->template = $template;\r
57         $this->smarty->_current_file = $this->template->getTemplateFilepath(); \r
58         // template header code\r
59         $template_header = '';\r
60         if (!$template->suppressHeader) {\r
61             $template_header .= "<?php /* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") . "\n";\r
62             $template_header .= "         compiled from \"" . $this->template->getTemplateFilepath() . "\" */ ?>\n";\r
63         } \r
64 \r
65         do {\r
66             // flag for aborting current and start recompile\r
67             $this->abort_and_recompile = false; \r
68             // get template source\r
69             $_content = $template->getTemplateSource(); \r
70             // run prefilter if required\r
71             if (isset($this->smarty->autoload_filters['pre']) || isset($this->smarty->registered_filters['pre'])) {\r
72                 $_content = Smarty_Internal_Filter_Handler::runFilter('pre', $_content, $this->smarty);\r
73             } \r
74             // on empty template just return header\r
75             if ($_content == '') {\r
76                 if ($template->suppressFileDependency) {\r
77                     $template->compiled_template = '';\r
78                 } else {\r
79                     $template->compiled_template = $template_header . $template->createPropertyHeader();\r
80                 } \r
81                 return true;\r
82             } \r
83             // call compiler\r
84             $_compiled_code = $this->doCompile($_content);\r
85         } while ($this->abort_and_recompile);\r
86         if (!$this->compile_error) {\r
87             // return compiled code to template object\r
88             if ($template->suppressFileDependency) {\r
89                 $template->compiled_template = $_compiled_code;\r
90             } else {\r
91                 $template->compiled_template = $template_header . $template->createPropertyHeader() . $_compiled_code;\r
92             } \r
93             // run postfilter if required\r
94             if (isset($this->smarty->autoload_filters['post']) || isset($this->smarty->registered_filters['post'])) {\r
95                 $template->compiled_template = Smarty_Internal_Filter_Handler::runFilter('post', $template->compiled_template, $this->smarty);\r
96             } \r
97             return true;\r
98         } else {\r
99             // compilation error\r
100             return false;\r
101         } \r
102     } \r
103 \r
104     /**\r
105      * Compile Tag\r
106      * \r
107      * This is a call back from the lexer/parser\r
108      * It executes the required compile plugin for the Smarty tag\r
109      * \r
110      * @param string $tag tag name\r
111      * @param array $args array with tag attributes\r
112      * @return string compiled code\r
113      */\r
114     public function compileTag($tag, $args)\r
115     { \r
116         // $args contains the attributes parsed and compiled by the lexer/parser\r
117         // assume that tag does compile into code, but creates no HTML output\r
118         $this->has_code = true;\r
119         $this->has_output = false; \r
120         // compile the smarty tag (required compile classes to compile the tag are autoloaded)\r
121         if (($_output = $this->callTagCompiler($tag, $args)) === false) {\r
122             if (isset($this->smarty->template_functions[$tag])) {\r
123                 // template defined by {template} tag\r
124                 $args['name'] = "'" . $tag . "'";\r
125                 $_output = $this->callTagCompiler('call', $args);\r
126             } \r
127         } \r
128         if ($_output !== false) {\r
129             if ($_output !== true) {\r
130                 // did we get compiled code\r
131                 if ($this->has_code) {\r
132                     // Does it create output?\r
133                     if ($this->has_output) {\r
134                         $_output .= "\n";\r
135                     } \r
136                     // return compiled code\r
137                     return $_output;\r
138                 } \r
139             } \r
140             // tag did not produce compiled code\r
141             return '';\r
142         } else {\r
143             // not an internal compiler tag\r
144             if (strlen($tag) < 6 || substr_compare($tag, 'close', -5, 5) != 0) {\r
145                 // check if tag is a registered object\r
146                 if (isset($this->smarty->registered_objects[$tag]) && isset($args['object_methode'])) {\r
147                     $methode = $args['object_methode'];\r
148                     unset ($args['object_methode']);\r
149                     if (!in_array($methode, $this->smarty->registered_objects[$tag][3]) &&\r
150                             (empty($this->smarty->registered_objects[$tag][1]) || in_array($methode, $this->smarty->registered_objects[$tag][1]))) {\r
151                         return $this->callTagCompiler('private_object_function', $args, $tag, $methode);\r
152                     } elseif (in_array($methode, $this->smarty->registered_objects[$tag][3])) {\r
153                         return $this->callTagCompiler('private_object_block_function', $args, $tag, $methode);\r
154                     } else {\r
155                         return $this->trigger_template_error ('unallowed methode "' . $methode . '" in registered object "' . $tag . '"', $this->lex->taglineno);\r
156                     } \r
157                 } \r
158                 // check if tag is registered\r
159                 foreach (array('compiler', 'function', 'block') as $type) {\r
160                     if (isset($this->smarty->registered_plugins[$type][$tag])) {\r
161                         // if compiler function plugin call it now\r
162                         if ($type == 'compiler') {\r
163                             if (!$this->smarty->registered_plugins[$type][$tag][1]) {\r
164                                 $this->tag_nocache = true;\r
165                             } \r
166                             return call_user_func_array($this->smarty->registered_plugins[$type][$tag][0], array($args, $this));\r
167                         } \r
168                         // compile registered function or block function\r
169                         if ($type == 'function' || $type == 'block') {\r
170                             return $this->callTagCompiler('private_registered_' . $type, $args, $tag);\r
171                         } \r
172                     } \r
173                 } \r
174                 // check plugins from plugins folder\r
175                 foreach ($this->smarty->plugin_search_order as $plugin_type) {\r
176                     if ($plugin_type == 'compiler' && $this->smarty->loadPlugin('smarty_compiler_' . $tag)) {\r
177                         $plugin = 'smarty_compiler_' . $tag;\r
178                         if (is_callable($plugin)) {\r
179                             return $plugin($args, $this->smarty);\r
180                         } \r
181                         if (class_exists($plugin, false)) {\r
182                             $plugin_object = new $plugin;\r
183                             if (method_exists($plugin_object, 'compile')) {\r
184                                 return $plugin_object->compile($args, $this);\r
185                             } \r
186                         } \r
187                         throw new Exception("Plugin \"{$tag}\" not callable");\r
188                     } else {\r
189                         if ($function = $this->getPlugin($tag, $plugin_type)) {\r
190                             return $this->callTagCompiler('private_' . $plugin_type . '_plugin', $args, $tag, $function);\r
191                         } \r
192                     } \r
193                 } \r
194             } else {\r
195                 // compile closing tag of block function\r
196                 $base_tag = substr($tag, 0, -5); \r
197                 // check if closing tag is a registered object\r
198                 if (isset($this->smarty->registered_objects[$base_tag]) && isset($args['object_methode'])) {\r
199                     $methode = $args['object_methode'];\r
200                     unset ($args['object_methode']);\r
201                     if (in_array($methode, $this->smarty->registered_objects[$base_tag][3])) {\r
202                         return $this->callTagCompiler('private_object_block_function', $args, $tag, $methode);\r
203                     } else {\r
204                         return $this->trigger_template_error ('unallowed closing tag methode "' . $methode . '" in registered object "' . $base_tag . '"', $this->lex->taglineno);\r
205                     } \r
206                 } \r
207                 // registered block tag ?\r
208                 if (isset($this->smarty->registered_plugins['block'][$base_tag])) {\r
209                     return $this->callTagCompiler('private_registered_block', $args, $tag);\r
210                 } \r
211                 // block plugin?\r
212                 if ($function = $this->getPlugin($base_tag, 'block')) {\r
213                     return $this->callTagCompiler('private_block_plugin', $args, $tag, $function);\r
214                 } \r
215                 if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {\r
216                     $plugin = 'smarty_compiler_' . $tag;\r
217                     if (is_callable($plugin)) {\r
218                         return $plugin($args, $this->smarty);\r
219                     } \r
220                     if (class_exists($plugin, false)) {\r
221                         $plugin_object = new $plugin;\r
222                         if (method_exists($plugin_object, 'compile')) {\r
223                             return $plugin_object->compile($args, $this);\r
224                         } \r
225                     } \r
226                     throw new Exception("Plugin \"{$tag}\" not callable");\r
227                 } \r
228             } \r
229             $this->trigger_template_error ("unknown tag \"" . $tag . "\"", $this->lex->taglineno);\r
230         } \r
231     } \r
232 \r
233     /**\r
234      * lazy loads internal compile plugin for tag and calls the compile methode\r
235      * \r
236      * compile objects cached for reuse.\r
237      * class name format:  Smarty_Internal_Compile_TagName\r
238      * plugin filename format: Smarty_Internal_Tagname.php\r
239      * \r
240      * @param  $tag string tag name\r
241      * @param  $args array with tag attributes\r
242      * @param  $param1 optional parameter\r
243      * @param  $param2 optional parameter\r
244      * @param  $param3 optional parameter\r
245      * @return string compiled code\r
246      */\r
247     public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)\r
248     { \r
249         // re-use object if already exists\r
250         if (isset(self::$_tag_objects[$tag])) {\r
251             // compile this tag\r
252             return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);\r
253         } \r
254         // lazy load internal compiler plugin\r
255         $class_name = 'Smarty_Internal_Compile_' . $tag;\r
256         if ($this->smarty->loadPlugin($class_name)) {\r
257             // use plugin if found\r
258             self::$_tag_objects[$tag] = new $class_name; \r
259             // compile this tag\r
260             return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);\r
261         } \r
262         // no internal compile plugin for this tag\r
263         return false;\r
264     } \r
265 \r
266     /**\r
267      * Check for plugins and return function name\r
268      * \r
269      * @param  $pugin_name string name of plugin or function\r
270      * @param  $type string type of plugin\r
271      * @return string call name of function\r
272      */\r
273     public function getPlugin($plugin_name, $type)\r
274     {\r
275         $function = null;\r
276         if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {\r
277             if (isset($this->template->required_plugins['nocache'][$plugin_name][$type])) {\r
278                 $function = $this->template->required_plugins['nocache'][$plugin_name][$type]['function'];\r
279             } else if (isset($this->template->required_plugins['compiled'][$plugin_name][$type])) {\r
280                 $this->template->required_plugins['nocache'][$plugin_name][$type] = $this->template->required_plugins['compiled'][$plugin_name][$type];\r
281                 $function = $this->template->required_plugins['nocache'][$plugin_name][$type]['function'];\r
282             } \r
283         } else {\r
284             if (isset($this->template->required_plugins['compiled'][$plugin_name][$type])) {\r
285                 $function = $this->template->required_plugins['compiled'][$plugin_name][$type]['function'];\r
286             } else if (isset($this->template->required_plugins['compiled'][$plugin_name][$type])) {\r
287                 $this->template->required_plugins['compiled'][$plugin_name][$type] = $this->template->required_plugins['nocache'][$plugin_name][$type];\r
288                 $function = $this->template->required_plugins['compiled'][$plugin_name][$type]['function'];\r
289             } \r
290         } \r
291         if (isset($function)) {\r
292             if ($type == 'modifier') {\r
293                 $this->template->saved_modifer[$plugin_name] = true;\r
294             } \r
295             return $function;\r
296         } \r
297         /**\r
298          * if (isset($this->template->required_plugins_call[$plugin_name][$type])) {\r
299          * if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {\r
300          * if (isset($this->template->required_plugins['compiled'][$plugin_name])) {\r
301          * $this->template->required_plugins['cache'][$plugin_name] = $this->template->required_plugins['compiled'][$plugin_name];\r
302          * } \r
303          * } else {\r
304          * if (isset($this->template->required_plugins['cache'][$plugin_name])) {\r
305          * $this->template->required_plugins['compiled'][$plugin_name] = $this->template->required_plugins['cache'][$plugin_name];\r
306          * } \r
307          * } \r
308          * if ($type == 'modifier') {\r
309          * $this->template->saved_modifer[$plugin_name] = true;\r
310          * } \r
311          * return $this->template->required_plugins_call[$plugin_name][$type];\r
312          * }\r
313          */\r
314         // loop through plugin dirs and find the plugin\r
315         $function = 'smarty_' . $type . '_' . $plugin_name;\r
316         $found = false;\r
317         foreach((array)$this->smarty->plugins_dir as $_plugin_dir) {\r
318             $file = rtrim($_plugin_dir, '/\\') . DS . $type . '.' . $plugin_name . '.php';\r
319             if (file_exists($file)) {\r
320                 // require_once($file);\r
321                 $found = true;\r
322                 break;\r
323             } \r
324         } \r
325         if ($found) {\r
326             // if (is_callable($plugin)) {\r
327             if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {\r
328                 $this->template->required_plugins['nocache'][$plugin_name][$type]['file'] = $file;\r
329                 $this->template->required_plugins['nocache'][$plugin_name][$type]['function'] = $function;\r
330             } else {\r
331                 $this->template->required_plugins['compiled'][$plugin_name][$type]['file'] = $file;\r
332                 $this->template->required_plugins['compiled'][$plugin_name][$type]['function'] = $function;\r
333             } \r
334             /**\r
335              * $this->template->required_plugins_call[$plugin_name][$type] = $plugin;\r
336              * if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {\r
337              * $this->template->required_plugins['cache'][$plugin_name]['file'] = $file;\r
338              * $this->template->required_plugins['cache'][$plugin_name]['type'] = $type;\r
339              * } else {\r
340              * $this->template->required_plugins['compiled'][$plugin_name]['file'] = $file;\r
341              * $this->template->required_plugins['compiled'][$plugin_name]['type'] = $type;\r
342              * }\r
343              */\r
344             if ($type == 'modifier') {\r
345                 $this->template->saved_modifer[$plugin_name] = true;\r
346             } \r
347             return $function;\r
348         } \r
349         if (is_callable($function)) {\r
350             // plugin function is defined in the script\r
351             return $function;\r
352         } \r
353         return false;\r
354     } \r
355     /**\r
356      * Inject inline code for nocache template sections\r
357      * \r
358      * This method gets the content of each template element from the parser.\r
359      * If the content is compiled code and it should be not cached the code is injected\r
360      * into the rendered output.\r
361      * \r
362      * @param string $content content of template element\r
363      * @param boolean $tag_nocache true if the parser detected a nocache situation\r
364      * @param boolean $is_code true if content is compiled code\r
365      * @return string content\r
366      */\r
367     public function processNocacheCode ($content, $is_code)\r
368     { \r
369         // If the template is not evaluated and we have a nocache section and or a nocache tag\r
370         if ($is_code && !empty($content)) {\r
371             // generate replacement code\r
372             if ((!$this->template->resource_object->isEvaluated || $this->template->forceNocache) && $this->template->caching && !$this->suppressNocacheProcessing &&\r
373                     ($this->nocache || $this->tag_nocache || $this->template->forceNocache == 2)) {\r
374                 $this->tag_nocache = false;\r
375                 $this->template->has_nocache_code = true;\r
376                 $_output = str_replace("'", "\'", $content);\r
377                 $_output = "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/" . $_output . "/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>"; \r
378                 // make sure we include modifer plugins for nocache code\r
379                 if (isset($this->template->saved_modifer)) {\r
380                     foreach ($this->template->saved_modifer as $plugin_name => $dummy) {\r
381                         if (isset($this->template->required_plugins['compiled'][$plugin_name]['modifier'])) {\r
382                             $this->template->required_plugins['nocache'][$plugin_name]['modifier'] = $this->template->required_plugins['compiled'][$plugin_name]['modifier'];\r
383                         } \r
384                     } \r
385                     unset($this->template->saved_modifer);\r
386                 } \r
387             } else {\r
388                 $_output = $content;\r
389             } \r
390         } else {\r
391             $_output = $content;\r
392         } \r
393         $this->suppressNocacheProcessing = false;\r
394         return $_output;\r
395     } \r
396     /**\r
397      * display compiler error messages without dying\r
398      * \r
399      * If parameter $args is empty it is a parser detected syntax error.\r
400      * In this case the parser is called to obtain information about expected tokens.\r
401      * \r
402      * If parameter $args contains a string this is used as error message\r
403      * \r
404      * @param  $args string individual error message or null\r
405      */\r
406     public function trigger_template_error($args = null, $line = null)\r
407     { \r
408         // get template source line which has error\r
409         if (!isset($line)) {\r
410             $line = $this->lex->line;\r
411         } \r
412         $match = preg_split("/\n/", $this->lex->data);\r
413         $error_text = 'Syntax Error in template "' . $this->template->getTemplateFilepath() . '"  on line ' . $line . ' "' . htmlspecialchars($match[$line-1]) . '" ';\r
414         if (isset($args)) {\r
415             // individual error message\r
416             $error_text .= $args;\r
417         } else {\r
418             // expected token from parser\r
419             foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {\r
420                 $exp_token = $this->parser->yyTokenName[$token];\r
421                 if (isset($this->lex->smarty_token_names[$exp_token])) {\r
422                     // token type from lexer\r
423                     $expect[] = '"' . $this->lex->smarty_token_names[$exp_token] . '"';\r
424                 } else {\r
425                     // otherwise internal token name\r
426                     $expect[] = $this->parser->yyTokenName[$token];\r
427                 } \r
428             } \r
429             // output parser error message\r
430             $error_text .= ' - Unexpected "' . $this->lex->value . '", expected one of: ' . implode(' , ', $expect);\r
431         } \r
432         throw new Exception($error_text); \r
433         // set error flag\r
434         $this->compile_error = true;\r
435     } \r
436\r
437 \r
438 ?>