source: pro-violet-viettel/sourcecode/application/third_party/Smarty/sysplugins/smarty_internal_templatecompilerbase.php @ 914

Last change on this file since 914 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 27.6 KB
Line 
1<?php
2/**
3 * Smarty Internal Plugin Smarty Template Compiler Base
4 *
5 * This file contains the basic classes and methodes for compiling Smarty templates with lexer/parser
6 *
7 * @package Smarty
8 * @subpackage Compiler
9 * @author Uwe Tews
10 */
11
12/**
13 * Main abstract compiler class
14 *
15 * @package Smarty
16 * @subpackage Compiler
17 */
18abstract class Smarty_Internal_TemplateCompilerBase {
19
20    /**
21     * hash for nocache sections
22     *
23     * @var mixed
24     */
25    private $nocache_hash = null;
26    /**
27     * suppress generation of nocache code
28     *
29     * @var bool
30     */
31    public $suppressNocacheProcessing = false;
32    /**
33     * suppress generation of merged template code
34     *
35     * @var bool
36     */
37    public $suppressMergedTemplates = false;
38    /**
39     * compile tag objects
40     *
41     * @var array
42     */
43    public static $_tag_objects = array();
44    /**
45     * tag stack
46     *
47     * @var array
48     */
49    public $_tag_stack = array();
50    /**
51     * current template
52     *
53     * @var Smarty_Internal_Template
54     */
55    public $template = null;
56    /**
57     * merged templates
58     *
59     * @var array
60     */
61    public $merged_templates = array();
62    /**
63     * flag when compiling {block}
64     *
65     * @var bool
66     */
67    public $inheritance = false;
68    /**
69     * plugins loaded by default plugin handler
70     *
71     * @var array
72     */
73    public $default_handler_plugins = array();
74    /**
75     * saved preprocessed modifier list
76     *
77     * @var mixed
78     */
79    public $default_modifier_list = null;
80    /**
81     * force compilation of complete template as nocache
82     * @var boolean
83     */
84    public $forceNocache = false;
85    /**
86     * suppress Smarty header code in compiled template
87     * @var bool
88     */
89    public $suppressHeader = false;
90    /**
91     * suppress template property header code in compiled template
92     * @var bool
93     */
94    public $suppressTemplatePropertyHeader = false;
95    /**
96     * flag if compiled template file shall we written
97     * @var bool
98     */
99    public $write_compiled_code = true;
100    /**
101     * flag if currently a template function is compiled
102     * @var bool
103     */
104    public $compiles_template_function = false;
105    /**
106     * called subfuntions from template function
107     * @var array
108     */
109    public $called_functions = array();
110    /**
111     * flags for used modifier plugins
112     * @var array
113     */
114    public $modifier_plugins = array();
115
116    /**
117     * Initialize compiler
118     */
119    public function __construct()
120    {
121        $this->nocache_hash = str_replace('.', '-', uniqid(rand(), true));
122    }
123
124    /**
125     * Method to compile a Smarty template
126     *
127     * @param  Smarty_Internal_Template $template template object to compile
128     * @return bool true if compiling succeeded, false if it failed
129     */
130    public function compileTemplate(Smarty_Internal_Template $template)
131    {
132        if (empty($template->properties['nocache_hash'])) {
133            $template->properties['nocache_hash'] = $this->nocache_hash;
134        } else {
135            $this->nocache_hash = $template->properties['nocache_hash'];
136        }
137        // flag for nochache sections
138        $this->nocache = false;
139        $this->tag_nocache = false;
140        // save template object in compiler class
141        $this->template = $template;
142        // reset has noche code flag
143        $this->template->has_nocache_code = false;
144        $this->smarty->_current_file = $saved_filepath = $this->template->source->filepath;
145        // template header code
146        $template_header = '';
147        if (!$this->suppressHeader) {
148            $template_header .= "<?php /* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") . "\n";
149            $template_header .= "         compiled from \"" . $this->template->source->filepath . "\" */ ?>\n";
150        }
151
152        do {
153            // flag for aborting current and start recompile
154            $this->abort_and_recompile = false;
155            // get template source
156            $_content = $template->source->content;
157            // run prefilter if required
158            if (isset($this->smarty->autoload_filters['pre']) || isset($this->smarty->registered_filters['pre'])) {
159                $template->source->content = $_content = Smarty_Internal_Filter_Handler::runFilter('pre', $_content, $template);
160            }
161            // on empty template just return header
162            if ($_content == '') {
163                if ($this->suppressTemplatePropertyHeader) {
164                    $code = '';
165                } else {
166                    $code = $template_header . $template->createTemplateCodeFrame();
167                }
168                return $code;
169            }
170            // call compiler
171            $_compiled_code = $this->doCompile($_content);
172        } while ($this->abort_and_recompile);
173        $this->template->source->filepath = $saved_filepath;
174        // free memory
175        unset($this->parser->root_buffer, $this->parser->current_buffer, $this->parser, $this->lex, $this->template);
176        self::$_tag_objects = array();
177        // return compiled code to template object
178        $merged_code = '';
179        if (!$this->suppressMergedTemplates) {
180            foreach ($this->merged_templates as $code) {
181                $merged_code .= $code;
182            }
183        }
184        if ($this->suppressTemplatePropertyHeader) {
185            $code = $_compiled_code . $merged_code;
186        } else {
187            $code = $template_header . $template->createTemplateCodeFrame($_compiled_code) . $merged_code;
188        }
189        // run postfilter if required
190        if (isset($this->smarty->autoload_filters['post']) || isset($this->smarty->registered_filters['post'])) {
191            $code = Smarty_Internal_Filter_Handler::runFilter('post', $code, $template);
192        }
193        return $code;
194    }
195
196    /**
197     * Compile Tag
198     *
199     * This is a call back from the lexer/parser
200     * It executes the required compile plugin for the Smarty tag
201     *
202     * @param string $tag       tag name
203     * @param array  $args      array with tag attributes
204     * @param array  $parameter array with compilation parameter
205     * @return string compiled code
206     */
207    public function compileTag($tag, $args, $parameter = array())
208    {
209        // $args contains the attributes parsed and compiled by the lexer/parser
210        // assume that tag does compile into code, but creates no HTML output
211        $this->has_code = true;
212        $this->has_output = false;
213        // log tag/attributes
214        if (isset($this->smarty->get_used_tags) && $this->smarty->get_used_tags) {
215            $this->template->used_tags[] = array($tag, $args);
216        }
217        // check nocache option flag
218        if (in_array("'nocache'",$args) || in_array(array('nocache'=>'true'),$args)
219        || in_array(array('nocache'=>'"true"'),$args) || in_array(array('nocache'=>"'true'"),$args)) {
220            $this->tag_nocache = true;
221        }
222        // compile the smarty tag (required compile classes to compile the tag are autoloaded)
223        if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
224            if (isset($this->smarty->template_functions[$tag])) {
225                // template defined by {template} tag
226                $args['_attr']['name'] = "'" . $tag . "'";
227                $_output = $this->callTagCompiler('call', $args, $parameter);
228            }
229        }
230        if ($_output !== false) {
231            if ($_output !== true) {
232                // did we get compiled code
233                if ($this->has_code) {
234                    // Does it create output?
235                    if ($this->has_output) {
236                        $_output .= "\n";
237                    }
238                    // return compiled code
239                    return $_output;
240                }
241            }
242            // tag did not produce compiled code
243            return '';
244        } else {
245            // map_named attributes
246            if (isset($args['_attr'])) {
247                foreach ($args['_attr'] as $key => $attribute) {
248                    if (is_array($attribute)) {
249                        $args = array_merge($args, $attribute);
250                    }
251                }
252            }
253            // not an internal compiler tag
254            if (strlen($tag) < 6 || substr($tag, -5) != 'close') {
255                // check if tag is a registered object
256                if (isset($this->smarty->registered_objects[$tag]) && isset($parameter['object_methode'])) {
257                    $methode = $parameter['object_methode'];
258                    if (!in_array($methode, $this->smarty->registered_objects[$tag][3]) &&
259                    (empty($this->smarty->registered_objects[$tag][1]) || in_array($methode, $this->smarty->registered_objects[$tag][1]))) {
260                        return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $methode);
261                    } elseif (in_array($methode, $this->smarty->registered_objects[$tag][3])) {
262                        return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $methode);
263                    } else {
264                        return $this->trigger_template_error ('unallowed methode "' . $methode . '" in registered object "' . $tag . '"', $this->lex->taglineno);
265                    }
266                }
267                // check if tag is registered
268                foreach (array(Smarty::PLUGIN_COMPILER, Smarty::PLUGIN_FUNCTION, Smarty::PLUGIN_BLOCK) as $plugin_type) {
269                    if (isset($this->smarty->registered_plugins[$plugin_type][$tag])) {
270                        // if compiler function plugin call it now
271                        if ($plugin_type == Smarty::PLUGIN_COMPILER) {
272                            $new_args = array();
273                            foreach ($args as $key => $mixed) {
274                                if (is_array($mixed)) {
275                                        $new_args = array_merge($new_args, $mixed);
276                                } else {
277                                        $new_args[$key] = $mixed;
278                                }
279                            }
280                            if (!$this->smarty->registered_plugins[$plugin_type][$tag][1]) {
281                                $this->tag_nocache = true;
282                            }
283                            $function = $this->smarty->registered_plugins[$plugin_type][$tag][0];
284                            if (!is_array($function)) {
285                                return $function($new_args, $this);
286                            } else if (is_object($function[0])) {
287                                return $this->smarty->registered_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this);
288                            } else {
289                                return call_user_func_array($function, array($new_args, $this));
290                            }
291                        }
292                        // compile registered function or block function
293                        if ($plugin_type == Smarty::PLUGIN_FUNCTION || $plugin_type == Smarty::PLUGIN_BLOCK) {
294                            return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag);
295                        }
296
297                    }
298                }
299                // check plugins from plugins folder
300                foreach ($this->smarty->plugin_search_order as $plugin_type) {
301                    if ($plugin_type == Smarty::PLUGIN_BLOCK && $this->smarty->loadPlugin('smarty_compiler_' . $tag) && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))) {
302                        $plugin = 'smarty_compiler_' . $tag;
303                        if (is_callable($plugin)) {
304                            // convert arguments format for old compiler plugins
305                            $new_args = array();
306                            foreach ($args as $key => $mixed) {
307                                if (is_array($mixed)) {
308                                        $new_args = array_merge($new_args, $mixed);
309                                } else {
310                                        $new_args[$key] = $mixed;
311                                }
312                            }
313                            return $plugin($new_args, $this->smarty);
314                        }
315                        if (class_exists($plugin, false)) {
316                            $plugin_object = new $plugin;
317                            if (method_exists($plugin_object, 'compile')) {
318                                return $plugin_object->compile($args, $this);
319                            }
320                        }
321                        throw new SmartyException("Plugin \"{$tag}\" not callable");
322                    } else {
323                        if ($function = $this->getPlugin($tag, $plugin_type)) {
324                            if(!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
325                                return $this->callTagCompiler('private_' . $plugin_type . '_plugin', $args, $parameter, $tag, $function);
326                            }
327                        }
328                    }
329                }
330                if (is_callable($this->smarty->default_plugin_handler_func)) {
331                    $found = false;
332                    // look for already resolved tags
333                    foreach ($this->smarty->plugin_search_order as $plugin_type) {
334                        if (isset($this->default_handler_plugins[$plugin_type][$tag])) {
335                            $found = true;
336                            break;
337                        }
338                    }
339                    if (!$found) {
340                        // call default handler
341                        foreach ($this->smarty->plugin_search_order as $plugin_type) {
342                            if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
343                                $found = true;
344                                break;
345                            }
346                        }
347                    }
348                    if ($found) {
349                        // if compiler function plugin call it now
350                        if ($plugin_type == Smarty::PLUGIN_COMPILER) {
351                            $new_args = array();
352                            foreach ($args as $mixed) {
353                                $new_args = array_merge($new_args, $mixed);
354                            }
355                            $function = $this->default_handler_plugins[$plugin_type][$tag][0];
356                            if (!is_array($function)) {
357                                return $function($new_args, $this);
358                            } else if (is_object($function[0])) {
359                                return $this->default_handler_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this);
360                            } else {
361                                return call_user_func_array($function, array($new_args, $this));
362                            }
363                        } else {
364                            return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag);
365                        }
366                    }
367                }
368            } else {
369                // compile closing tag of block function
370                $base_tag = substr($tag, 0, -5);
371                // check if closing tag is a registered object
372                if (isset($this->smarty->registered_objects[$base_tag]) && isset($parameter['object_methode'])) {
373                    $methode = $parameter['object_methode'];
374                    if (in_array($methode, $this->smarty->registered_objects[$base_tag][3])) {
375                        return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $methode);
376                    } else {
377                        return $this->trigger_template_error ('unallowed closing tag methode "' . $methode . '" in registered object "' . $base_tag . '"', $this->lex->taglineno);
378                    }
379                }
380                // registered block tag ?
381                if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_BLOCK][$base_tag]) || isset($this->default_handler_plugins[Smarty::PLUGIN_BLOCK][$base_tag])) {
382                    return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
383                }
384                // block plugin?
385                if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
386                    return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
387                }
388                if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
389                    $plugin = 'smarty_compiler_' . $tag;
390                    if (is_callable($plugin)) {
391                        return $plugin($args, $this->smarty);
392                    }
393                    if (class_exists($plugin, false)) {
394                        $plugin_object = new $plugin;
395                        if (method_exists($plugin_object, 'compile')) {
396                            return $plugin_object->compile($args, $this);
397                        }
398                    }
399                    throw new SmartyException("Plugin \"{$tag}\" not callable");
400                }
401            }
402            $this->trigger_template_error ("unknown tag \"" . $tag . "\"", $this->lex->taglineno);
403        }
404    }
405
406    /**
407     * lazy loads internal compile plugin for tag and calls the compile methode
408     *
409     * compile objects cached for reuse.
410     * class name format:  Smarty_Internal_Compile_TagName
411     * plugin filename format: Smarty_Internal_Tagname.php
412     *
413     * @param string $tag   tag name
414     * @param array $args   list of tag attributes
415     * @param mixed $param1 optional parameter
416     * @param mixed $param2 optional parameter
417     * @param mixed $param3 optional parameter
418     * @return string compiled code
419     */
420    public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
421    {
422        // re-use object if already exists
423        if (isset(self::$_tag_objects[$tag])) {
424            // compile this tag
425            return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);
426        }
427        // lazy load internal compiler plugin
428        $class_name = 'Smarty_Internal_Compile_' . $tag;
429        if ($this->smarty->loadPlugin($class_name)) {
430            // check if tag allowed by security
431            if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
432            // use plugin if found
433            self::$_tag_objects[$tag] = new $class_name;
434            // compile this tag
435            return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);
436            }
437        }
438        // no internal compile plugin for this tag
439        return false;
440    }
441
442    /**
443     * Check for plugins and return function name
444     *
445     * @param string $pugin_name  name of plugin or function
446     * @param string $plugin_type type of plugin
447     * @return string call name of function
448     */
449    public function getPlugin($plugin_name, $plugin_type)
450    {
451        $function = null;
452        if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
453            if (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) {
454                $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'];
455            } else if (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) {
456                $this->template->required_plugins['nocache'][$plugin_name][$plugin_type] = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type];
457                $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'];
458            }
459        } else {
460            if (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) {
461                $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'];
462            } else if (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) {
463                $this->template->required_plugins['compiled'][$plugin_name][$plugin_type] = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type];
464                $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'];
465            }
466        }
467        if (isset($function)) {
468            if ($plugin_type == 'modifier') {
469                $this->modifier_plugins[$plugin_name] = true;
470            }
471            return $function;
472        }
473        // loop through plugin dirs and find the plugin
474        $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
475        $file = $this->smarty->loadPlugin($function, false);
476
477        if (is_string($file)) {
478            if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
479                $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['file'] = $file;
480                $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'] = $function;
481            } else {
482                $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['file'] = $file;
483                $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'] = $function;
484            }
485            if ($plugin_type == 'modifier') {
486                $this->modifier_plugins[$plugin_name] = true;
487            }
488            return $function;
489        }
490        if (is_callable($function)) {
491            // plugin function is defined in the script
492            return $function;
493        }
494        return false;
495    }
496
497    /**
498     * Check for plugins by default plugin handler
499     *
500     * @param string $tag         name of tag
501     * @param string $plugin_type type of plugin
502     * @return boolean true if found
503     */
504    public function getPluginFromDefaultHandler($tag, $plugin_type)
505    {
506        $callback = null;
507        $script = null;
508        $result = call_user_func_array(
509            $this->smarty->default_plugin_handler_func,
510            array($tag, $plugin_type, $this->template, &$callback, &$script)
511        );
512        if ($result) {
513            if ($script !== null) {
514                if (is_file($script)) {
515                    if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
516                        $this->template->required_plugins['nocache'][$tag][$plugin_type]['file'] = $script;
517                        $this->template->required_plugins['nocache'][$tag][$plugin_type]['function'] = $callback;
518                    } else {
519                        $this->template->required_plugins['compiled'][$tag][$plugin_type]['file'] = $script;
520                        $this->template->required_plugins['compiled'][$tag][$plugin_type]['function'] = $callback;
521                    }
522                    include_once $script;
523                }  else {
524                    $this->trigger_template_error("Default plugin handler: Returned script file \"{$script}\" for \"{$tag}\" not found");
525                }
526            }
527            if (!is_string($callback) && !(is_array($callback) && is_string($callback[0]) && is_string($callback[1]))) {
528                $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" must be a static function name or array of class and function name");
529            }
530            if (is_callable($callback)) {
531                $this->default_handler_plugins[$plugin_type][$tag] = array($callback, true, array());
532                return true;
533            } else {
534                $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" not callable");
535            }
536        }
537        return false;
538    }
539
540    /**
541     * Inject inline code for nocache template sections
542     *
543     * This method gets the content of each template element from the parser.
544     * If the content is compiled code and it should be not cached the code is injected
545     * into the rendered output.
546     *
547     * @param string  $content content of template element
548     * @param boolean $is_code true if content is compiled code
549     * @return string content
550     */
551    public function processNocacheCode($content, $is_code)
552    {
553        // If the template is not evaluated and we have a nocache section and or a nocache tag
554        if ($is_code && !empty($content)) {
555            // generate replacement code
556            if ((!($this->template->source->recompiled) || $this->forceNocache) && $this->template->caching && !$this->suppressNocacheProcessing &&
557            ($this->nocache || $this->tag_nocache || $this->forceNocache == 2)) {
558                $this->template->has_nocache_code = true;
559                $_output = str_replace("'", "\'", $content);
560                $_output = str_replace('\\\\', '\\\\\\\\', $_output);
561                $_output = str_replace("^#^", "'", $_output);
562                $_output = "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/" . $_output . "/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
563                // make sure we include modifer plugins for nocache code
564                foreach ($this->modifier_plugins as $plugin_name => $dummy) {
565                    if (isset($this->template->required_plugins['compiled'][$plugin_name]['modifier'])) {
566                        $this->template->required_plugins['nocache'][$plugin_name]['modifier'] = $this->template->required_plugins['compiled'][$plugin_name]['modifier'];
567                    }
568                }
569            } else {
570                $_output = $content;
571            }
572        } else {
573            $_output = $content;
574        }
575        $this->modifier_plugins = array();
576        $this->suppressNocacheProcessing = false;
577        $this->tag_nocache = false;
578        return $_output;
579    }
580
581    /**
582     * display compiler error messages without dying
583     *
584     * If parameter $args is empty it is a parser detected syntax error.
585     * In this case the parser is called to obtain information about expected tokens.
586     *
587     * If parameter $args contains a string this is used as error message
588     *
589     * @param string $args individual error message or null
590     * @param string $line line-number
591     * @throws SmartyCompilerException when an unexpected token is found
592     */
593    public function trigger_template_error($args = null, $line = null)
594    {
595        // get template source line which has error
596        if (!isset($line)) {
597            $line = $this->lex->line;
598        }
599        $match = preg_split("/\n/", $this->lex->data);
600        $error_text = 'Syntax Error in template "' . $this->template->source->filepath . '"  on line ' . $line . ' "' . htmlspecialchars(trim(preg_replace('![\t\r\n]+!',' ',$match[$line-1]))) . '" ';
601        if (isset($args)) {
602            // individual error message
603            $error_text .= $args;
604        } else {
605            // expected token from parser
606            $error_text .= ' - Unexpected "' . $this->lex->value.'"';
607            if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4 ) {
608                foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
609                    $exp_token = $this->parser->yyTokenName[$token];
610                    if (isset($this->lex->smarty_token_names[$exp_token])) {
611                        // token type from lexer
612                        $expect[] = '"' . $this->lex->smarty_token_names[$exp_token] . '"';
613                    } else {
614                        // otherwise internal token name
615                        $expect[] = $this->parser->yyTokenName[$token];
616                    }
617                }
618                $error_text .= ', expected one of: ' . implode(' , ', $expect);
619            }
620        }
621        throw new SmartyCompilerException($error_text);
622    }
623
624}
625
626?>
Note: See TracBrowser for help on using the repository browser.