includes/classes/smarty/sysplugins/smarty_internal_templatecompilerbase.php line 400

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