Code

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