library/smarty/libs/sysplugins/smarty_internal_template.php
changeset 0 4869aea77e21
equal deleted inserted replaced
-1:000000000000 0:4869aea77e21
       
     1 <?php
       
     2 /**
       
     3  * Smarty Internal Plugin Template
       
     4  * This file contains the Smarty template engine
       
     5  *
       
     6  * @package    Smarty
       
     7  * @subpackage Template
       
     8  * @author     Uwe Tews
       
     9  */
       
    10 
       
    11 /**
       
    12  * Main class with template data structures and methods
       
    13  *
       
    14  * @package    Smarty
       
    15  * @subpackage Template
       
    16  *
       
    17  * @property Smarty_Template_Source $source
       
    18  * @property Smarty_Template_Compiled $compiled
       
    19  * @property Smarty_Template_Cached $cached
       
    20  */
       
    21 class Smarty_Internal_Template extends Smarty_Internal_TemplateBase {
       
    22     /**
       
    23      * Global smarty instance
       
    24      *
       
    25      * @var Smarty
       
    26      */
       
    27     public $smarty = null;
       
    28 
       
    29     /**
       
    30      * Template resource
       
    31      *
       
    32      * @var string
       
    33      */
       
    34     public $template_resource = null;
       
    35     /**
       
    36      * Saved template Id
       
    37      *
       
    38      * @var null|string
       
    39      */
       
    40     public $templateId = null;
       
    41     /**
       
    42      * flag if compiled template is invalid and must be (re)compiled
       
    43      *
       
    44      * @var bool
       
    45      */
       
    46     public $mustCompile = null;
       
    47     /**
       
    48      * flag if template does contain nocache code sections
       
    49      *
       
    50      * @var bool
       
    51      */
       
    52     public $has_nocache_code = false;
       
    53     /**
       
    54      * special compiled and cached template properties
       
    55      *
       
    56      * @var array
       
    57      */
       
    58     public $properties = array('file_dependency' => array(),
       
    59         'nocache_hash' => '',
       
    60         'tpl_function' => array(),
       
    61     );
       
    62     /**
       
    63      * required plugins
       
    64      *
       
    65      * @var array
       
    66      */
       
    67     public $required_plugins = array('compiled' => array(), 'nocache' => array());
       
    68     /**
       
    69      * blocks for template inheritance
       
    70      *
       
    71      * @var array
       
    72      */
       
    73     public $block_data = array();
       
    74     /**
       
    75      * variable filters
       
    76      *
       
    77      * @var array
       
    78      */
       
    79     public $variable_filters = array();
       
    80     /**
       
    81      * optional log of tag/attributes
       
    82      *
       
    83      * @var array
       
    84      */
       
    85     public $used_tags = array();
       
    86     /**
       
    87      * internal flag to allow relative path in child template blocks
       
    88      *
       
    89      * @var bool
       
    90      */
       
    91     public $allow_relative_path = false;
       
    92     /**
       
    93      * internal capture runtime stack
       
    94      *
       
    95      * @var array
       
    96      */
       
    97     public $_capture_stack = array(0 => array());
       
    98 
       
    99     /**
       
   100      * Create template data object
       
   101      * Some of the global Smarty settings copied to template scope
       
   102      * It load the required template resources and caching plugins
       
   103      *
       
   104      * @param string $template_resource template resource string
       
   105      * @param Smarty $smarty Smarty instance
       
   106      * @param Smarty_Internal_Template $_parent back pointer to parent object with variables or null
       
   107      * @param mixed $_cache_id cache   id or null
       
   108      * @param mixed $_compile_id compile id or null
       
   109      * @param bool $_caching use caching?
       
   110      * @param int $_cache_lifetime cache life-time in seconds
       
   111      */
       
   112     public function __construct($template_resource, $smarty, $_parent = null, $_cache_id = null, $_compile_id = null, $_caching = null, $_cache_lifetime = null) {
       
   113         $this->smarty = &$smarty;
       
   114         // Smarty parameter
       
   115         $this->cache_id = $_cache_id === null ? $this->smarty->cache_id : $_cache_id;
       
   116         $this->compile_id = $_compile_id === null ? $this->smarty->compile_id : $_compile_id;
       
   117         $this->caching = $_caching === null ? $this->smarty->caching : $_caching;
       
   118         if ($this->caching === true) {
       
   119             $this->caching = Smarty::CACHING_LIFETIME_CURRENT;
       
   120         }
       
   121         $this->cache_lifetime = $_cache_lifetime === null ? $this->smarty->cache_lifetime : $_cache_lifetime;
       
   122         $this->parent = $_parent;
       
   123         // Template resource
       
   124         $this->template_resource = $template_resource;
       
   125         // copy block data of template inheritance
       
   126         if ($this->parent instanceof Smarty_Internal_Template) {
       
   127             $this->block_data = $this->parent->block_data;
       
   128         }
       
   129     }
       
   130 
       
   131     /**
       
   132      * fetches rendered template
       
   133      *
       
   134      * @throws Exception
       
   135      * @throws SmartyException
       
   136      * @return string rendered template output
       
   137      */
       
   138     public function fetch() {
       
   139         return $this->render(true, false, false);
       
   140     }
       
   141 
       
   142     /**
       
   143      * displays a Smarty template
       
   144      */
       
   145     public function display() {
       
   146         $this->render(true, false, true);
       
   147     }
       
   148 
       
   149     /**
       
   150      * render template
       
   151      *
       
   152      * @param  bool $merge_tpl_vars if true parent template variables merged in to local scope
       
   153      * @param  bool $no_output_filter if true do not run output filter
       
   154      * @param  bool $display true: display, false: fetch null: subtemplate
       
   155      *
       
   156      * @throws Exception
       
   157      * @throws SmartyException
       
   158      * @return string rendered template output
       
   159      */
       
   160     public function render($merge_tpl_vars = false, $no_output_filter = true, $display = null) {
       
   161         $parentIsTpl = $this->parent instanceof Smarty_Internal_Template;
       
   162         if ($this->smarty->debugging) {
       
   163             Smarty_Internal_Debug::start_template($this, $display);
       
   164         }
       
   165         $save_tpl_vars = null;
       
   166         $save_config_vars = null;
       
   167         // merge all variable scopes into template
       
   168         if ($merge_tpl_vars) {
       
   169             // save local variables
       
   170             $save_tpl_vars = $this->tpl_vars;
       
   171             $save_config_vars = $this->config_vars;
       
   172             $ptr_array = array($this);
       
   173             $ptr = $this;
       
   174             while (isset($ptr->parent)) {
       
   175                 $ptr_array[] = $ptr = $ptr->parent;
       
   176             }
       
   177             $ptr_array = array_reverse($ptr_array);
       
   178             $parent_ptr = reset($ptr_array);
       
   179             $tpl_vars = $parent_ptr->tpl_vars;
       
   180             $config_vars = $parent_ptr->config_vars;
       
   181             while ($parent_ptr = next($ptr_array)) {
       
   182                 if (!empty($parent_ptr->tpl_vars)) {
       
   183                     $tpl_vars = array_merge($tpl_vars, $parent_ptr->tpl_vars);
       
   184                 }
       
   185                 if (!empty($parent_ptr->config_vars)) {
       
   186                     $config_vars = array_merge($config_vars, $parent_ptr->config_vars);
       
   187                 }
       
   188             }
       
   189             if (!empty(Smarty::$global_tpl_vars)) {
       
   190                 $tpl_vars = array_merge(Smarty::$global_tpl_vars, $tpl_vars);
       
   191             }
       
   192             $this->tpl_vars = $tpl_vars;
       
   193             $this->config_vars = $config_vars;
       
   194         }
       
   195         // dummy local smarty variable
       
   196         if (!isset($this->tpl_vars['smarty'])) {
       
   197             $this->tpl_vars['smarty'] = new Smarty_Variable;
       
   198         }
       
   199         $_smarty_old_error_level = isset($this->smarty->error_reporting) ? error_reporting($this->smarty->error_reporting) : null;
       
   200         // check URL debugging control
       
   201         if (!$this->smarty->debugging && $this->smarty->debugging_ctrl == 'URL') {
       
   202             Smarty_Internal_Debug::debugUrl($this);
       
   203         }
       
   204         if (!isset($this->source)) {
       
   205             $this->loadSource();
       
   206         }
       
   207         // checks if template exists
       
   208         if (!$this->source->exists) {
       
   209             if ($parentIsTpl) {
       
   210                 $parent_resource = " in '{$this->parent->template_resource}'";
       
   211             } else {
       
   212                 $parent_resource = '';
       
   213             }
       
   214             throw new SmartyException("Unable to load template {$this->source->type} '{$this->source->name}'{$parent_resource}");
       
   215         }
       
   216         // disable caching for evaluated code
       
   217         if ($this->source->recompiled) {
       
   218             $this->caching = false;
       
   219         }
       
   220         // read from cache or render
       
   221         $isCacheTpl = $this->caching == Smarty::CACHING_LIFETIME_CURRENT || $this->caching == Smarty::CACHING_LIFETIME_SAVED;
       
   222         if ($isCacheTpl) {
       
   223             if (!isset($this->cached)) {
       
   224                 $this->loadCached();
       
   225             }
       
   226             $this->cached->isCached($this);
       
   227         }
       
   228         if (!($isCacheTpl) || !$this->cached->valid) {
       
   229             if ($isCacheTpl) {
       
   230                 $this->properties['tpl_function'] = array();
       
   231             }
       
   232             // render template (not loaded and not in cache)
       
   233             if ($this->smarty->debugging) {
       
   234                 Smarty_Internal_Debug::start_render($this);
       
   235             }
       
   236             if (!$this->source->uncompiled) {
       
   237                 // render compiled code
       
   238                 if (!isset($this->compiled)) {
       
   239                     $this->loadCompiled();
       
   240                 }
       
   241                 $content = $this->compiled->render($this);
       
   242             } else {
       
   243                 $content = $this->source->renderUncompiled($this);
       
   244             }
       
   245             if (!$this->source->recompiled && empty($this->properties['file_dependency'][$this->source->uid])) {
       
   246                 $this->properties['file_dependency'][$this->source->uid] = array($this->source->filepath, $this->source->timestamp, $this->source->type);
       
   247             }
       
   248             if ($parentIsTpl) {
       
   249                 $this->parent->properties['file_dependency'] = array_merge($this->parent->properties['file_dependency'], $this->properties['file_dependency']);
       
   250                 //$this->parent->properties['tpl_function'] = array_merge($this->parent->properties['tpl_function'], $this->properties['tpl_function']);
       
   251             }
       
   252             if ($this->smarty->debugging) {
       
   253                 Smarty_Internal_Debug::end_render($this);
       
   254             }
       
   255             // write to cache when necessary
       
   256             if (!$this->source->recompiled && $isCacheTpl) {
       
   257                 if ($this->smarty->debugging) {
       
   258                     Smarty_Internal_Debug::start_cache($this);
       
   259                 }
       
   260                 $this->cached->updateCache($this, $content, $no_output_filter);
       
   261                 $compile_check = $this->smarty->compile_check;
       
   262                 $this->smarty->compile_check = false;
       
   263                 if ($parentIsTpl) {
       
   264                     $this->properties['tpl_function'] = $this->parent->properties['tpl_function'];
       
   265                 }
       
   266                 if (!$this->cached->processed) {
       
   267                     $this->cached->process($this);
       
   268                 }
       
   269                 $this->smarty->compile_check = $compile_check;
       
   270                 $content = $this->getRenderedTemplateCode();
       
   271                 if ($this->smarty->debugging) {
       
   272                     Smarty_Internal_Debug::end_cache($this);
       
   273                 }
       
   274             } else {
       
   275                 if (!empty($this->properties['nocache_hash']) && !empty($this->parent->properties['nocache_hash'])) {
       
   276                     // replace nocache_hash
       
   277                     $content = str_replace("{$this->properties['nocache_hash']}", $this->parent->properties['nocache_hash'], $content);
       
   278                     $this->parent->has_nocache_code = $this->parent->has_nocache_code || $this->has_nocache_code;
       
   279                 }
       
   280             }
       
   281         } else {
       
   282             if ($this->smarty->debugging) {
       
   283                 Smarty_Internal_Debug::start_cache($this);
       
   284             }
       
   285             $content = $this->cached->render($this);
       
   286             if ($this->smarty->debugging) {
       
   287                 Smarty_Internal_Debug::end_cache($this);
       
   288             }
       
   289         }
       
   290         if ((!$this->caching || $this->has_nocache_code || $this->source->recompiled) && !$no_output_filter && (isset($this->smarty->autoload_filters['output']) || isset($this->smarty->registered_filters['output']))) {
       
   291             $content = Smarty_Internal_Filter_Handler::runFilter('output', $content, $this);
       
   292         }
       
   293         if (isset($_smarty_old_error_level)) {
       
   294             error_reporting($_smarty_old_error_level);
       
   295         }
       
   296         // display or fetch
       
   297         if ($display) {
       
   298             if ($this->caching && $this->smarty->cache_modified_check) {
       
   299                 $this->cached->cacheModifiedCheck($this, $content);
       
   300             } else {
       
   301                 echo $content;
       
   302             }
       
   303             if ($this->smarty->debugging) {
       
   304                 Smarty_Internal_Debug::end_template($this);
       
   305             }
       
   306             // debug output
       
   307             if ($this->smarty->debugging) {
       
   308                 Smarty_Internal_Debug::display_debug($this, true);
       
   309             }
       
   310             if ($merge_tpl_vars) {
       
   311                 // restore local variables
       
   312                 $this->tpl_vars = $save_tpl_vars;
       
   313                 $this->config_vars = $save_config_vars;
       
   314             }
       
   315             return '';
       
   316         } else {
       
   317             if ($merge_tpl_vars) {
       
   318                 // restore local variables
       
   319                 $this->tpl_vars = $save_tpl_vars;
       
   320                 $this->config_vars = $save_config_vars;
       
   321             }
       
   322             if ($this->smarty->debugging) {
       
   323                 Smarty_Internal_Debug::end_template($this);
       
   324             }
       
   325             if ($this->smarty->debugging == 2 and $display === false) {
       
   326                 if ($this->smarty->debugging) {
       
   327                     Smarty_Internal_Debug::display_debug($this, true);
       
   328                 }
       
   329             }
       
   330             if ($parentIsTpl) {
       
   331                 $this->parent->properties['tpl_function'] = array_merge($this->parent->properties['tpl_function'], $this->properties['tpl_function']);
       
   332                 foreach ($this->required_plugins as $code => $tmp1) {
       
   333                     foreach ($tmp1 as $name => $tmp) {
       
   334                         foreach ($tmp as $type => $data) {
       
   335                             $this->parent->required_plugins[$code][$name][$type] = $data;
       
   336                         }
       
   337                     }
       
   338                 }
       
   339             }
       
   340             // return cache content
       
   341             return $content;
       
   342         }
       
   343     }
       
   344 
       
   345     /**
       
   346      * get rendered template content by calling compiled or cached template code
       
   347      *
       
   348      * @return string
       
   349      * @throws Exception
       
   350      */
       
   351     public function getRenderedTemplateCode() {
       
   352         $level = ob_get_level();
       
   353         try {
       
   354             ob_start();
       
   355             if (empty($this->properties['unifunc']) || !is_callable($this->properties['unifunc'])) {
       
   356                 throw new SmartyException("Invalid compiled template for '{$this->template_resource}'");
       
   357             }
       
   358             if (isset($this->smarty->security_policy)) {
       
   359                 $this->smarty->security_policy->startTemplate($this);
       
   360             }
       
   361             array_unshift($this->_capture_stack, array());
       
   362             //
       
   363             // render compiled or saved template code
       
   364             //
       
   365             $this->properties['unifunc']($this);
       
   366             // any unclosed {capture} tags ?
       
   367             if (isset($this->_capture_stack[0][0])) {
       
   368                 $this->capture_error();
       
   369             }
       
   370             array_shift($this->_capture_stack);
       
   371             if (isset($this->smarty->security_policy)) {
       
   372                 $this->smarty->security_policy->exitTemplate($this);
       
   373             }
       
   374             return ob_get_clean();
       
   375         } catch (Exception $e) {
       
   376             while (ob_get_level() > $level) {
       
   377                 ob_end_clean();
       
   378             }
       
   379             throw $e;
       
   380         }
       
   381     }
       
   382 
       
   383     /**
       
   384      * Returns if the current template must be compiled by the Smarty compiler
       
   385      * It does compare the timestamps of template source and the compiled templates and checks the force compile
       
   386      * configuration
       
   387      *
       
   388      * @throws SmartyException
       
   389      * @return boolean true if the template must be compiled
       
   390      */
       
   391     public function mustCompile() {
       
   392         if (!$this->source->exists) {
       
   393             if ($this->parent instanceof Smarty_Internal_Template) {
       
   394                 $parent_resource = " in '$this->parent->template_resource}'";
       
   395             } else {
       
   396                 $parent_resource = '';
       
   397             }
       
   398             throw new SmartyException("Unable to load template {$this->source->type} '{$this->source->name}'{$parent_resource}");
       
   399         }
       
   400         if ($this->mustCompile === null) {
       
   401             $this->mustCompile = (!$this->source->uncompiled && ($this->smarty->force_compile || $this->source->recompiled || $this->compiled->timestamp === false ||
       
   402                     ($this->smarty->compile_check && $this->compiled->timestamp < $this->source->timestamp)));
       
   403         }
       
   404 
       
   405         return $this->mustCompile;
       
   406     }
       
   407 
       
   408     /**
       
   409      * Compiles the template
       
   410      * If the template is not evaluated the compiled template is saved on disk
       
   411      */
       
   412     public function compileTemplateSource() {
       
   413         return $this->compiled->compileTemplateSource($this);
       
   414     }
       
   415 
       
   416     /**
       
   417      * Writes the content to cache resource
       
   418      *
       
   419      * @param string $content
       
   420      *
       
   421      * @return bool
       
   422      */
       
   423     public function writeCachedContent($content) {
       
   424         return $this->cached->writeCachedContent($this, $content);
       
   425     }
       
   426 
       
   427     /**
       
   428      * Template code runtime function to get subtemplate content
       
   429      *
       
   430      * @param string $template the resource handle of the template file
       
   431      * @param mixed $cache_id cache id to be used with this template
       
   432      * @param mixed $compile_id compile id to be used with this template
       
   433      * @param integer $caching cache mode
       
   434      * @param integer $cache_lifetime life time of cache data
       
   435      * @param         $data
       
   436      * @param int $parent_scope scope in which {include} should execute
       
   437      *
       
   438      * @returns string template content
       
   439      */
       
   440     public function getSubTemplate($template, $cache_id, $compile_id, $caching, $cache_lifetime, $data, $parent_scope) {
       
   441         $tpl = $this->setupSubTemplate($template, $cache_id, $compile_id, $caching, $cache_lifetime, $data, $parent_scope);
       
   442         return $tpl->render();
       
   443     }
       
   444 
       
   445     /**
       
   446      * Template code runtime function to set up an inline subtemplate
       
   447      *
       
   448      * @param string $template the resource handle of the template file
       
   449      * @param mixed $cache_id cache id to be used with this template
       
   450      * @param mixed $compile_id compile id to be used with this template
       
   451      * @param integer $caching cache mode
       
   452      * @param integer $cache_lifetime life time of cache data
       
   453      * @param array $data passed parameter template variables
       
   454      * @param int $parent_scope scope in which {include} should execute
       
   455      *
       
   456      * @returns object template object
       
   457      */
       
   458     public function setupSubTemplate($template, $cache_id, $compile_id, $caching, $cache_lifetime, $data, $parent_scope) {
       
   459         $_templateId = $this->getTemplateId($template, $cache_id, $compile_id);
       
   460         // already in template cache?
       
   461         if (isset($this->smarty->template_objects[$_templateId])) {
       
   462             // clone cached template object because of possible recursive call
       
   463             $tpl = clone $this->smarty->template_objects[$_templateId];
       
   464             $tpl->parent = $this;
       
   465             if ((bool)$tpl->caching !== (bool)$caching) {
       
   466                 unset($tpl->compiled);
       
   467             }
       
   468             $tpl->caching = $caching;
       
   469             $tpl->cache_lifetime = $cache_lifetime;
       
   470         } else {
       
   471             $tpl = new $this->smarty->template_class($template, $this->smarty, $this, $cache_id, $compile_id, $caching, $cache_lifetime);
       
   472             $tpl->templateId = $_templateId;
       
   473         }
       
   474         // get variables from calling scope
       
   475         if ($parent_scope == Smarty::SCOPE_LOCAL) {
       
   476             $tpl->tpl_vars = $this->tpl_vars;
       
   477             $tpl->tpl_vars['smarty'] = clone $this->tpl_vars['smarty'];
       
   478         } elseif ($parent_scope == Smarty::SCOPE_PARENT) {
       
   479             $tpl->tpl_vars = &$this->tpl_vars;
       
   480         } elseif ($parent_scope == Smarty::SCOPE_GLOBAL) {
       
   481             $tpl->tpl_vars = &Smarty::$global_tpl_vars;
       
   482         } elseif (($scope_ptr = $this->getScopePointer($parent_scope)) == null) {
       
   483             $tpl->tpl_vars = &$this->tpl_vars;
       
   484         } else {
       
   485             $tpl->tpl_vars = &$scope_ptr->tpl_vars;
       
   486         }
       
   487         $tpl->config_vars = $this->config_vars;
       
   488         if (!empty($data)) {
       
   489             // set up variable values
       
   490             foreach ($data as $_key => $_val) {
       
   491                 $tpl->tpl_vars[$_key] = new Smarty_Variable($_val);
       
   492             }
       
   493         }
       
   494         $tpl->properties['tpl_function'] = $this->properties['tpl_function'];
       
   495         return $tpl;
       
   496     }
       
   497 
       
   498     /**
       
   499      * Template code runtime function to set up an inline subtemplate
       
   500      *
       
   501      * @param string $template the resource handle of the template file
       
   502      * @param mixed $cache_id cache id to be used with this template
       
   503      * @param mixed $compile_id compile id to be used with this template
       
   504      * @param integer $caching cache mode
       
   505      * @param integer $cache_lifetime life time of cache data
       
   506      * @param array $data passed parameter template variables
       
   507      * @param int $parent_scope scope in which {include} should execute
       
   508      * @param string $hash nocache hash code
       
   509      * @param string $content_func name of content function
       
   510      *
       
   511      * @returns object template content
       
   512      */
       
   513     public function getInlineSubTemplate($template, $cache_id, $compile_id, $caching, $cache_lifetime, $data, $parent_scope, $hash, $content_func) {
       
   514         $tpl = $this->setupSubTemplate($template, $cache_id, $compile_id, $caching, $cache_lifetime, $data, $parent_scope);
       
   515         $tpl->properties['nocache_hash'] = $hash;
       
   516         if (!isset($this->smarty->template_objects[$tpl->templateId])) {
       
   517             $this->smarty->template_objects[$tpl->templateId] = $tpl;
       
   518         }
       
   519         if ($this->smarty->debugging) {
       
   520             Smarty_Internal_Debug::start_template($tpl);
       
   521             Smarty_Internal_Debug::start_render($tpl);
       
   522         }
       
   523         $tpl->properties['unifunc'] = $content_func;
       
   524         $output = $tpl->getRenderedTemplateCode();
       
   525         if ($this->smarty->debugging) {
       
   526             Smarty_Internal_Debug::end_template($tpl);
       
   527             Smarty_Internal_Debug::end_render($tpl);
       
   528         }
       
   529         if (!empty($tpl->properties['file_dependency'])) {
       
   530             $this->properties['file_dependency'] = array_merge($this->properties['file_dependency'], $tpl->properties['file_dependency']);
       
   531         }
       
   532         $this->properties['tpl_function'] = $tpl->properties['tpl_function'];
       
   533         return str_replace($tpl->properties['nocache_hash'], $this->properties['nocache_hash'], $output);
       
   534     }
       
   535 
       
   536     /**
       
   537      * Call template function
       
   538      *
       
   539      * @param string $name template function name
       
   540      * @param object|\Smarty_Internal_Template $_smarty_tpl template object
       
   541      * @param array $params parameter array
       
   542      * @param bool $nocache true if called nocache
       
   543      *
       
   544      * @throws \SmartyException
       
   545      */
       
   546     public function callTemplateFunction($name, Smarty_Internal_Template $_smarty_tpl, $params, $nocache) {
       
   547         if (isset($_smarty_tpl->properties['tpl_function'][$name])) {
       
   548             if (!$_smarty_tpl->caching || ($_smarty_tpl->caching && $nocache)) {
       
   549                 $function = $_smarty_tpl->properties['tpl_function'][$name]['call_name'];
       
   550             } else {
       
   551                 if (isset($_smarty_tpl->properties['tpl_function'][$name]['call_name_caching'])) {
       
   552                     $function = $_smarty_tpl->properties['tpl_function'][$name]['call_name_caching'];
       
   553                 } else {
       
   554                     $function = $_smarty_tpl->properties['tpl_function'][$name]['call_name'];
       
   555                 }
       
   556             }
       
   557             if (function_exists($function)) {
       
   558                 $function ($_smarty_tpl, $params);
       
   559                 return;
       
   560             }
       
   561             // try to load template function dynamically
       
   562             if (Smarty_Internal_Function_Call_Handler::call($name, $_smarty_tpl, $function, $params, $nocache)) {
       
   563                 $function ($_smarty_tpl, $params);
       
   564                 return;
       
   565             }
       
   566         }
       
   567         throw new SmartyException("Unable to find template function '{$name}'");
       
   568     }
       
   569 
       
   570     /**
       
   571      * This function is executed automatically when a compiled or cached template file is included
       
   572      * - Decode saved properties from compiled template and cache files
       
   573      * - Check if compiled or cache file is valid
       
   574      *
       
   575      * @param  array $properties special template properties
       
   576      * @param  bool $cache flag if called from cache file
       
   577      *
       
   578      * @return bool  flag if compiled or cache file is valid
       
   579      */
       
   580     public function decodeProperties($properties, $cache = false) {
       
   581         $properties['version'] = (isset($properties['version'])) ? $properties['version'] : '';
       
   582         $is_valid = true;
       
   583         if (Smarty::SMARTY_VERSION != $properties['version']) {
       
   584             // new version must rebuild
       
   585             $is_valid = false;
       
   586         } elseif ((!$cache && $this->smarty->compile_check || $cache && ($this->smarty->compile_check === true || $this->smarty->compile_check === Smarty::COMPILECHECK_ON)) && !empty($properties['file_dependency'])) {
       
   587             // check file dependencies at compiled code
       
   588             foreach ($properties['file_dependency'] as $_file_to_check) {
       
   589                 if ($_file_to_check[2] == 'file' || $_file_to_check[2] == 'php') {
       
   590                     if ($this->source->filepath == $_file_to_check[0] && isset($this->source->timestamp)) {
       
   591                         // do not recheck current template
       
   592                         $mtime = $this->source->timestamp;
       
   593                     } else {
       
   594                         // file and php types can be checked without loading the respective resource handlers
       
   595                         $mtime = is_file($_file_to_check[0]) ? @filemtime($_file_to_check[0]) : false;
       
   596                     }
       
   597                 } elseif ($_file_to_check[2] == 'string') {
       
   598                     continue;
       
   599                 } else {
       
   600                     $source = Smarty_Resource::source(null, $this->smarty, $_file_to_check[0]);
       
   601                     $mtime = $source->timestamp;
       
   602                 }
       
   603                 if (!$mtime || $mtime > $_file_to_check[1]) {
       
   604                     $is_valid = false;
       
   605                     break;
       
   606                 }
       
   607             }
       
   608         }
       
   609         if ($cache) {
       
   610             // CACHING_LIFETIME_SAVED cache expiry has to be validated here since otherwise we'd define the unifunc
       
   611             if ($this->caching === Smarty::CACHING_LIFETIME_SAVED &&
       
   612                 $properties['cache_lifetime'] >= 0 &&
       
   613                 (time() > ($this->cached->timestamp + $properties['cache_lifetime']))
       
   614             ) {
       
   615                 $is_valid = false;
       
   616             }
       
   617             $this->cached->valid = $is_valid;
       
   618         } else {
       
   619             $this->mustCompile = !$is_valid;
       
   620         }
       
   621         if ($is_valid) {
       
   622             $this->has_nocache_code = $properties['has_nocache_code'];
       
   623             //            $this->properties['nocache_hash'] = $properties['nocache_hash'];
       
   624             if (isset($properties['cache_lifetime'])) {
       
   625                 $this->properties['cache_lifetime'] = $properties['cache_lifetime'];
       
   626             }
       
   627             if (isset($properties['file_dependency'])) {
       
   628                 $this->properties['file_dependency'] = array_merge($this->properties['file_dependency'], $properties['file_dependency']);
       
   629             }
       
   630             if (isset($properties['tpl_function'])) {
       
   631                 $this->properties['tpl_function'] = array_merge($this->properties['tpl_function'], $properties['tpl_function']);
       
   632             }
       
   633             $this->properties['version'] = $properties['version'];
       
   634             $this->properties['unifunc'] = $properties['unifunc'];
       
   635         }
       
   636         return $is_valid;
       
   637     }
       
   638 
       
   639     /**
       
   640      * Template code runtime function to create a local Smarty variable for array assignments
       
   641      *
       
   642      * @param string $tpl_var template variable name
       
   643      * @param bool $nocache cache mode of variable
       
   644      * @param int $scope scope of variable
       
   645      */
       
   646     public function createLocalArrayVariable($tpl_var, $nocache = false, $scope = Smarty::SCOPE_LOCAL) {
       
   647         if (!isset($this->tpl_vars[$tpl_var])) {
       
   648             $this->tpl_vars[$tpl_var] = new Smarty_Variable(array(), $nocache, $scope);
       
   649         } else {
       
   650             $this->tpl_vars[$tpl_var] = clone $this->tpl_vars[$tpl_var];
       
   651             if ($scope != Smarty::SCOPE_LOCAL) {
       
   652                 $this->tpl_vars[$tpl_var]->scope = $scope;
       
   653             }
       
   654             if (!(is_array($this->tpl_vars[$tpl_var]->value) || $this->tpl_vars[$tpl_var]->value instanceof ArrayAccess)) {
       
   655                 settype($this->tpl_vars[$tpl_var]->value, 'array');
       
   656             }
       
   657         }
       
   658     }
       
   659 
       
   660     /**
       
   661      * Template code runtime function to get pointer to template variable array of requested scope
       
   662      *
       
   663      * @param  int $scope requested variable scope
       
   664      *
       
   665      * @return array array of template variables
       
   666      */
       
   667     public function &getScope($scope) {
       
   668         if ($scope == Smarty::SCOPE_PARENT && !empty($this->parent)) {
       
   669             return $this->parent->tpl_vars;
       
   670         } elseif ($scope == Smarty::SCOPE_ROOT && !empty($this->parent)) {
       
   671             $ptr = $this->parent;
       
   672             while (!empty($ptr->parent)) {
       
   673                 $ptr = $ptr->parent;
       
   674             }
       
   675 
       
   676             return $ptr->tpl_vars;
       
   677         } elseif ($scope == Smarty::SCOPE_GLOBAL) {
       
   678             return Smarty::$global_tpl_vars;
       
   679         }
       
   680         $null = null;
       
   681 
       
   682         return $null;
       
   683     }
       
   684 
       
   685     /**
       
   686      * Get parent or root of template parent chain
       
   687      *
       
   688      * @param  int $scope parent or root scope
       
   689      *
       
   690      * @return mixed object
       
   691      */
       
   692     public function getScopePointer($scope) {
       
   693         if ($scope == Smarty::SCOPE_PARENT && !empty($this->parent)) {
       
   694             return $this->parent;
       
   695         } elseif ($scope == Smarty::SCOPE_ROOT && !empty($this->parent)) {
       
   696             $ptr = $this->parent;
       
   697             while (!empty($ptr->parent)) {
       
   698                 $ptr = $ptr->parent;
       
   699             }
       
   700 
       
   701             return $ptr;
       
   702         }
       
   703 
       
   704         return null;
       
   705     }
       
   706 
       
   707     /**
       
   708      * [util function] counts an array, arrayAccess/traversable or PDOStatement object
       
   709      *
       
   710      * @param  mixed $value
       
   711      *
       
   712      * @return int   the count for arrays and objects that implement countable, 1 for other objects that don't, and 0
       
   713      *               for empty elements
       
   714      */
       
   715     public function _count($value) {
       
   716         if (is_array($value) === true || $value instanceof Countable) {
       
   717             return count($value);
       
   718         } elseif ($value instanceof IteratorAggregate) {
       
   719             // Note: getIterator() returns a Traversable, not an Iterator
       
   720             // thus rewind() and valid() methods may not be present
       
   721             return iterator_count($value->getIterator());
       
   722         } elseif ($value instanceof Iterator) {
       
   723             return iterator_count($value);
       
   724         } elseif ($value instanceof PDOStatement) {
       
   725             return $value->rowCount();
       
   726         } elseif ($value instanceof Traversable) {
       
   727             return iterator_count($value);
       
   728         } elseif ($value instanceof ArrayAccess) {
       
   729             if ($value->offsetExists(0)) {
       
   730                 return 1;
       
   731             }
       
   732         } elseif (is_object($value)) {
       
   733             return count($value);
       
   734         }
       
   735 
       
   736         return 0;
       
   737     }
       
   738 
       
   739     /**
       
   740      * runtime error not matching capture tags
       
   741      */
       
   742     public function capture_error() {
       
   743         throw new SmartyException("Not matching {capture} open/close in \"{$this->template_resource}\"");
       
   744     }
       
   745 
       
   746     /**
       
   747      * Empty cache for this template
       
   748      *
       
   749      * @param integer $exp_time expiration time
       
   750      *
       
   751      * @return integer number of cache files deleted
       
   752      */
       
   753     public function clearCache($exp_time = null) {
       
   754         Smarty_CacheResource::invalidLoadedCache($this->smarty);
       
   755 
       
   756         return $this->cached->handler->clear($this->smarty, $this->template_resource, $this->cache_id, $this->compile_id, $exp_time);
       
   757     }
       
   758 
       
   759     /**
       
   760      * Load source resource
       
   761      *
       
   762      * @throws SmartyException
       
   763      */
       
   764     public function loadSource() {
       
   765         $this->source = Smarty_Template_Source::load($this);
       
   766         if ($this->smarty->template_resource_caching && !$this->source->recompiled && isset($this->templateId)) {
       
   767             $this->smarty->template_objects[$this->templateId] = $this;
       
   768         }
       
   769     }
       
   770 
       
   771     /**
       
   772      * Load compiled object
       
   773      *
       
   774      */
       
   775     public function loadCompiled() {
       
   776         if (!isset($this->compiled)) {
       
   777             if (!class_exists('Smarty_Template_Compiled', false)) {
       
   778                 require SMARTY_SYSPLUGINS_DIR . 'smarty_template_compiled.php';
       
   779             }
       
   780             $this->compiled = Smarty_Template_Compiled::load($this);
       
   781         }
       
   782     }
       
   783 
       
   784     /**
       
   785      * Load cached object
       
   786      *
       
   787      */
       
   788     public function loadCached() {
       
   789         if (!isset($this->cached)) {
       
   790             if (!class_exists('Smarty_Template_Cached', false)) {
       
   791                 require SMARTY_SYSPLUGINS_DIR . 'smarty_template_cached.php';
       
   792             }
       
   793             $this->cached = Smarty_Template_Cached::load($this);
       
   794         }
       
   795     }
       
   796 
       
   797     /**
       
   798      * Load compiler object
       
   799      *
       
   800      * @throws \SmartyException
       
   801      */
       
   802     public function loadCompiler() {
       
   803         $this->smarty->loadPlugin($this->source->compiler_class);
       
   804         $this->compiler = new $this->source->compiler_class($this->source->template_lexer_class, $this->source->template_parser_class, $this->smarty);
       
   805     }
       
   806 
       
   807     /**
       
   808      * Handle unknown class methods
       
   809      *
       
   810      * @param string $name unknown method-name
       
   811      * @param array $args argument array
       
   812      *
       
   813      * @return mixed
       
   814      * @throws SmartyException
       
   815      */
       
   816     public function __call($name, $args) {
       
   817         // method of Smarty object?
       
   818         if (method_exists($this->smarty, $name)) {
       
   819             return call_user_func_array(array($this->smarty, $name), $args);
       
   820         }
       
   821         // parent
       
   822         return parent::__call($name, $args);
       
   823     }
       
   824 
       
   825     /**
       
   826      * set Smarty property in template context
       
   827      *
       
   828      * @param string $property_name property name
       
   829      * @param mixed $value value
       
   830      *
       
   831      * @throws SmartyException
       
   832      */
       
   833     public function __set($property_name, $value) {
       
   834         switch ($property_name) {
       
   835             case 'source':
       
   836             case 'compiled':
       
   837             case 'cached':
       
   838             case 'compiler':
       
   839                 $this->$property_name = $value;
       
   840                 return;
       
   841             default:
       
   842                 // Smarty property ?
       
   843                 if (property_exists($this->smarty, $property_name)) {
       
   844                     $this->smarty->$property_name = $value;
       
   845                     return;
       
   846                 }
       
   847         }
       
   848         throw new SmartyException("invalid template property '$property_name'.");
       
   849     }
       
   850 
       
   851     /**
       
   852      * get Smarty property in template context
       
   853      *
       
   854      * @param string $property_name property name
       
   855      *
       
   856      * @return mixed|Smarty_Template_Cached
       
   857      * @throws SmartyException
       
   858      */
       
   859     public function __get($property_name) {
       
   860         switch ($property_name) {
       
   861             case 'source':
       
   862                 $this->loadSource();
       
   863                 return $this->source;
       
   864 
       
   865             case 'compiled':
       
   866                 $this->loadCompiled();
       
   867                 return $this->compiled;
       
   868 
       
   869             case 'cached':
       
   870                 $this->loadCached();
       
   871                 return $this->cached;
       
   872 
       
   873             case 'compiler':
       
   874                 $this->loadCompiler();
       
   875                 return $this->compiler;
       
   876             default:
       
   877                 // Smarty property ?
       
   878                 if (property_exists($this->smarty, $property_name)) {
       
   879                     return $this->smarty->$property_name;
       
   880                 }
       
   881         }
       
   882         throw new SmartyException("template property '$property_name' does not exist.");
       
   883     }
       
   884 
       
   885     /**
       
   886      * Template data object destructor
       
   887      */
       
   888     public function __destruct() {
       
   889         if ($this->smarty->cache_locking && isset($this->cached) && $this->cached->is_locked) {
       
   890             $this->cached->handler->releaseLock($this->smarty, $this->cached);
       
   891         }
       
   892     }
       
   893 }