library/smarty/libs/sysplugins/smarty_internal_templatecompilerbase.php
changeset 46 f11c31f7fa3e
parent 45 a56e7f9a0463
child 47 03388ec805b4
equal deleted inserted replaced
45:a56e7f9a0463 46:f11c31f7fa3e
     1 <?php
       
     2 
       
     3 /**
       
     4  * Smarty Internal Plugin Smarty Template Compiler Base
       
     5  * This file contains the basic classes and methods 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  */
       
    18 abstract class Smarty_Internal_TemplateCompilerBase {
       
    19 
       
    20     /**
       
    21      * Smarty object
       
    22      *
       
    23      * @var Smarty
       
    24      */
       
    25     public $smarty = null;
       
    26 
       
    27     /**
       
    28      * hash for nocache sections
       
    29      *
       
    30      * @var mixed
       
    31      */
       
    32     public $nocache_hash = null;
       
    33 
       
    34     /**
       
    35      * suppress generation of nocache code
       
    36      *
       
    37      * @var bool
       
    38      */
       
    39     public $suppressNocacheProcessing = false;
       
    40 
       
    41     /**
       
    42      * compile tag objects
       
    43      *
       
    44      * @var array
       
    45      */
       
    46     public static $_tag_objects = array();
       
    47 
       
    48     /**
       
    49      * tag stack
       
    50      *
       
    51      * @var array
       
    52      */
       
    53     public $_tag_stack = array();
       
    54 
       
    55     /**
       
    56      * current template
       
    57      *
       
    58      * @var Smarty_Internal_Template
       
    59      */
       
    60     public $template = null;
       
    61 
       
    62     /**
       
    63      * merged included sub template data
       
    64      *
       
    65      * @var array
       
    66      */
       
    67     public $mergedSubTemplatesData = array();
       
    68 
       
    69     /**
       
    70      * merged sub template code
       
    71      *
       
    72      * @var array
       
    73      */
       
    74     public $mergedSubTemplatesCode = array();
       
    75 
       
    76     /**
       
    77      * collected template properties during compilation
       
    78      *
       
    79      * @var array
       
    80      */
       
    81     public $templateProperties = array();
       
    82 
       
    83     /**
       
    84      * sources which must be compiled
       
    85      *
       
    86      * @var array
       
    87      */
       
    88     public $sources = array();
       
    89 
       
    90     /**
       
    91      * flag that we are inside {block}
       
    92      *
       
    93      * @var bool
       
    94      */
       
    95     public $inheritance = false;
       
    96 
       
    97     /**
       
    98      * flag when compiling inheritance child template
       
    99      *
       
   100      * @var bool
       
   101      */
       
   102     public $inheritance_child = false;
       
   103 
       
   104     /**
       
   105      * uid of templates called by {extends} for recursion check
       
   106      *
       
   107      * @var array
       
   108      */
       
   109     public $extends_uid = array();
       
   110 
       
   111     /**
       
   112      * source line offset for error messages
       
   113      *
       
   114      * @var int
       
   115      */
       
   116     public $trace_line_offset = 0;
       
   117 
       
   118     /**
       
   119      * trace uid
       
   120      *
       
   121      * @var string
       
   122      */
       
   123     public $trace_uid = '';
       
   124 
       
   125     /**
       
   126      * trace file path
       
   127      *
       
   128      * @var string
       
   129      */
       
   130     public $trace_filepath = '';
       
   131 
       
   132     /**
       
   133      * stack for tracing file and line of nested {block} tags
       
   134      *
       
   135      * @var array
       
   136      */
       
   137     public $trace_stack = array();
       
   138 
       
   139     /**
       
   140      * plugins loaded by default plugin handler
       
   141      *
       
   142      * @var array
       
   143      */
       
   144     public $default_handler_plugins = array();
       
   145 
       
   146     /**
       
   147      * saved preprocessed modifier list
       
   148      *
       
   149      * @var mixed
       
   150      */
       
   151     public $default_modifier_list = null;
       
   152 
       
   153     /**
       
   154      * force compilation of complete template as nocache
       
   155      *
       
   156      * @var boolean
       
   157      */
       
   158     public $forceNocache = false;
       
   159 
       
   160     /**
       
   161      * suppress Smarty header code in compiled template
       
   162      *
       
   163      * @var bool
       
   164      */
       
   165     public $suppressHeader = false;
       
   166 
       
   167     /**
       
   168      * suppress template property header code in compiled template
       
   169      *
       
   170      * @var bool
       
   171      */
       
   172     public $suppressTemplatePropertyHeader = false;
       
   173 
       
   174     /**
       
   175      * suppress pre and post filter
       
   176      *
       
   177      * @var bool
       
   178      */
       
   179     public $suppressFilter = false;
       
   180 
       
   181     /**
       
   182      * flag if compiled template file shall we written
       
   183      *
       
   184      * @var bool
       
   185      */
       
   186     public $write_compiled_code = true;
       
   187 
       
   188     /**
       
   189      * flag if currently a template function is compiled
       
   190      *
       
   191      * @var bool
       
   192      */
       
   193     public $compiles_template_function = false;
       
   194 
       
   195     /**
       
   196      * called sub functions from template function
       
   197      *
       
   198      * @var array
       
   199      */
       
   200     public $called_functions = array();
       
   201 
       
   202     /**
       
   203      * compiled template function code
       
   204      *
       
   205      * @var string
       
   206      */
       
   207     public $templateFunctionCode = '';
       
   208 
       
   209     /**
       
   210      * php_handling setting either from Smarty or security
       
   211      *
       
   212      * @var int
       
   213      */
       
   214     public $php_handling = 0;
       
   215 
       
   216     /**
       
   217      * flags for used modifier plugins
       
   218      *
       
   219      * @var array
       
   220      */
       
   221     public $modifier_plugins = array();
       
   222 
       
   223     /**
       
   224      * type of already compiled modifier
       
   225      *
       
   226      * @var array
       
   227      */
       
   228     public $known_modifier_type = array();
       
   229 
       
   230     /**
       
   231      * parent compiler object for merged subtemplates and template functions
       
   232      *
       
   233      * @var Smarty_Internal_TemplateCompilerBase
       
   234      */
       
   235     public $parent_compiler = null;
       
   236 
       
   237     /**
       
   238      * Flag true when compiling nocache section
       
   239      *
       
   240      * @var bool
       
   241      */
       
   242     public $nocache = false;
       
   243 
       
   244     /**
       
   245      * Flag true when tag is compiled as nocache
       
   246      *
       
   247      * @var bool
       
   248      */
       
   249     public $tag_nocache = false;
       
   250 
       
   251     /**
       
   252      * Flag to restart parsing
       
   253      *
       
   254      * @var bool
       
   255      */
       
   256     public $abort_and_recompile = false;
       
   257 
       
   258     /**
       
   259      * Compiled tag prefix code
       
   260      *
       
   261      * @var array
       
   262      */
       
   263     public $prefix_code = array();
       
   264 
       
   265     /**
       
   266      * Prefix code  stack
       
   267      *
       
   268      * @var array
       
   269      */
       
   270     public $prefixCodeStack = array();
       
   271 
       
   272     /**
       
   273      * Tag has compiled code
       
   274      *
       
   275      * @var bool
       
   276      */
       
   277     public $has_code = false;
       
   278 
       
   279     /**
       
   280      * A variable string was compiled
       
   281      *
       
   282      * @var bool
       
   283      */
       
   284     public $has_variable_string = false;
       
   285 
       
   286     /**
       
   287      * Tag creates output
       
   288      *
       
   289      * @var bool
       
   290      */
       
   291     public $has_output = false;
       
   292 
       
   293     /**
       
   294      * Strip preg pattern
       
   295      *
       
   296      * @var string
       
   297      */
       
   298     public $stripRegEx = '![\t ]*[\r\n]+[\t ]*!';
       
   299 
       
   300     /**
       
   301      * method to compile a Smarty template
       
   302      *
       
   303      * @param  mixed $_content template source
       
   304      *
       
   305      * @return bool  true if compiling succeeded, false if it failed
       
   306      */
       
   307     abstract protected function doCompile($_content);
       
   308 
       
   309     /**
       
   310      * Initialize compiler
       
   311      */
       
   312     public function __construct() {
       
   313         $this->nocache_hash = str_replace(array('.', ','), '_', uniqid(rand(), true));
       
   314     }
       
   315 
       
   316     /**
       
   317      * Method to compile a Smarty template
       
   318      *
       
   319      * @param  Smarty_Internal_Template $template template object to compile
       
   320      * @param  bool $nocache true is shall be compiled in nocache mode
       
   321      * @param null|Smarty_Internal_TemplateCompilerBase $parent_compiler
       
   322      *
       
   323      * @return bool true if compiling succeeded, false if it failed
       
   324      */
       
   325     public function compileTemplate(Smarty_Internal_Template $template, $nocache = null, $parent_compiler = null) {
       
   326         // save template object in compiler class
       
   327         $this->template = $template;
       
   328         if (isset($this->template->smarty->security_policy)) {
       
   329             $this->php_handling = $this->template->smarty->security_policy->php_handling;
       
   330         } else {
       
   331             $this->php_handling = $this->template->smarty->php_handling;
       
   332         }
       
   333         $this->parent_compiler = $parent_compiler ? $parent_compiler : $this;
       
   334         $nocache = isset($nocache) ? $nocache : false;
       
   335         if (empty($template->properties['nocache_hash'])) {
       
   336             $template->properties['nocache_hash'] = $this->nocache_hash;
       
   337         } else {
       
   338             $this->nocache_hash = $template->properties['nocache_hash'];
       
   339         }
       
   340         $save_source = $this->template->source;
       
   341         // template header code
       
   342         $template_header = '';
       
   343         if (!$this->suppressHeader) {
       
   344             $template_header .= "<?php /* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") . "\n";
       
   345             $template_header .= "         compiled from \"" . $this->template->source->filepath . "\" */ ?>\n";
       
   346         }
       
   347 
       
   348         if (empty($this->template->source->components)) {
       
   349             $this->sources = array($template->source);
       
   350         } else {
       
   351             // we have array of inheritance templates by extends: resource
       
   352             $this->sources = array_reverse($template->source->components);
       
   353         }
       
   354         $loop = 0;
       
   355         // the $this->sources array can get additional elements while compiling by the {extends} tag
       
   356         while ($this->template->source = array_shift($this->sources)) {
       
   357             $this->smarty->_current_file = $this->template->source->filepath;
       
   358             if ($this->smarty->debugging) {
       
   359                 Smarty_Internal_Debug::start_compile($this->template);
       
   360             }
       
   361             $no_sources = count($this->sources);
       
   362             $this->parent_compiler->template->properties['file_dependency'][$this->template->source->uid] = array($this->template->source->filepath, $this->template->source->timestamp, $this->template->source->type);
       
   363             $loop++;
       
   364             if ($no_sources) {
       
   365                 $this->inheritance_child = true;
       
   366             } else {
       
   367                 $this->inheritance_child = false;
       
   368             }
       
   369             do {
       
   370                 // flag for nochache sections
       
   371                 $this->nocache = $nocache;
       
   372                 $this->tag_nocache = false;
       
   373                 // reset has nocache code flag
       
   374                 $this->template->has_nocache_code = false;
       
   375                 $this->has_variable_string = false;
       
   376                 $this->prefix_code = array();
       
   377                 $_compiled_code = '';
       
   378                 // flag for aborting current and start recompile
       
   379                 $this->abort_and_recompile = false;
       
   380                 // get template source
       
   381                 $_content = $this->template->source->content;
       
   382                 if ($_content != '') {
       
   383                     // run prefilter if required
       
   384                     if ((isset($this->smarty->autoload_filters['pre']) || isset($this->smarty->registered_filters['pre'])) && !$this->suppressFilter) {
       
   385                         $_content = Smarty_Internal_Filter_Handler::runFilter('pre', $_content, $template);
       
   386                     }
       
   387                     // call compiler
       
   388                     $_compiled_code = $this->doCompile($_content, true);
       
   389                 }
       
   390             } while ($this->abort_and_recompile);
       
   391             if ($this->smarty->debugging) {
       
   392                 Smarty_Internal_Debug::end_compile($this->template);
       
   393             }
       
   394         }
       
   395         // restore source
       
   396         $this->template->source = $save_source;
       
   397         unset($save_source);
       
   398         $this->smarty->_current_file = $this->template->source->filepath;
       
   399         // free memory
       
   400         unset($this->parser->root_buffer, $this->parser->current_buffer, $this->parser, $this->lex);
       
   401         self::$_tag_objects = array();
       
   402         // return compiled code to template object
       
   403         $merged_code = '';
       
   404         if (!empty($this->mergedSubTemplatesCode)) {
       
   405             foreach ($this->mergedSubTemplatesCode as $code) {
       
   406                 $merged_code .= $code;
       
   407             }
       
   408         }
       
   409         // run postfilter if required on compiled template code
       
   410         if ((isset($this->smarty->autoload_filters['post']) || isset($this->smarty->registered_filters['post'])) && !$this->suppressFilter && $_compiled_code != '') {
       
   411             $_compiled_code = Smarty_Internal_Filter_Handler::runFilter('post', $_compiled_code, $template);
       
   412         }
       
   413         if ($this->suppressTemplatePropertyHeader) {
       
   414             $_compiled_code .= $merged_code;
       
   415         } else {
       
   416             $_compiled_code = $template_header . Smarty_Internal_Extension_CodeFrame::create($template, $_compiled_code) . $merged_code;
       
   417         }
       
   418         if (!empty($this->templateFunctionCode)) {
       
   419             // run postfilter if required on compiled template code
       
   420             if ((isset($this->smarty->autoload_filters['post']) || isset($this->smarty->registered_filters['post'])) && !$this->suppressFilter) {
       
   421                 $_compiled_code .= Smarty_Internal_Filter_Handler::runFilter('post', $this->templateFunctionCode, $template);
       
   422             } else {
       
   423                 $_compiled_code .= $this->templateFunctionCode;
       
   424             }
       
   425         }
       
   426         // unset content because template inheritance could have replace source with parent code
       
   427         unset ($template->source->content);
       
   428         $this->parent_compiler = null;
       
   429         $this->template = null;
       
   430         return $_compiled_code;
       
   431     }
       
   432 
       
   433     /**
       
   434      * Compile Tag
       
   435      * This is a call back from the lexer/parser
       
   436      *
       
   437      * Save current prefix code
       
   438      * Compile tag
       
   439      * Merge tag prefix code with saved one
       
   440      * (required nested tags in attributes)
       
   441      *
       
   442      * @param  string $tag tag name
       
   443      * @param  array $args array with tag attributes
       
   444      * @param  array $parameter array with compilation parameter
       
   445      *
       
   446      * @throws SmartyCompilerException
       
   447      * @throws SmartyException
       
   448      * @return string compiled code
       
   449      */
       
   450     public function compileTag($tag, $args, $parameter = array()) {
       
   451         $this->prefixCodeStack[] = $this->prefix_code;
       
   452         $this->prefix_code = array();
       
   453         $result = $this->compileTag2($tag, $args, $parameter);
       
   454         $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
       
   455         return $result;
       
   456     }
       
   457 
       
   458     /**
       
   459      * Compile Tag
       
   460      *
       
   461      * @param  string $tag tag name
       
   462      * @param  array $args array with tag attributes
       
   463      * @param  array $parameter array with compilation parameter
       
   464      *
       
   465      * @throws SmartyCompilerException
       
   466      * @throws SmartyException
       
   467      * @return string compiled code
       
   468      */
       
   469     private function compileTag2($tag, $args, $parameter) {
       
   470         $plugin_type = '';
       
   471         // $args contains the attributes parsed and compiled by the lexer/parser
       
   472         // assume that tag does compile into code, but creates no HTML output
       
   473         $this->has_code = true;
       
   474         $this->has_output = false;
       
   475         // log tag/attributes
       
   476         if (isset($this->smarty->get_used_tags) && $this->smarty->get_used_tags) {
       
   477             $this->template->used_tags[] = array($tag, $args);
       
   478         }
       
   479         // check nocache option flag
       
   480         if (in_array("'nocache'", $args) || in_array(array('nocache' => 'true'), $args) || in_array(array('nocache' => '"true"'), $args) || in_array(array('nocache' => "'true'"), $args)
       
   481         ) {
       
   482             $this->tag_nocache = true;
       
   483         }
       
   484         // compile the smarty tag (required compile classes to compile the tag are autoloaded)
       
   485         if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
       
   486             if (isset($this->parent_compiler->templateProperties['tpl_function'][$tag])) {
       
   487                 // template defined by {template} tag
       
   488                 $args['_attr']['name'] = "'" . $tag . "'";
       
   489                 $_output = $this->callTagCompiler('call', $args, $parameter);
       
   490             }
       
   491         }
       
   492         if ($_output !== false) {
       
   493             if ($_output !== true) {
       
   494                 // did we get compiled code
       
   495                 if ($this->has_code) {
       
   496                     // Does it create output?
       
   497                     if ($this->has_output) {
       
   498                         $_output .= "\n";
       
   499                     }
       
   500                     // return compiled code
       
   501                     return $_output;
       
   502                 }
       
   503             }
       
   504             // tag did not produce compiled code
       
   505             return null;
       
   506         } else {
       
   507             // map_named attributes
       
   508             if (isset($args['_attr'])) {
       
   509                 foreach ($args['_attr'] as $key => $attribute) {
       
   510                     if (is_array($attribute)) {
       
   511                         $args = array_merge($args, $attribute);
       
   512                     }
       
   513                 }
       
   514             }
       
   515             // not an internal compiler tag
       
   516             if (strlen($tag) < 6 || substr($tag, -5) != 'close') {
       
   517                 // check if tag is a registered object
       
   518                 if (isset($this->smarty->registered_objects[$tag]) && isset($parameter['object_method'])) {
       
   519                     $method = $parameter['object_method'];
       
   520                     if (!in_array($method, $this->smarty->registered_objects[$tag][3]) && (empty($this->smarty->registered_objects[$tag][1]) || in_array($method, $this->smarty->registered_objects[$tag][1]))
       
   521                     ) {
       
   522                         return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
       
   523                     } elseif (in_array($method, $this->smarty->registered_objects[$tag][3])) {
       
   524                         return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $method);
       
   525                     } else {
       
   526                         // throw exception
       
   527                         $this->trigger_template_error('not allowed method "' . $method . '" in registered object "' . $tag . '"', $this->lex->taglineno);
       
   528                     }
       
   529                 }
       
   530                 // check if tag is registered
       
   531                 foreach (array(Smarty::PLUGIN_COMPILER, Smarty::PLUGIN_FUNCTION, Smarty::PLUGIN_BLOCK) as $plugin_type) {
       
   532                     if (isset($this->smarty->registered_plugins[$plugin_type][$tag])) {
       
   533                         // if compiler function plugin call it now
       
   534                         if ($plugin_type == Smarty::PLUGIN_COMPILER) {
       
   535                             $new_args = array();
       
   536                             foreach ($args as $key => $mixed) {
       
   537                                 if (is_array($mixed)) {
       
   538                                     $new_args = array_merge($new_args, $mixed);
       
   539                                 } else {
       
   540                                     $new_args[$key] = $mixed;
       
   541                                 }
       
   542                             }
       
   543                             if (!$this->smarty->registered_plugins[$plugin_type][$tag][1]) {
       
   544                                 $this->tag_nocache = true;
       
   545                             }
       
   546                             $function = $this->smarty->registered_plugins[$plugin_type][$tag][0];
       
   547                             if (!is_array($function)) {
       
   548                                 return $function($new_args, $this);
       
   549                             } elseif (is_object($function[0])) {
       
   550                                 return $this->smarty->registered_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this);
       
   551                             } else {
       
   552                                 return call_user_func_array($function, array($new_args, $this));
       
   553                             }
       
   554                         }
       
   555                         // compile registered function or block function
       
   556                         if ($plugin_type == Smarty::PLUGIN_FUNCTION || $plugin_type == Smarty::PLUGIN_BLOCK) {
       
   557                             return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag);
       
   558                         }
       
   559                     }
       
   560                 }
       
   561                 // check plugins from plugins folder
       
   562                 foreach ($this->smarty->plugin_search_order as $plugin_type) {
       
   563                     if ($plugin_type == Smarty::PLUGIN_COMPILER && $this->smarty->loadPlugin('smarty_compiler_' . $tag) && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))) {
       
   564                         $plugin = 'smarty_compiler_' . $tag;
       
   565                         if (is_callable($plugin)) {
       
   566                             // convert arguments format for old compiler plugins
       
   567                             $new_args = array();
       
   568                             foreach ($args as $key => $mixed) {
       
   569                                 if (is_array($mixed)) {
       
   570                                     $new_args = array_merge($new_args, $mixed);
       
   571                                 } else {
       
   572                                     $new_args[$key] = $mixed;
       
   573                                 }
       
   574                             }
       
   575 
       
   576                             return $plugin($new_args, $this->smarty);
       
   577                         }
       
   578                         if (class_exists($plugin, false)) {
       
   579                             $plugin_object = new $plugin;
       
   580                             if (method_exists($plugin_object, 'compile')) {
       
   581                                 return $plugin_object->compile($args, $this);
       
   582                             }
       
   583                         }
       
   584                         throw new SmartyException("Plugin \"{$tag}\" not callable");
       
   585                     } else {
       
   586                         if ($function = $this->getPlugin($tag, $plugin_type)) {
       
   587                             if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
       
   588                                 return $this->callTagCompiler('private_' . $plugin_type . '_plugin', $args, $parameter, $tag, $function);
       
   589                             }
       
   590                         }
       
   591                     }
       
   592                 }
       
   593                 if (is_callable($this->smarty->default_plugin_handler_func)) {
       
   594                     $found = false;
       
   595                     // look for already resolved tags
       
   596                     foreach ($this->smarty->plugin_search_order as $plugin_type) {
       
   597                         if (isset($this->default_handler_plugins[$plugin_type][$tag])) {
       
   598                             $found = true;
       
   599                             break;
       
   600                         }
       
   601                     }
       
   602                     if (!$found) {
       
   603                         // call default handler
       
   604                         foreach ($this->smarty->plugin_search_order as $plugin_type) {
       
   605                             if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
       
   606                                 $found = true;
       
   607                                 break;
       
   608                             }
       
   609                         }
       
   610                     }
       
   611                     if ($found) {
       
   612                         // if compiler function plugin call it now
       
   613                         if ($plugin_type == Smarty::PLUGIN_COMPILER) {
       
   614                             $new_args = array();
       
   615                             foreach ($args as $mixed) {
       
   616                                 $new_args = array_merge($new_args, $mixed);
       
   617                             }
       
   618                             $function = $this->default_handler_plugins[$plugin_type][$tag][0];
       
   619                             if (!is_array($function)) {
       
   620                                 return $function($new_args, $this);
       
   621                             } elseif (is_object($function[0])) {
       
   622                                 return $this->default_handler_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this);
       
   623                             } else {
       
   624                                 return call_user_func_array($function, array($new_args, $this));
       
   625                             }
       
   626                         } else {
       
   627                             return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag);
       
   628                         }
       
   629                     }
       
   630                 }
       
   631             } else {
       
   632                 // compile closing tag of block function
       
   633                 $base_tag = substr($tag, 0, -5);
       
   634                 // check if closing tag is a registered object
       
   635                 if (isset($this->smarty->registered_objects[$base_tag]) && isset($parameter['object_method'])) {
       
   636                     $method = $parameter['object_method'];
       
   637                     if (in_array($method, $this->smarty->registered_objects[$base_tag][3])) {
       
   638                         return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $method);
       
   639                     } else {
       
   640                         // throw exception
       
   641                         $this->trigger_template_error('not allowed closing tag method "' . $method . '" in registered object "' . $base_tag . '"', $this->lex->taglineno);
       
   642                     }
       
   643                 }
       
   644                 // registered block tag ?
       
   645                 if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_BLOCK][$base_tag]) || isset($this->default_handler_plugins[Smarty::PLUGIN_BLOCK][$base_tag])) {
       
   646                     return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
       
   647                 }
       
   648                 // block plugin?
       
   649                 if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
       
   650                     return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
       
   651                 }
       
   652                 // registered compiler plugin ?
       
   653                 if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag])) {
       
   654                     // if compiler function plugin call it now
       
   655                     $args = array();
       
   656                     if (!$this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][1]) {
       
   657                         $this->tag_nocache = true;
       
   658                     }
       
   659                     $function = $this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][0];
       
   660                     if (!is_array($function)) {
       
   661                         return $function($args, $this);
       
   662                     } elseif (is_object($function[0])) {
       
   663                         return $this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][0][0]->$function[1]($args, $this);
       
   664                     } else {
       
   665                         return call_user_func_array($function, array($args, $this));
       
   666                     }
       
   667                 }
       
   668                 if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
       
   669                     $plugin = 'smarty_compiler_' . $tag;
       
   670                     if (is_callable($plugin)) {
       
   671                         return $plugin($args, $this->smarty);
       
   672                     }
       
   673                     if (class_exists($plugin, false)) {
       
   674                         $plugin_object = new $plugin;
       
   675                         if (method_exists($plugin_object, 'compile')) {
       
   676                             return $plugin_object->compile($args, $this);
       
   677                         }
       
   678                     }
       
   679                     throw new SmartyException("Plugin \"{$tag}\" not callable");
       
   680                 }
       
   681             }
       
   682             $this->trigger_template_error("unknown tag \"" . $tag . "\"", $this->lex->taglineno);
       
   683         }
       
   684     }
       
   685 
       
   686     /**
       
   687      * compile variable
       
   688      *
       
   689      * @param string $variable
       
   690      *
       
   691      * @return string
       
   692      */
       
   693     public function compileVariable($variable) {
       
   694         if (strpos($variable, '(') == 0) {
       
   695             // not a variable variable
       
   696             $var = trim($variable, '\'');
       
   697             $this->tag_nocache = $this->tag_nocache | $this->template->getVariable($var, null, true, false)->nocache;
       
   698             $this->template->properties['variables'][$var] = $this->tag_nocache | $this->nocache;
       
   699         }
       
   700         return '$_smarty_tpl->tpl_vars[' . $variable . ']->value';
       
   701     }
       
   702 
       
   703     /**
       
   704      * This method is called from parser to process a text content section
       
   705      * - remove text from inheritance child templates as they may generate output
       
   706      * - strip text if strip is enabled
       
   707      *
       
   708      * @param string $text
       
   709      *
       
   710      * @return null|\Smarty_Internal_ParseTree_Text
       
   711      */
       
   712     public function processText($text) {
       
   713         if ($this->parser->strip) {
       
   714             return new Smarty_Internal_ParseTree_Text($this->parser, preg_replace($this->stripRegEx, '', $text));
       
   715         } else {
       
   716             return new Smarty_Internal_ParseTree_Text($this->parser, $text);
       
   717         }
       
   718     }
       
   719 
       
   720     /**
       
   721      * lazy loads internal compile plugin for tag and calls the compile method
       
   722      * compile objects cached for reuse.
       
   723      * class name format:  Smarty_Internal_Compile_TagName
       
   724      * plugin filename format: Smarty_Internal_Tagname.php
       
   725      *
       
   726      * @param  string $tag tag name
       
   727      * @param  array $args list of tag attributes
       
   728      * @param  mixed $param1 optional parameter
       
   729      * @param  mixed $param2 optional parameter
       
   730      * @param  mixed $param3 optional parameter
       
   731      *
       
   732      * @return string compiled code
       
   733      */
       
   734     public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null) {
       
   735         // check if tag allowed by security
       
   736         if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
       
   737             // re-use object if already exists
       
   738             if (!isset(self::$_tag_objects[$tag])) {
       
   739                 // lazy load internal compiler plugin
       
   740                 $class_name = 'Smarty_Internal_Compile_' . $tag;
       
   741                 if ($this->smarty->loadPlugin($class_name)) {
       
   742                     self::$_tag_objects[$tag] = new $class_name;
       
   743                 } else {
       
   744                     return false;
       
   745                 }
       
   746             }
       
   747             // compile this tag
       
   748             return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);
       
   749         }
       
   750         // no internal compile plugin for this tag
       
   751         return false;
       
   752     }
       
   753 
       
   754     /**
       
   755      * Check for plugins and return function name
       
   756      *
       
   757      * @param         $plugin_name
       
   758      * @param  string $plugin_type type of plugin
       
   759      *
       
   760      * @return string call name of function
       
   761      */
       
   762     public function getPlugin($plugin_name, $plugin_type) {
       
   763         $function = null;
       
   764         if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
       
   765             if (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) {
       
   766                 $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'];
       
   767             } elseif (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) {
       
   768                 $this->template->required_plugins['nocache'][$plugin_name][$plugin_type] = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type];
       
   769                 $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'];
       
   770             }
       
   771         } else {
       
   772             if (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) {
       
   773                 $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'];
       
   774             } elseif (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) {
       
   775                 $this->template->required_plugins['compiled'][$plugin_name][$plugin_type] = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type];
       
   776                 $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'];
       
   777             }
       
   778         }
       
   779         if (isset($function)) {
       
   780             if ($plugin_type == 'modifier') {
       
   781                 $this->modifier_plugins[$plugin_name] = true;
       
   782             }
       
   783 
       
   784             return $function;
       
   785         }
       
   786         // loop through plugin dirs and find the plugin
       
   787         $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
       
   788         $file = $this->smarty->loadPlugin($function, false);
       
   789 
       
   790         if (is_string($file)) {
       
   791             if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
       
   792                 $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['file'] = $file;
       
   793                 $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'] = $function;
       
   794             } else {
       
   795                 $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['file'] = $file;
       
   796                 $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'] = $function;
       
   797             }
       
   798             if ($plugin_type == 'modifier') {
       
   799                 $this->modifier_plugins[$plugin_name] = true;
       
   800             }
       
   801 
       
   802             return $function;
       
   803         }
       
   804         if (is_callable($function)) {
       
   805             // plugin function is defined in the script
       
   806             return $function;
       
   807         }
       
   808 
       
   809         return false;
       
   810     }
       
   811 
       
   812     /**
       
   813      * Check for plugins by default plugin handler
       
   814      *
       
   815      * @param  string $tag name of tag
       
   816      * @param  string $plugin_type type of plugin
       
   817      *
       
   818      * @return boolean true if found
       
   819      */
       
   820     public function getPluginFromDefaultHandler($tag, $plugin_type) {
       
   821         $callback = null;
       
   822         $script = null;
       
   823         $cacheable = true;
       
   824         $result = call_user_func_array($this->smarty->default_plugin_handler_func, array($tag, $plugin_type, $this->template, &$callback, &$script, &$cacheable));
       
   825         if ($result) {
       
   826             $this->tag_nocache = $this->tag_nocache || !$cacheable;
       
   827             if ($script !== null) {
       
   828                 if (is_file($script)) {
       
   829                     if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
       
   830                         $this->template->required_plugins['nocache'][$tag][$plugin_type]['file'] = $script;
       
   831                         $this->template->required_plugins['nocache'][$tag][$plugin_type]['function'] = $callback;
       
   832                     } else {
       
   833                         $this->template->required_plugins['compiled'][$tag][$plugin_type]['file'] = $script;
       
   834                         $this->template->required_plugins['compiled'][$tag][$plugin_type]['function'] = $callback;
       
   835                     }
       
   836                     require_once $script;
       
   837                 } else {
       
   838                     $this->trigger_template_error("Default plugin handler: Returned script file \"{$script}\" for \"{$tag}\" not found");
       
   839                 }
       
   840             }
       
   841             if (!is_string($callback) && !(is_array($callback) && is_string($callback[0]) && is_string($callback[1]))) {
       
   842                 $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" must be a static function name or array of class and function name");
       
   843             }
       
   844             if (is_callable($callback)) {
       
   845                 $this->default_handler_plugins[$plugin_type][$tag] = array($callback, true, array());
       
   846 
       
   847                 return true;
       
   848             } else {
       
   849                 $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" not callable");
       
   850             }
       
   851         }
       
   852 
       
   853         return false;
       
   854     }
       
   855 
       
   856     /**
       
   857      * Append code segments and remove unneeded ?> <?php transitions
       
   858      *
       
   859      * @param string $left
       
   860      * @param string $right
       
   861      *
       
   862      * @return string
       
   863      */
       
   864     public function appendCode($left, $right) {
       
   865         if (preg_match('/\s*\?>\s*$/', $left) && preg_match('/^\s*<\?php\s+/', $right)) {
       
   866             $left = preg_replace('/\s*\?>\s*$/', "\n", $left);
       
   867             $left .= preg_replace('/^\s*<\?php\s+/', '', $right);
       
   868         } else {
       
   869             $left .= $right;
       
   870         }
       
   871         return $left;
       
   872     }
       
   873 
       
   874     /**
       
   875      * Inject inline code for nocache template sections
       
   876      * This method gets the content of each template element from the parser.
       
   877      * If the content is compiled code and it should be not cached the code is injected
       
   878      * into the rendered output.
       
   879      *
       
   880      * @param  string $content content of template element
       
   881      * @param  boolean $is_code true if content is compiled code
       
   882      *
       
   883      * @return string  content
       
   884      */
       
   885     public function processNocacheCode($content, $is_code) {
       
   886         // If the template is not evaluated and we have a nocache section and or a nocache tag
       
   887         if ($is_code && !empty($content)) {
       
   888             // generate replacement code
       
   889             if ((!($this->template->source->recompiled) || $this->forceNocache) && $this->template->caching && !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache)
       
   890             ) {
       
   891                 $this->template->has_nocache_code = true;
       
   892                 $_output = addcslashes($content, '\'\\');
       
   893                 $_output = str_replace("^#^", "'", $_output);
       
   894                 $_output = "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/" . $_output . "/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
       
   895                 // make sure we include modifier plugins for nocache code
       
   896                 foreach ($this->modifier_plugins as $plugin_name => $dummy) {
       
   897                     if (isset($this->template->required_plugins['compiled'][$plugin_name]['modifier'])) {
       
   898                         $this->template->required_plugins['nocache'][$plugin_name]['modifier'] = $this->template->required_plugins['compiled'][$plugin_name]['modifier'];
       
   899                     }
       
   900                 }
       
   901             } else {
       
   902                 $_output = $content;
       
   903             }
       
   904         } else {
       
   905             $_output = $content;
       
   906         }
       
   907         $this->modifier_plugins = array();
       
   908         $this->suppressNocacheProcessing = false;
       
   909         $this->tag_nocache = false;
       
   910 
       
   911         return $_output;
       
   912     }
       
   913 
       
   914     /**
       
   915      * Generate nocache code string
       
   916      *
       
   917      * @param string $code PHP code
       
   918      *
       
   919      * @return string
       
   920      */
       
   921     public function makeNocacheCode($code) {
       
   922         return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " . str_replace("^#^", "'", addcslashes($code, '\'\\')) . "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n";
       
   923     }
       
   924 
       
   925     /**
       
   926      *  push current file and line offset on stack for tracing {block} source lines
       
   927      *
       
   928      * @param string $file new filename
       
   929      * @param string $uid uid of file
       
   930      * @param int $line line offset to source
       
   931      * @param bool $debug false debug end_compile shall not be called
       
   932      */
       
   933     public function pushTrace($file, $uid, $line, $debug = true) {
       
   934         if ($this->smarty->debugging && $debug) {
       
   935             Smarty_Internal_Debug::end_compile($this->template);
       
   936         }
       
   937         array_push($this->trace_stack, array($this->smarty->_current_file, $this->trace_filepath, $this->trace_uid, $this->trace_line_offset));
       
   938         $this->trace_filepath = $this->smarty->_current_file = $file;
       
   939         $this->trace_uid = $uid;
       
   940         $this->trace_line_offset = $line;
       
   941         if ($this->smarty->debugging) {
       
   942             Smarty_Internal_Debug::start_compile($this->template);
       
   943         }
       
   944     }
       
   945 
       
   946     /**
       
   947      *  restore file and line offset
       
   948      */
       
   949     public function popTrace() {
       
   950         if ($this->smarty->debugging) {
       
   951             Smarty_Internal_Debug::end_compile($this->template);
       
   952         }
       
   953         $r = array_pop($this->trace_stack);
       
   954         $this->smarty->_current_file = $r[0];
       
   955         $this->trace_filepath = $r[1];
       
   956         $this->trace_uid = $r[2];
       
   957         $this->trace_line_offset = $r[3];
       
   958         if ($this->smarty->debugging) {
       
   959             Smarty_Internal_Debug::start_compile($this->template);
       
   960         }
       
   961     }
       
   962 
       
   963     /**
       
   964      * display compiler error messages without dying
       
   965      * If parameter $args is empty it is a parser detected syntax error.
       
   966      * In this case the parser is called to obtain information about expected tokens.
       
   967      * If parameter $args contains a string this is used as error message
       
   968      *
       
   969      * @param  string $args individual error message or null
       
   970      * @param  string $line line-number
       
   971      *
       
   972      * @throws SmartyCompilerException when an unexpected token is found
       
   973      */
       
   974     public function trigger_template_error($args = null, $line = null) {
       
   975         // get template source line which has error
       
   976         if (!isset($line)) {
       
   977             $line = $this->lex->line;
       
   978         }
       
   979         //        $line += $this->trace_line_offset;
       
   980         $match = preg_split("/\n/", $this->lex->data);
       
   981         $error_text = 'Syntax error in template "' . (empty($this->trace_filepath) ? $this->template->source->filepath : $this->trace_filepath) . '"  on line ' . ($line + $this->trace_line_offset) . ' "' . trim(preg_replace('![\t\r\n]+!', ' ', $match[$line - 1])) . '" ';
       
   982         if (isset($args)) {
       
   983             // individual error message
       
   984             $error_text .= $args;
       
   985         } else {
       
   986             $expect = array();
       
   987             // expected token from parser
       
   988             $error_text .= ' - Unexpected "' . $this->lex->value . '"';
       
   989             if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
       
   990                 foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
       
   991                     $exp_token = $this->parser->yyTokenName[$token];
       
   992                     if (isset($this->lex->smarty_token_names[$exp_token])) {
       
   993                         // token type from lexer
       
   994                         $expect[] = '"' . $this->lex->smarty_token_names[$exp_token] . '"';
       
   995                     } else {
       
   996                         // otherwise internal token name
       
   997                         $expect[] = $this->parser->yyTokenName[$token];
       
   998                     }
       
   999                 }
       
  1000                 $error_text .= ', expected one of: ' . implode(' , ', $expect);
       
  1001             }
       
  1002         }
       
  1003         $e = new SmartyCompilerException($error_text);
       
  1004         $e->line = $line;
       
  1005         $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[$line - 1]));
       
  1006         $e->desc = $args;
       
  1007         $e->template = $this->template->source->filepath;
       
  1008         throw $e;
       
  1009     }
       
  1010 }