"Fossies" - the Fresh Open Source Software Archive

Member "smarty-3.1.34/libs/sysplugins/smarty_internal_templatecompilerbase.php" (28 Feb 2019, 60382 Bytes) of package /linux/www/smarty-3.1.34.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "smarty_internal_templatecompilerbase.php" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.1.32_vs_3.1.33.

    1 <?php
    2 /**
    3  * Smarty Internal Plugin Smarty Template Compiler Base
    4  * This file contains the basic classes and methods for compiling Smarty templates with lexer/parser
    5  *
    6  * @package    Smarty
    7  * @subpackage Compiler
    8  * @author     Uwe Tews
    9  */
   10 
   11 /**
   12  * Main abstract compiler class
   13  *
   14  * @package    Smarty
   15  * @subpackage Compiler
   16  *
   17  * @property Smarty_Internal_SmartyTemplateCompiler $prefixCompiledCode  = ''
   18  * @property Smarty_Internal_SmartyTemplateCompiler $postfixCompiledCode = ''
   19  * @method   registerPostCompileCallback($callback, $parameter = array(), $key = null, $replace = false)
   20  * @method   unregisterPostCompileCallback($key)
   21  */
   22 abstract class Smarty_Internal_TemplateCompilerBase
   23 {
   24     /**
   25      * compile tag objects cache
   26      *
   27      * @var array
   28      */
   29     public static $_tag_objects = array();
   30 
   31     /**
   32      * counter for prefix variable number
   33      *
   34      * @var int
   35      */
   36     public static $prefixVariableNumber = 0;
   37 
   38     /**
   39      * Smarty object
   40      *
   41      * @var Smarty
   42      */
   43     public $smarty = null;
   44 
   45     /**
   46      * Parser object
   47      *
   48      * @var Smarty_Internal_Templateparser
   49      */
   50     public $parser = null;
   51 
   52     /**
   53      * hash for nocache sections
   54      *
   55      * @var mixed
   56      */
   57     public $nocache_hash = null;
   58 
   59     /**
   60      * suppress generation of nocache code
   61      *
   62      * @var bool
   63      */
   64     public $suppressNocacheProcessing = false;
   65 
   66     /**
   67      * caching enabled (copied from template object)
   68      *
   69      * @var int
   70      */
   71     public $caching = 0;
   72 
   73     /**
   74      * tag stack
   75      *
   76      * @var array
   77      */
   78     public $_tag_stack = array();
   79 
   80     /**
   81      * tag stack count
   82      *
   83      * @var array
   84      */
   85     public $_tag_stack_count = array();
   86 
   87     /**
   88      * Plugins used by template
   89      *
   90      * @var array
   91      */
   92     public $required_plugins = array('compiled' => array(), 'nocache' => array());
   93 
   94     /**
   95      * Required plugins stack
   96      *
   97      * @var array
   98      */
   99     public $required_plugins_stack = array();
  100 
  101     /**
  102      * current template
  103      *
  104      * @var Smarty_Internal_Template
  105      */
  106     public $template = null;
  107 
  108     /**
  109      * merged included sub template data
  110      *
  111      * @var array
  112      */
  113     public $mergedSubTemplatesData = array();
  114 
  115     /**
  116      * merged sub template code
  117      *
  118      * @var array
  119      */
  120     public $mergedSubTemplatesCode = array();
  121 
  122     /**
  123      * collected template properties during compilation
  124      *
  125      * @var array
  126      */
  127     public $templateProperties = array();
  128 
  129     /**
  130      * source line offset for error messages
  131      *
  132      * @var int
  133      */
  134     public $trace_line_offset = 0;
  135 
  136     /**
  137      * trace uid
  138      *
  139      * @var string
  140      */
  141     public $trace_uid = '';
  142 
  143     /**
  144      * trace file path
  145      *
  146      * @var string
  147      */
  148     public $trace_filepath = '';
  149 
  150     /**
  151      * stack for tracing file and line of nested {block} tags
  152      *
  153      * @var array
  154      */
  155     public $trace_stack = array();
  156 
  157     /**
  158      * plugins loaded by default plugin handler
  159      *
  160      * @var array
  161      */
  162     public $default_handler_plugins = array();
  163 
  164     /**
  165      * saved preprocessed modifier list
  166      *
  167      * @var mixed
  168      */
  169     public $default_modifier_list = null;
  170 
  171     /**
  172      * force compilation of complete template as nocache
  173      *
  174      * @var boolean
  175      */
  176     public $forceNocache = false;
  177 
  178     /**
  179      * flag if compiled template file shall we written
  180      *
  181      * @var bool
  182      */
  183     public $write_compiled_code = true;
  184 
  185     /**
  186      * Template functions
  187      *
  188      * @var array
  189      */
  190     public $tpl_function = array();
  191 
  192     /**
  193      * called sub functions from template function
  194      *
  195      * @var array
  196      */
  197     public $called_functions = array();
  198 
  199     /**
  200      * compiled template or block function code
  201      *
  202      * @var string
  203      */
  204     public $blockOrFunctionCode = '';
  205 
  206     /**
  207      * php_handling setting either from Smarty or security
  208      *
  209      * @var int
  210      */
  211     public $php_handling = 0;
  212 
  213     /**
  214      * flags for used modifier plugins
  215      *
  216      * @var array
  217      */
  218     public $modifier_plugins = array();
  219 
  220     /**
  221      * type of already compiled modifier
  222      *
  223      * @var array
  224      */
  225     public $known_modifier_type = array();
  226 
  227     /**
  228      * parent compiler object for merged subtemplates and template functions
  229      *
  230      * @var Smarty_Internal_TemplateCompilerBase
  231      */
  232     public $parent_compiler = null;
  233 
  234     /**
  235      * Flag true when compiling nocache section
  236      *
  237      * @var bool
  238      */
  239     public $nocache = false;
  240 
  241     /**
  242      * Flag true when tag is compiled as nocache
  243      *
  244      * @var bool
  245      */
  246     public $tag_nocache = false;
  247 
  248     /**
  249      * Compiled tag prefix code
  250      *
  251      * @var array
  252      */
  253     public $prefix_code = array();
  254 
  255     /**
  256      * used prefix variables by current compiled tag
  257      *
  258      * @var array
  259      */
  260     public $usedPrefixVariables = array();
  261 
  262     /**
  263      * Prefix code  stack
  264      *
  265      * @var array
  266      */
  267     public $prefixCodeStack = array();
  268 
  269     /**
  270      * Tag has compiled code
  271      *
  272      * @var bool
  273      */
  274     public $has_code = false;
  275 
  276     /**
  277      * A variable string was compiled
  278      *
  279      * @var bool
  280      */
  281     public $has_variable_string = false;
  282 
  283     /**
  284      * Stack for {setfilter} {/setfilter}
  285      *
  286      * @var array
  287      */
  288     public $variable_filter_stack = array();
  289 
  290     /**
  291      * variable filters for {setfilter} {/setfilter}
  292      *
  293      * @var array
  294      */
  295     public $variable_filters = array();
  296 
  297     /**
  298      * Nesting count of looping tags like {foreach}, {for}, {section}, {while}
  299      *
  300      * @var int
  301      */
  302     public $loopNesting = 0;
  303 
  304     /**
  305      * Strip preg pattern
  306      *
  307      * @var string
  308      */
  309     public $stripRegEx = '![\t ]*[\r\n]+[\t ]*!';
  310 
  311     /**
  312      * plugin search order
  313      *
  314      * @var array
  315      */
  316     public $plugin_search_order = array(
  317         'function',
  318         'block',
  319         'compiler',
  320         'class'
  321     );
  322 
  323     /**
  324      * General storage area for tag compiler plugins
  325      *
  326      * @var array
  327      */
  328     public $_cache = array();
  329 
  330     /**
  331      * Lexer preg pattern for left delimiter
  332      *
  333      * @var string
  334      */
  335     private $ldelPreg = '[{]';
  336 
  337     /**
  338      * Lexer preg pattern for right delimiter
  339      *
  340      * @var string
  341      */
  342     private $rdelPreg = '[}]';
  343 
  344     /**
  345      * Length of right delimiter
  346      *
  347      * @var int
  348      */
  349     private $rdelLength = 0;
  350 
  351     /**
  352      * Length of left delimiter
  353      *
  354      * @var int
  355      */
  356     private $ldelLength = 0;
  357 
  358     /**
  359      * Lexer preg pattern for user literals
  360      *
  361      * @var string
  362      */
  363     private $literalPreg = '';
  364 
  365     /**
  366      * Initialize compiler
  367      *
  368      * @param Smarty $smarty global instance
  369      */
  370     public function __construct(Smarty $smarty)
  371     {
  372         $this->smarty = $smarty;
  373         $this->nocache_hash = str_replace(
  374             array(
  375                 '.',
  376                 ','
  377             ),
  378             '_',
  379             uniqid(mt_rand(), true)
  380         );
  381     }
  382 
  383     /**
  384      * Method to compile a Smarty template
  385      *
  386      * @param Smarty_Internal_Template                  $template template object to compile
  387      * @param bool                                      $nocache  true is shall be compiled in nocache mode
  388      * @param null|Smarty_Internal_TemplateCompilerBase $parent_compiler
  389      *
  390      * @return bool true if compiling succeeded, false if it failed
  391      * @throws \Exception
  392      */
  393     public function compileTemplate(
  394         Smarty_Internal_Template $template,
  395         $nocache = null,
  396         Smarty_Internal_TemplateCompilerBase $parent_compiler = null
  397     ) {
  398         // get code frame of compiled template
  399         $_compiled_code = $template->smarty->ext->_codeFrame->create(
  400             $template,
  401             $this->compileTemplateSource(
  402                 $template,
  403                 $nocache,
  404                 $parent_compiler
  405             ),
  406             $this->postFilter($this->blockOrFunctionCode) .
  407             join('', $this->mergedSubTemplatesCode),
  408             false,
  409             $this
  410         );
  411         return $_compiled_code;
  412     }
  413 
  414     /**
  415      * Compile template source and run optional post filter
  416      *
  417      * @param \Smarty_Internal_Template             $template
  418      * @param null|bool                             $nocache flag if template must be compiled in nocache mode
  419      * @param \Smarty_Internal_TemplateCompilerBase $parent_compiler
  420      *
  421      * @return string
  422      * @throws \Exception
  423      */
  424     public function compileTemplateSource(
  425         Smarty_Internal_Template $template,
  426         $nocache = null,
  427         Smarty_Internal_TemplateCompilerBase $parent_compiler = null
  428     ) {
  429         try {
  430             // save template object in compiler class
  431             $this->template = $template;
  432             if (property_exists($this->template->smarty, 'plugin_search_order')) {
  433                 $this->plugin_search_order = $this->template->smarty->plugin_search_order;
  434             }
  435             if ($this->smarty->debugging) {
  436                 if (!isset($this->smarty->_debug)) {
  437                     $this->smarty->_debug = new Smarty_Internal_Debug();
  438                 }
  439                 $this->smarty->_debug->start_compile($this->template);
  440             }
  441             if (isset($this->template->smarty->security_policy)) {
  442                 $this->php_handling = $this->template->smarty->security_policy->php_handling;
  443             } else {
  444                 $this->php_handling = $this->template->smarty->php_handling;
  445             }
  446             $this->parent_compiler = $parent_compiler ? $parent_compiler : $this;
  447             $nocache = isset($nocache) ? $nocache : false;
  448             if (empty($template->compiled->nocache_hash)) {
  449                 $template->compiled->nocache_hash = $this->nocache_hash;
  450             } else {
  451                 $this->nocache_hash = $template->compiled->nocache_hash;
  452             }
  453             $this->caching = $template->caching;
  454             // flag for nocache sections
  455             $this->nocache = $nocache;
  456             $this->tag_nocache = false;
  457             // reset has nocache code flag
  458             $this->template->compiled->has_nocache_code = false;
  459             $this->has_variable_string = false;
  460             $this->prefix_code = array();
  461             // add file dependency
  462             if ($this->smarty->merge_compiled_includes || $this->template->source->handler->checkTimestamps()) {
  463                 $this->parent_compiler->template->compiled->file_dependency[ $this->template->source->uid ] =
  464                     array(
  465                         $this->template->source->filepath,
  466                         $this->template->source->getTimeStamp(),
  467                         $this->template->source->type,
  468                     );
  469             }
  470             $this->smarty->_current_file = $this->template->source->filepath;
  471             // get template source
  472             if (!empty($this->template->source->components)) {
  473                 // we have array of inheritance templates by extends: resource
  474                 // generate corresponding source code sequence
  475                 $_content =
  476                     Smarty_Internal_Compile_Extends::extendsSourceArrayCode($this->template);
  477             } else {
  478                 // get template source
  479                 $_content = $this->template->source->getContent();
  480             }
  481             $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
  482             if (!empty($this->required_plugins[ 'compiled' ]) || !empty($this->required_plugins[ 'nocache' ])) {
  483                 $_compiled_code = '<?php ' . $this->compileRequiredPlugins() . "?>\n" . $_compiled_code;
  484             }
  485         } catch (Exception $e) {
  486             if ($this->smarty->debugging) {
  487                 $this->smarty->_debug->end_compile($this->template);
  488             }
  489             $this->_tag_stack = array();
  490             // free memory
  491             $this->parent_compiler = null;
  492             $this->template = null;
  493             $this->parser = null;
  494             throw $e;
  495         }
  496         if ($this->smarty->debugging) {
  497             $this->smarty->_debug->end_compile($this->template);
  498         }
  499         $this->parent_compiler = null;
  500         $this->parser = null;
  501         return $_compiled_code;
  502     }
  503 
  504     /**
  505      * Optionally process compiled code by post filter
  506      *
  507      * @param string $code compiled code
  508      *
  509      * @return string
  510      * @throws \SmartyException
  511      */
  512     public function postFilter($code)
  513     {
  514         // run post filter if on code
  515         if (!empty($code)
  516             && (isset($this->smarty->autoload_filters[ 'post' ]) || isset($this->smarty->registered_filters[ 'post' ]))
  517         ) {
  518             return $this->smarty->ext->_filterHandler->runFilter('post', $code, $this->template);
  519         } else {
  520             return $code;
  521         }
  522     }
  523 
  524     /**
  525      * Run optional prefilter
  526      *
  527      * @param string $_content template source
  528      *
  529      * @return string
  530      * @throws \SmartyException
  531      */
  532     public function preFilter($_content)
  533     {
  534         // run pre filter if required
  535         if ($_content !== ''
  536             && ((isset($this->smarty->autoload_filters[ 'pre' ]) || isset($this->smarty->registered_filters[ 'pre' ])))
  537         ) {
  538             return $this->smarty->ext->_filterHandler->runFilter('pre', $_content, $this->template);
  539         } else {
  540             return $_content;
  541         }
  542     }
  543 
  544     /**
  545      * Compile Tag
  546      * This is a call back from the lexer/parser
  547      *
  548      * Save current prefix code
  549      * Compile tag
  550      * Merge tag prefix code with saved one
  551      * (required nested tags in attributes)
  552      *
  553      * @param string $tag       tag name
  554      * @param array  $args      array with tag attributes
  555      * @param array  $parameter array with compilation parameter
  556      *
  557      * @throws SmartyCompilerException
  558      * @throws SmartyException
  559      * @return string compiled code
  560      */
  561     public function compileTag($tag, $args, $parameter = array())
  562     {
  563         $this->prefixCodeStack[] = $this->prefix_code;
  564         $this->prefix_code = array();
  565         $result = $this->compileTag2($tag, $args, $parameter);
  566         $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
  567         return $result;
  568     }
  569 
  570     /**
  571      * compile variable
  572      *
  573      * @param string $variable
  574      *
  575      * @return string
  576      */
  577     public function compileVariable($variable)
  578     {
  579         if (!strpos($variable, '(')) {
  580             // not a variable variable
  581             $var = trim($variable, '\'');
  582             $this->tag_nocache = $this->tag_nocache |
  583                                  $this->template->ext->getTemplateVars->_getVariable(
  584                                      $this->template,
  585                                      $var,
  586                                      null,
  587                                      true,
  588                                      false
  589                                  )->nocache;
  590             // todo $this->template->compiled->properties['variables'][$var] = $this->tag_nocache | $this->nocache;
  591         }
  592         return '$_smarty_tpl->tpl_vars[' . $variable . ']->value';
  593     }
  594 
  595     /**
  596      * compile config variable
  597      *
  598      * @param string $variable
  599      *
  600      * @return string
  601      */
  602     public function compileConfigVariable($variable)
  603     {
  604         // return '$_smarty_tpl->config_vars[' . $variable . ']';
  605         return '$_smarty_tpl->smarty->ext->configLoad->_getConfigVariable($_smarty_tpl, ' . $variable . ')';
  606     }
  607 
  608     /**
  609      * compile PHP function call
  610      *
  611      * @param string $name
  612      * @param array  $parameter
  613      *
  614      * @return string
  615      * @throws \SmartyCompilerException
  616      */
  617     public function compilePHPFunctionCall($name, $parameter)
  618     {
  619         if (!$this->smarty->security_policy || $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) {
  620             if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0
  621                 || strcasecmp($name, 'array') === 0 || is_callable($name)
  622             ) {
  623                 $func_name = strtolower($name);
  624                 $par = implode(',', $parameter);
  625                 $parHasFuction = strpos($par, '(') !== false;
  626                 if ($func_name === 'isset') {
  627                     if (count($parameter) === 0) {
  628                         $this->trigger_template_error('Illegal number of parameter in "isset()"');
  629                     }
  630                     if ($parHasFuction) {
  631                         $pa = array();
  632                         foreach ($parameter as $p) {
  633                             $pa[] = (strpos($p, '(') === false) ? ('isset(' . $p . ')') : ('(' . $p . ' !== null )');
  634                         }
  635                         return '(' . implode(' && ', $pa) . ')';
  636                     } else {
  637                         $isset_par = str_replace("')->value", "',null,true,false)->value", $par);
  638                     }
  639                     return $name . '(' . $isset_par . ')';
  640                 } elseif (in_array(
  641                     $func_name,
  642                     array(
  643                         'empty',
  644                         'reset',
  645                         'current',
  646                         'end',
  647                         'prev',
  648                         'next'
  649                     )
  650                 )
  651                 ) {
  652                     if (count($parameter) !== 1) {
  653                         $this->trigger_template_error("Illegal number of parameter in '{$func_name()}'");
  654                     }
  655                     if ($func_name === 'empty') {
  656                         if ($parHasFuction && version_compare(PHP_VERSION, '5.5.0', '<')) {
  657                             return '(' . $parameter[ 0 ] . ' === false )';
  658                         } else {
  659                             return $func_name . '(' .
  660                                    str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')';
  661                         }
  662                     } else {
  663                         return $func_name . '(' . $parameter[ 0 ] . ')';
  664                     }
  665                 } else {
  666                     return $name . '(' . implode(',', $parameter) . ')';
  667                 }
  668             } else {
  669                 $this->trigger_template_error("unknown function '{$name}'");
  670             }
  671         }
  672     }
  673 
  674     /**
  675      * This method is called from parser to process a text content section
  676      * - remove text from inheritance child templates as they may generate output
  677      * - strip text if strip is enabled
  678      *
  679      * @param string $text
  680      *
  681      * @return null|\Smarty_Internal_ParseTree_Text
  682      */
  683     public function processText($text)
  684     {
  685         if ((string)$text != '') {
  686             $store = array();
  687             $_store = 0;
  688             if ($this->parser->strip) {
  689                 if (strpos($text, '<') !== false) {
  690                     // capture html elements not to be messed with
  691                     $_offset = 0;
  692                     if (preg_match_all(
  693                         '#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
  694                         $text,
  695                         $matches,
  696                         PREG_OFFSET_CAPTURE | PREG_SET_ORDER
  697                     )
  698                     ) {
  699                         foreach ($matches as $match) {
  700                             $store[] = $match[ 0 ][ 0 ];
  701                             $_length = strlen($match[ 0 ][ 0 ]);
  702                             $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
  703                             $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
  704                             $_offset += $_length - strlen($replace);
  705                             $_store++;
  706                         }
  707                     }
  708                     $expressions = array(// replace multiple spaces between tags by a single space
  709                                          '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s'                            => '\1 \2',
  710                                          // remove newline between tags
  711                                          '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s'                     => '\1\2',
  712                                          // remove multiple spaces between attributes (but not in attribute values!)
  713                                          '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
  714                                          '#>[\040\011]+$#Ss'                                                       => '> ',
  715                                          '#>[\040\011]*[\n]\s*$#Ss'                                                => '>',
  716                                          $this->stripRegEx                                                         => '',
  717                     );
  718                     $text = preg_replace(array_keys($expressions), array_values($expressions), $text);
  719                     $_offset = 0;
  720                     if (preg_match_all(
  721                         '#@!@SMARTY:([0-9]+):SMARTY@!@#is',
  722                         $text,
  723                         $matches,
  724                         PREG_OFFSET_CAPTURE | PREG_SET_ORDER
  725                     )
  726                     ) {
  727                         foreach ($matches as $match) {
  728                             $_length = strlen($match[ 0 ][ 0 ]);
  729                             $replace = $store[ $match[ 1 ][ 0 ] ];
  730                             $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);
  731                             $_offset += strlen($replace) - $_length;
  732                             $_store++;
  733                         }
  734                     }
  735                 } else {
  736                     $text = preg_replace($this->stripRegEx, '', $text);
  737                 }
  738             }
  739             return new Smarty_Internal_ParseTree_Text($text);
  740         }
  741         return null;
  742     }
  743 
  744     /**
  745      * lazy loads internal compile plugin for tag and calls the compile method
  746      * compile objects cached for reuse.
  747      * class name format:  Smarty_Internal_Compile_TagName
  748      * plugin filename format: Smarty_Internal_TagName.php
  749      *
  750      * @param string $tag    tag name
  751      * @param array  $args   list of tag attributes
  752      * @param mixed  $param1 optional parameter
  753      * @param mixed  $param2 optional parameter
  754      * @param mixed  $param3 optional parameter
  755      *
  756      * @return bool|string compiled code or false
  757      * @throws \SmartyCompilerException
  758      */
  759     public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
  760     {
  761         /* @var Smarty_Internal_CompileBase $tagCompiler */
  762         $tagCompiler = $this->getTagCompiler($tag);
  763         // compile this tag
  764         return $tagCompiler === false ? false : $tagCompiler->compile($args, $this, $param1, $param2, $param3);
  765     }
  766 
  767     /**
  768      * lazy loads internal compile plugin for tag compile objects cached for reuse.
  769      *
  770      * class name format:  Smarty_Internal_Compile_TagName
  771      * plugin filename format: Smarty_Internal_TagName.php
  772      *
  773      * @param string $tag tag name
  774      *
  775      * @return bool|\Smarty_Internal_CompileBase tag compiler object or false if not found
  776      */
  777     public function getTagCompiler($tag)
  778     {
  779         // re-use object if already exists
  780         if (!isset(self::$_tag_objects[ $tag ])) {
  781             // lazy load internal compiler plugin
  782             $_tag = explode('_', $tag);
  783             $_tag = array_map('ucfirst', $_tag);
  784             $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag);
  785             if (class_exists($class_name)
  786                 && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))
  787             ) {
  788                 self::$_tag_objects[ $tag ] = new $class_name;
  789             } else {
  790                 self::$_tag_objects[ $tag ] = false;
  791             }
  792         }
  793         return self::$_tag_objects[ $tag ];
  794     }
  795 
  796     /**
  797      * Check for plugins and return function name
  798      *
  799      * @param        $plugin_name
  800      * @param string $plugin_type type of plugin
  801      *
  802      * @return string call name of function
  803      * @throws \SmartyException
  804      */
  805     public function getPlugin($plugin_name, $plugin_type)
  806     {
  807         $function = null;
  808         if ($this->caching && ($this->nocache || $this->tag_nocache)) {
  809             if (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
  810                 $function =
  811                     $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  812             } elseif (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
  813                 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ] =
  814                     $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ];
  815                 $function =
  816                     $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  817             }
  818         } else {
  819             if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
  820                 $function =
  821                     $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  822             } elseif (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
  823                 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ] =
  824                     $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ];
  825                 $function =
  826                     $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  827             }
  828         }
  829         if (isset($function)) {
  830             if ($plugin_type === 'modifier') {
  831                 $this->modifier_plugins[ $plugin_name ] = true;
  832             }
  833             return $function;
  834         }
  835         // loop through plugin dirs and find the plugin
  836         $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
  837         $file = $this->smarty->loadPlugin($function, false);
  838         if (is_string($file)) {
  839             if ($this->caching && ($this->nocache || $this->tag_nocache)) {
  840                 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
  841                     $file;
  842                 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
  843                     $function;
  844             } else {
  845                 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
  846                     $file;
  847                 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
  848                     $function;
  849             }
  850             if ($plugin_type === 'modifier') {
  851                 $this->modifier_plugins[ $plugin_name ] = true;
  852             }
  853             return $function;
  854         }
  855         if (is_callable($function)) {
  856             // plugin function is defined in the script
  857             return $function;
  858         }
  859         return false;
  860     }
  861 
  862     /**
  863      * Check for plugins by default plugin handler
  864      *
  865      * @param string $tag         name of tag
  866      * @param string $plugin_type type of plugin
  867      *
  868      * @return bool true if found
  869      * @throws \SmartyCompilerException
  870      */
  871     public function getPluginFromDefaultHandler($tag, $plugin_type)
  872     {
  873         $callback = null;
  874         $script = null;
  875         $cacheable = true;
  876         $result = call_user_func_array(
  877             $this->smarty->default_plugin_handler_func,
  878             array(
  879                 $tag,
  880                 $plugin_type,
  881                 $this->template,
  882                 &$callback,
  883                 &$script,
  884                 &$cacheable,
  885             )
  886         );
  887         if ($result) {
  888             $this->tag_nocache = $this->tag_nocache || !$cacheable;
  889             if ($script !== null) {
  890                 if (is_file($script)) {
  891                     if ($this->caching && ($this->nocache || $this->tag_nocache)) {
  892                         $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'file' ] =
  893                             $script;
  894                         $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'function' ] =
  895                             $callback;
  896                     } else {
  897                         $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'file' ] =
  898                             $script;
  899                         $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'function' ] =
  900                             $callback;
  901                     }
  902                     include_once $script;
  903                 } else {
  904                     $this->trigger_template_error("Default plugin handler: Returned script file '{$script}' for '{$tag}' not found");
  905                 }
  906             }
  907             if (is_callable($callback)) {
  908                 $this->default_handler_plugins[ $plugin_type ][ $tag ] = array(
  909                     $callback,
  910                     true,
  911                     array()
  912                 );
  913                 return true;
  914             } else {
  915                 $this->trigger_template_error("Default plugin handler: Returned callback for '{$tag}' not callable");
  916             }
  917         }
  918         return false;
  919     }
  920 
  921     /**
  922      * Append code segments and remove unneeded ?> <?php transitions
  923      *
  924      * @param string $left
  925      * @param string $right
  926      *
  927      * @return string
  928      */
  929     public function appendCode($left, $right)
  930     {
  931         if (preg_match('/\s*\?>\s?$/D', $left) && preg_match('/^<\?php\s+/', $right)) {
  932             $left = preg_replace('/\s*\?>\s?$/D', "\n", $left);
  933             $left .= preg_replace('/^<\?php\s+/', '', $right);
  934         } else {
  935             $left .= $right;
  936         }
  937         return $left;
  938     }
  939 
  940     /**
  941      * Inject inline code for nocache template sections
  942      * This method gets the content of each template element from the parser.
  943      * If the content is compiled code and it should be not cached the code is injected
  944      * into the rendered output.
  945      *
  946      * @param string  $content content of template element
  947      * @param boolean $is_code true if content is compiled code
  948      *
  949      * @return string  content
  950      */
  951     public function processNocacheCode($content, $is_code)
  952     {
  953         // If the template is not evaluated and we have a nocache section and or a nocache tag
  954         if ($is_code && !empty($content)) {
  955             // generate replacement code
  956             if ((!($this->template->source->handler->recompiled) || $this->forceNocache) && $this->caching
  957                 && !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache)
  958             ) {
  959                 $this->template->compiled->has_nocache_code = true;
  960                 $_output = addcslashes($content, '\'\\');
  961                 $_output = str_replace('^#^', '\'', $_output);
  962                 $_output =
  963                     "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/{$_output}/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
  964                 // make sure we include modifier plugins for nocache code
  965                 foreach ($this->modifier_plugins as $plugin_name => $dummy) {
  966                     if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ])) {
  967                         $this->required_plugins[ 'nocache' ][ $plugin_name ][ 'modifier' ] =
  968                             $this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ];
  969                     }
  970                 }
  971             } else {
  972                 $_output = $content;
  973             }
  974         } else {
  975             $_output = $content;
  976         }
  977         $this->modifier_plugins = array();
  978         $this->suppressNocacheProcessing = false;
  979         $this->tag_nocache = false;
  980         return $_output;
  981     }
  982 
  983     /**
  984      * Get Id
  985      *
  986      * @param string $input
  987      *
  988      * @return bool|string
  989      */
  990     public function getId($input)
  991     {
  992         if (preg_match('~^([\'"]*)([0-9]*[a-zA-Z_]\w*)\1$~', $input, $match)) {
  993             return $match[ 2 ];
  994         }
  995         return false;
  996     }
  997 
  998     /**
  999      * Get variable name from string
 1000      *
 1001      * @param string $input
 1002      *
 1003      * @return bool|string
 1004      */
 1005     public function getVariableName($input)
 1006     {
 1007         if (preg_match('~^[$]_smarty_tpl->tpl_vars\[[\'"]*([0-9]*[a-zA-Z_]\w*)[\'"]*\]->value$~', $input, $match)) {
 1008             return $match[ 1 ];
 1009         }
 1010         return false;
 1011     }
 1012 
 1013     /**
 1014      * Set nocache flag in variable or create new variable
 1015      *
 1016      * @param string $varName
 1017      */
 1018     public function setNocacheInVariable($varName)
 1019     {
 1020         // create nocache var to make it know for further compiling
 1021         if ($_var = $this->getId($varName)) {
 1022             if (isset($this->template->tpl_vars[ $_var ])) {
 1023                 $this->template->tpl_vars[ $_var ] = clone $this->template->tpl_vars[ $_var ];
 1024                 $this->template->tpl_vars[ $_var ]->nocache = true;
 1025             } else {
 1026                 $this->template->tpl_vars[ $_var ] = new Smarty_Variable(null, true);
 1027             }
 1028         }
 1029     }
 1030 
 1031     /**
 1032      * @param array $_attr tag attributes
 1033      * @param array $validScopes
 1034      *
 1035      * @return int|string
 1036      * @throws \SmartyCompilerException
 1037      */
 1038     public function convertScope($_attr, $validScopes)
 1039     {
 1040         $_scope = 0;
 1041         if (isset($_attr[ 'scope' ])) {
 1042             $_scopeName = trim($_attr[ 'scope' ], '\'"');
 1043             if (is_numeric($_scopeName) && in_array($_scopeName, $validScopes)) {
 1044                 $_scope = $_scopeName;
 1045             } elseif (is_string($_scopeName)) {
 1046                 $_scopeName = trim($_scopeName, '\'"');
 1047                 $_scope = isset($validScopes[ $_scopeName ]) ? $validScopes[ $_scopeName ] : false;
 1048             } else {
 1049                 $_scope = false;
 1050             }
 1051             if ($_scope === false) {
 1052                 $err = var_export($_scopeName, true);
 1053                 $this->trigger_template_error("illegal value '{$err}' for \"scope\" attribute", null, true);
 1054             }
 1055         }
 1056         return $_scope;
 1057     }
 1058 
 1059     /**
 1060      * Generate nocache code string
 1061      *
 1062      * @param string $code PHP code
 1063      *
 1064      * @return string
 1065      */
 1066     public function makeNocacheCode($code)
 1067     {
 1068         return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " .
 1069                str_replace('^#^', '\'', addcslashes($code, '\'\\')) .
 1070                "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n";
 1071     }
 1072 
 1073     /**
 1074      * display compiler error messages without dying
 1075      * If parameter $args is empty it is a parser detected syntax error.
 1076      * In this case the parser is called to obtain information about expected tokens.
 1077      * If parameter $args contains a string this is used as error message
 1078      *
 1079      * @param string    $args    individual error message or null
 1080      * @param string    $line    line-number
 1081      * @param null|bool $tagline if true the line number of last tag
 1082      *
 1083      * @throws \SmartyCompilerException when an unexpected token is found
 1084      */
 1085     public function trigger_template_error($args = null, $line = null, $tagline = null)
 1086     {
 1087         $lex = $this->parser->lex;
 1088         if ($tagline === true) {
 1089             // get line number of Tag
 1090             $line = $lex->taglineno;
 1091         } elseif (!isset($line)) {
 1092             // get template source line which has error
 1093             $line = $lex->line;
 1094         } else {
 1095             $line = (int)$line;
 1096         }
 1097         if (in_array(
 1098             $this->template->source->type,
 1099             array(
 1100                 'eval',
 1101                 'string'
 1102             )
 1103         )
 1104         ) {
 1105             $templateName = $this->template->source->type . ':' . trim(
 1106                     preg_replace(
 1107                         '![\t\r\n]+!',
 1108                         ' ',
 1109                         strlen($lex->data) > 40 ?
 1110                             substr($lex->data, 0, 40) .
 1111                             '...' : $lex->data
 1112                     )
 1113                 );
 1114         } else {
 1115             $templateName = $this->template->source->type . ':' . $this->template->source->filepath;
 1116         }
 1117         //        $line += $this->trace_line_offset;
 1118         $match = preg_split("/\n/", $lex->data);
 1119         $error_text =
 1120             'Syntax error in template "' . (empty($this->trace_filepath) ? $templateName : $this->trace_filepath) .
 1121             '"  on line ' . ($line + $this->trace_line_offset) . ' "' .
 1122             trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])) . '" ';
 1123         if (isset($args)) {
 1124             // individual error message
 1125             $error_text .= $args;
 1126         } else {
 1127             $expect = array();
 1128             // expected token from parser
 1129             $error_text .= ' - Unexpected "' . $lex->value . '"';
 1130             if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
 1131                 foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
 1132                     $exp_token = $this->parser->yyTokenName[ $token ];
 1133                     if (isset($lex->smarty_token_names[ $exp_token ])) {
 1134                         // token type from lexer
 1135                         $expect[] = '"' . $lex->smarty_token_names[ $exp_token ] . '"';
 1136                     } else {
 1137                         // otherwise internal token name
 1138                         $expect[] = $this->parser->yyTokenName[ $token ];
 1139                     }
 1140                 }
 1141                 $error_text .= ', expected one of: ' . implode(' , ', $expect);
 1142             }
 1143         }
 1144         if ($this->smarty->_parserdebug) {
 1145             $this->parser->errorRunDown();
 1146             echo ob_get_clean();
 1147             flush();
 1148         }
 1149         $e = new SmartyCompilerException($error_text);
 1150         $e->line = $line;
 1151         $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ]));
 1152         $e->desc = $args;
 1153         $e->template = $this->template->source->filepath;
 1154         throw $e;
 1155     }
 1156 
 1157     /**
 1158      * Return var_export() value with all white spaces removed
 1159      *
 1160      * @param mixed $value
 1161      *
 1162      * @return string
 1163      */
 1164     public function getVarExport($value)
 1165     {
 1166         return preg_replace('/\s/', '', var_export($value, true));
 1167     }
 1168 
 1169     /**
 1170      *  enter double quoted string
 1171      *  - save tag stack count
 1172      */
 1173     public function enterDoubleQuote()
 1174     {
 1175         array_push($this->_tag_stack_count, $this->getTagStackCount());
 1176     }
 1177 
 1178     /**
 1179      * Return tag stack count
 1180      *
 1181      * @return int
 1182      */
 1183     public function getTagStackCount()
 1184     {
 1185         return count($this->_tag_stack);
 1186     }
 1187 
 1188     /**
 1189      * @param $lexerPreg
 1190      *
 1191      * @return mixed
 1192      */
 1193     public function replaceDelimiter($lexerPreg)
 1194     {
 1195         return str_replace(
 1196             array('SMARTYldel', 'SMARTYliteral', 'SMARTYrdel', 'SMARTYautoliteral', 'SMARTYal'),
 1197             array(
 1198                 $this->ldelPreg, $this->literalPreg, $this->rdelPreg,
 1199                 $this->smarty->getAutoLiteral() ? '{1,}' : '{9}',
 1200                 $this->smarty->getAutoLiteral() ? '' : '\\s*'
 1201             ),
 1202             $lexerPreg
 1203         );
 1204     }
 1205 
 1206     /**
 1207      * Build lexer regular expressions for left and right delimiter and user defined literals
 1208      */
 1209     public function initDelimiterPreg()
 1210     {
 1211         $ldel = $this->smarty->getLeftDelimiter();
 1212         $this->ldelLength = strlen($ldel);
 1213         $this->ldelPreg = '';
 1214         foreach (str_split($ldel, 1) as $chr) {
 1215             $this->ldelPreg .= '[' . preg_quote($chr,'/') . ']';
 1216         }
 1217         $rdel = $this->smarty->getRightDelimiter();
 1218         $this->rdelLength = strlen($rdel);
 1219         $this->rdelPreg = '';
 1220         foreach (str_split($rdel, 1) as $chr) {
 1221             $this->rdelPreg .= '[' . preg_quote($chr,'/') . ']';
 1222         }
 1223         $literals = $this->smarty->getLiterals();
 1224         if (!empty($literals)) {
 1225             foreach ($literals as $key => $literal) {
 1226                 $literalPreg = '';
 1227                 foreach (str_split($literal, 1) as $chr) {
 1228                     $literalPreg .= '[' . preg_quote($chr,'/') . ']';
 1229                 }
 1230                 $literals[ $key ] = $literalPreg;
 1231             }
 1232             $this->literalPreg = '|' . implode('|', $literals);
 1233         } else {
 1234             $this->literalPreg = '';
 1235         }
 1236     }
 1237 
 1238     /**
 1239      *  leave double quoted string
 1240      *  - throw exception if block in string was not closed
 1241      *
 1242      * @throws \SmartyCompilerException
 1243      */
 1244     public function leaveDoubleQuote()
 1245     {
 1246         if (array_pop($this->_tag_stack_count) !== $this->getTagStackCount()) {
 1247             $tag = $this->getOpenBlockTag();
 1248             $this->trigger_template_error(
 1249                 "unclosed '{{$tag}}' in doubled quoted string",
 1250                 null,
 1251                 true
 1252             );
 1253         }
 1254     }
 1255 
 1256     /**
 1257      * Get left delimiter preg
 1258      *
 1259      * @return string
 1260      */
 1261     public function getLdelPreg()
 1262     {
 1263         return $this->ldelPreg;
 1264     }
 1265 
 1266     /**
 1267      * Get right delimiter preg
 1268      *
 1269      * @return string
 1270      */
 1271     public function getRdelPreg()
 1272     {
 1273         return $this->rdelPreg;
 1274     }
 1275 
 1276     /**
 1277      * Get length of left delimiter
 1278      *
 1279      * @return int
 1280      */
 1281     public function getLdelLength()
 1282     {
 1283         return $this->ldelLength;
 1284     }
 1285 
 1286     /**
 1287      * Get length of right delimiter
 1288      *
 1289      * @return int
 1290      */
 1291     public function getRdelLength()
 1292     {
 1293         return $this->rdelLength;
 1294     }
 1295 
 1296     /**
 1297      * Get name of current open block tag
 1298      *
 1299      * @return string|boolean
 1300      */
 1301     public function getOpenBlockTag()
 1302     {
 1303         $tagCount = $this->getTagStackCount();
 1304         if ($tagCount) {
 1305             return $this->_tag_stack[ $tagCount - 1 ][ 0 ];
 1306         } else {
 1307             return false;
 1308         }
 1309     }
 1310 
 1311     /**
 1312      * Check if $value contains variable elements
 1313      *
 1314      * @param mixed $value
 1315      *
 1316      * @return bool|int
 1317      */
 1318     public function isVariable($value)
 1319     {
 1320         if (is_string($value)) {
 1321             return preg_match('/[$(]/', $value);
 1322         }
 1323         if (is_bool($value) || is_numeric($value)) {
 1324             return false;
 1325         }
 1326         if (is_array($value)) {
 1327             foreach ($value as $k => $v) {
 1328                 if ($this->isVariable($k) || $this->isVariable($v)) {
 1329                     return true;
 1330                 }
 1331             }
 1332             return false;
 1333         }
 1334         return false;
 1335     }
 1336 
 1337     /**
 1338      * Get new prefix variable name
 1339      *
 1340      * @return string
 1341      */
 1342     public function getNewPrefixVariable()
 1343     {
 1344         ++self::$prefixVariableNumber;
 1345         return $this->getPrefixVariable();
 1346     }
 1347 
 1348     /**
 1349      * Get current prefix variable name
 1350      *
 1351      * @return string
 1352      */
 1353     public function getPrefixVariable()
 1354     {
 1355         return '$_prefixVariable' . self::$prefixVariableNumber;
 1356     }
 1357 
 1358     /**
 1359      * append  code to prefix buffer
 1360      *
 1361      * @param string $code
 1362      */
 1363     public function appendPrefixCode($code)
 1364     {
 1365         $this->prefix_code[] = $code;
 1366     }
 1367 
 1368     /**
 1369      * get prefix code string
 1370      *
 1371      * @return string
 1372      */
 1373     public function getPrefixCode()
 1374     {
 1375         $code = '';
 1376         $prefixArray = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
 1377         $this->prefixCodeStack[] = array();
 1378         foreach ($prefixArray as $c) {
 1379             $code = $this->appendCode($code, $c);
 1380         }
 1381         $this->prefix_code = array();
 1382         return $code;
 1383     }
 1384 
 1385     /**
 1386      * Save current required plugins
 1387      *
 1388      * @param bool $init if true init required plugins
 1389      */
 1390     public function saveRequiredPlugins($init = false)
 1391     {
 1392         $this->required_plugins_stack[] = $this->required_plugins;
 1393         if ($init) {
 1394             $this->required_plugins = array('compiled' => array(), 'nocache' => array());
 1395         }
 1396     }
 1397 
 1398     /**
 1399      * Restore required plugins
 1400      */
 1401     public function restoreRequiredPlugins()
 1402     {
 1403         $this->required_plugins = array_pop($this->required_plugins_stack);
 1404     }
 1405 
 1406     /**
 1407      * Compile code to call Smarty_Internal_Template::_checkPlugins()
 1408      * for required plugins
 1409      *
 1410      * @return string
 1411      */
 1412     public function compileRequiredPlugins()
 1413     {
 1414         $code = $this->compileCheckPlugins($this->required_plugins[ 'compiled' ]);
 1415         if ($this->caching && !empty($this->required_plugins[ 'nocache' ])) {
 1416             $code .= $this->makeNocacheCode($this->compileCheckPlugins($this->required_plugins[ 'nocache' ]));
 1417         }
 1418         return $code;
 1419     }
 1420 
 1421     /**
 1422      * Compile code to call Smarty_Internal_Template::_checkPlugins
 1423      *   - checks if plugin is callable require otherwise
 1424      *
 1425      * @param $requiredPlugins
 1426      *
 1427      * @return string
 1428      */
 1429     public function compileCheckPlugins($requiredPlugins)
 1430     {
 1431         if (!empty($requiredPlugins)) {
 1432             $plugins = array();
 1433             foreach ($requiredPlugins as $plugin) {
 1434                 foreach ($plugin as $data) {
 1435                     $plugins[] = $data;
 1436                 }
 1437             }
 1438             return '$_smarty_tpl->_checkPlugins(' . $this->getVarExport($plugins) . ');' . "\n";
 1439         } else {
 1440             return '';
 1441         }
 1442     }
 1443 
 1444     /**
 1445      * method to compile a Smarty template
 1446      *
 1447      * @param mixed $_content template source
 1448      * @param bool  $isTemplateSource
 1449      *
 1450      * @return bool true if compiling succeeded, false if it failed
 1451      */
 1452     abstract protected function doCompile($_content, $isTemplateSource = false);
 1453 
 1454     /**
 1455      * Compile Tag
 1456      *
 1457      * @param string $tag       tag name
 1458      * @param array  $args      array with tag attributes
 1459      * @param array  $parameter array with compilation parameter
 1460      *
 1461      * @throws SmartyCompilerException
 1462      * @throws SmartyException
 1463      * @return string compiled code
 1464      */
 1465     private function compileTag2($tag, $args, $parameter)
 1466     {
 1467         $plugin_type = '';
 1468         // $args contains the attributes parsed and compiled by the lexer/parser
 1469         // assume that tag does compile into code, but creates no HTML output
 1470         $this->has_code = true;
 1471         // log tag/attributes
 1472         if (isset($this->smarty->_cache[ 'get_used_tags' ])) {
 1473             $this->template->_cache[ 'used_tags' ][] = array(
 1474                 $tag,
 1475                 $args
 1476             );
 1477         }
 1478         // check nocache option flag
 1479         foreach ($args as $arg) {
 1480             if (!is_array($arg)) {
 1481                 if ($arg === "'nocache'" || $arg === 'nocache') {
 1482                     $this->tag_nocache = true;
 1483                 }
 1484             } else {
 1485                 foreach ($arg as $k => $v) {
 1486                     if (($k === "'nocache'" || $k === 'nocache') && (trim($v, "'\" ") === 'true')) {
 1487                         $this->tag_nocache = true;
 1488                     }
 1489                 }
 1490             }
 1491         }
 1492         // compile the smarty tag (required compile classes to compile the tag are auto loaded)
 1493         if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
 1494             if (isset($this->parent_compiler->tpl_function[ $tag ])
 1495                 || (isset($this->template->smarty->ext->_tplFunction)
 1496                     && $this->template->smarty->ext->_tplFunction->getTplFunction($this->template, $tag) !== false)
 1497             ) {
 1498                 // template defined by {template} tag
 1499                 $args[ '_attr' ][ 'name' ] = "'{$tag}'";
 1500                 $_output = $this->callTagCompiler('call', $args, $parameter);
 1501             }
 1502         }
 1503         if ($_output !== false) {
 1504             if ($_output !== true) {
 1505                 // did we get compiled code
 1506                 if ($this->has_code) {
 1507                     // return compiled code
 1508                     return $_output;
 1509                 }
 1510             }
 1511             // tag did not produce compiled code
 1512             return null;
 1513         } else {
 1514             // map_named attributes
 1515             if (isset($args[ '_attr' ])) {
 1516                 foreach ($args[ '_attr' ] as $key => $attribute) {
 1517                     if (is_array($attribute)) {
 1518                         $args = array_merge($args, $attribute);
 1519                     }
 1520                 }
 1521             }
 1522             // not an internal compiler tag
 1523             if (strlen($tag) < 6 || substr($tag, -5) !== 'close') {
 1524                 // check if tag is a registered object
 1525                 if (isset($this->smarty->registered_objects[ $tag ]) && isset($parameter[ 'object_method' ])) {
 1526                     $method = $parameter[ 'object_method' ];
 1527                     if (!in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])
 1528                         && (empty($this->smarty->registered_objects[ $tag ][ 1 ])
 1529                             || in_array($method, $this->smarty->registered_objects[ $tag ][ 1 ]))
 1530                     ) {
 1531                         return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
 1532                     } elseif (in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])) {
 1533                         return $this->callTagCompiler(
 1534                             'private_object_block_function',
 1535                             $args,
 1536                             $parameter,
 1537                             $tag,
 1538                             $method
 1539                         );
 1540                     } else {
 1541                         // throw exception
 1542                         $this->trigger_template_error(
 1543                             'not allowed method "' . $method . '" in registered object "' .
 1544                             $tag . '"',
 1545                             null,
 1546                             true
 1547                         );
 1548                     }
 1549                 }
 1550                 // check if tag is registered
 1551                 foreach (array(
 1552                     Smarty::PLUGIN_COMPILER,
 1553                     Smarty::PLUGIN_FUNCTION,
 1554                     Smarty::PLUGIN_BLOCK,
 1555                 ) as $plugin_type) {
 1556                     if (isset($this->smarty->registered_plugins[ $plugin_type ][ $tag ])) {
 1557                         // if compiler function plugin call it now
 1558                         if ($plugin_type === Smarty::PLUGIN_COMPILER) {
 1559                             $new_args = array();
 1560                             foreach ($args as $key => $mixed) {
 1561                                 if (is_array($mixed)) {
 1562                                     $new_args = array_merge($new_args, $mixed);
 1563                                 } else {
 1564                                     $new_args[ $key ] = $mixed;
 1565                                 }
 1566                             }
 1567                             if (!$this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 1 ]) {
 1568                                 $this->tag_nocache = true;
 1569                             }
 1570                             return call_user_func_array(
 1571                                 $this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 0 ],
 1572                                 array(
 1573                                     $new_args,
 1574                                     $this
 1575                                 )
 1576                             );
 1577                         }
 1578                         // compile registered function or block function
 1579                         if ($plugin_type === Smarty::PLUGIN_FUNCTION || $plugin_type === Smarty::PLUGIN_BLOCK) {
 1580                             return $this->callTagCompiler(
 1581                                 'private_registered_' . $plugin_type,
 1582                                 $args,
 1583                                 $parameter,
 1584                                 $tag
 1585                             );
 1586                         }
 1587                     }
 1588                 }
 1589                 // check plugins from plugins folder
 1590                 foreach ($this->plugin_search_order as $plugin_type) {
 1591                     if ($plugin_type === Smarty::PLUGIN_COMPILER
 1592                         && $this->smarty->loadPlugin('smarty_compiler_' . $tag)
 1593                         && (!isset($this->smarty->security_policy)
 1594                             || $this->smarty->security_policy->isTrustedTag($tag, $this))
 1595                     ) {
 1596                         $plugin = 'smarty_compiler_' . $tag;
 1597                         if (is_callable($plugin)) {
 1598                             // convert arguments format for old compiler plugins
 1599                             $new_args = array();
 1600                             foreach ($args as $key => $mixed) {
 1601                                 if (is_array($mixed)) {
 1602                                     $new_args = array_merge($new_args, $mixed);
 1603                                 } else {
 1604                                     $new_args[ $key ] = $mixed;
 1605                                 }
 1606                             }
 1607                             return $plugin($new_args, $this->smarty);
 1608                         }
 1609                         if (class_exists($plugin, false)) {
 1610                             $plugin_object = new $plugin;
 1611                             if (method_exists($plugin_object, 'compile')) {
 1612                                 return $plugin_object->compile($args, $this);
 1613                             }
 1614                         }
 1615                         throw new SmartyException("Plugin '{$tag}' not callable");
 1616                     } else {
 1617                         if ($function = $this->getPlugin($tag, $plugin_type)) {
 1618                             if (!isset($this->smarty->security_policy)
 1619                                 || $this->smarty->security_policy->isTrustedTag($tag, $this)
 1620                             ) {
 1621                                 return $this->callTagCompiler(
 1622                                     'private_' . $plugin_type . '_plugin',
 1623                                     $args,
 1624                                     $parameter,
 1625                                     $tag,
 1626                                     $function
 1627                                 );
 1628                             }
 1629                         }
 1630                     }
 1631                 }
 1632                 if (is_callable($this->smarty->default_plugin_handler_func)) {
 1633                     $found = false;
 1634                     // look for already resolved tags
 1635                     foreach ($this->plugin_search_order as $plugin_type) {
 1636                         if (isset($this->default_handler_plugins[ $plugin_type ][ $tag ])) {
 1637                             $found = true;
 1638                             break;
 1639                         }
 1640                     }
 1641                     if (!$found) {
 1642                         // call default handler
 1643                         foreach ($this->plugin_search_order as $plugin_type) {
 1644                             if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
 1645                                 $found = true;
 1646                                 break;
 1647                             }
 1648                         }
 1649                     }
 1650                     if ($found) {
 1651                         // if compiler function plugin call it now
 1652                         if ($plugin_type === Smarty::PLUGIN_COMPILER) {
 1653                             $new_args = array();
 1654                             foreach ($args as $key => $mixed) {
 1655                                 if (is_array($mixed)) {
 1656                                     $new_args = array_merge($new_args, $mixed);
 1657                                 } else {
 1658                                     $new_args[ $key ] = $mixed;
 1659                                 }
 1660                             }
 1661                             return call_user_func_array(
 1662                                 $this->default_handler_plugins[ $plugin_type ][ $tag ][ 0 ],
 1663                                 array(
 1664                                     $new_args,
 1665                                     $this
 1666                                 )
 1667                             );
 1668                         } else {
 1669                             return $this->callTagCompiler(
 1670                                 'private_registered_' . $plugin_type,
 1671                                 $args,
 1672                                 $parameter,
 1673                                 $tag
 1674                             );
 1675                         }
 1676                     }
 1677                 }
 1678             } else {
 1679                 // compile closing tag of block function
 1680                 $base_tag = substr($tag, 0, -5);
 1681                 // check if closing tag is a registered object
 1682                 if (isset($this->smarty->registered_objects[ $base_tag ]) && isset($parameter[ 'object_method' ])) {
 1683                     $method = $parameter[ 'object_method' ];
 1684                     if (in_array($method, $this->smarty->registered_objects[ $base_tag ][ 3 ])) {
 1685                         return $this->callTagCompiler(
 1686                             'private_object_block_function',
 1687                             $args,
 1688                             $parameter,
 1689                             $tag,
 1690                             $method
 1691                         );
 1692                     } else {
 1693                         // throw exception
 1694                         $this->trigger_template_error(
 1695                             'not allowed closing tag method "' . $method .
 1696                             '" in registered object "' . $base_tag . '"',
 1697                             null,
 1698                             true
 1699                         );
 1700                     }
 1701                 }
 1702                 // registered block tag ?
 1703                 if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
 1704                     || isset($this->default_handler_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
 1705                 ) {
 1706                     return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
 1707                 }
 1708                 // registered function tag ?
 1709                 if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_FUNCTION ][ $tag ])) {
 1710                     return $this->callTagCompiler('private_registered_function', $args, $parameter, $tag);
 1711                 }
 1712                 // block plugin?
 1713                 if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
 1714                     return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
 1715                 }
 1716                 // function plugin?
 1717                 if ($function = $this->getPlugin($tag, Smarty::PLUGIN_FUNCTION)) {
 1718                     if (!isset($this->smarty->security_policy)
 1719                         || $this->smarty->security_policy->isTrustedTag($tag, $this)
 1720                     ) {
 1721                         return $this->callTagCompiler('private_function_plugin', $args, $parameter, $tag, $function);
 1722                     }
 1723                 }
 1724                 // registered compiler plugin ?
 1725                 if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ])) {
 1726                     // if compiler function plugin call it now
 1727                     $args = array();
 1728                     if (!$this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 1 ]) {
 1729                         $this->tag_nocache = true;
 1730                     }
 1731                     return call_user_func_array(
 1732                         $this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 0 ],
 1733                         array(
 1734                             $args,
 1735                             $this
 1736                         )
 1737                     );
 1738                 }
 1739                 if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
 1740                     $plugin = 'smarty_compiler_' . $tag;
 1741                     if (is_callable($plugin)) {
 1742                         return $plugin($args, $this->smarty);
 1743                     }
 1744                     if (class_exists($plugin, false)) {
 1745                         $plugin_object = new $plugin;
 1746                         if (method_exists($plugin_object, 'compile')) {
 1747                             return $plugin_object->compile($args, $this);
 1748                         }
 1749                     }
 1750                     throw new SmartyException("Plugin '{$tag}' not callable");
 1751                 }
 1752             }
 1753             $this->trigger_template_error("unknown tag '{$tag}'", null, true);
 1754         }
 1755     }
 1756 }