3 * Smarty Internal Plugin Template
4 * This file contains the Smarty template engine
12 * Main class with template data structures and methods
15 * @subpackage Template
17 * @property Smarty_Template_Compiled $compiled
18 * @property Smarty_Template_Cached $cached
19 * @property Smarty_Internal_TemplateCompilerBase $compiler
21 * The following methods will be dynamically loaded by the extension handler when they are called.
22 * They are located in a corresponding Smarty_Internal_Method_xxxx class
24 * @method bool mustCompile()
26 class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
29 * This object type (Smarty = 1, template = 2, data = 4)
36 * Global smarty instance
40 public $smarty = null;
45 * @var Smarty_Template_Source|Smarty_Template_Config
47 public $source = null;
50 * Inheritance runtime extension
52 * @var Smarty_Internal_Runtime_Inheritance
54 public $inheritance = null;
61 public $template_resource = null;
64 * flag if compiled template is invalid and must be (re)compiled
68 public $mustCompile = null;
75 public $templateId = null;
78 * Scope in which variables shall be assigned
85 * Flag which is set while rending a cache file
89 public $isRenderingCache = false;
92 * Callbacks called before rendering template
96 public $startRenderCallbacks = array();
99 * Callbacks called after rendering template
103 public $endRenderCallbacks = array();
106 * Template object cache
108 * @var Smarty_Internal_Template[]
110 public static $tplObjCache = array();
113 * Template object cache for Smarty::isCached() == true
115 * @var Smarty_Internal_Template[]
117 public static $isCacheTplObj = array();
120 * Subtemplate Info Cache
124 public static $subTplInfo = array();
127 * Create template data object
128 * Some of the global Smarty settings copied to template scope
129 * It load the required template resources and caching plugins
131 * @param string $template_resource template resource string
132 * @param Smarty $smarty Smarty instance
133 * @param null|\Smarty_Internal_Template|\Smarty|\Smarty_Internal_Data $_parent back pointer to parent object
134 * with variables or null
135 * @param mixed $_cache_id cache id or null
136 * @param mixed $_compile_id compile id or null
137 * @param bool|int|null $_caching use caching?
138 * @param int|null $_cache_lifetime cache life-time in seconds
139 * @param bool $_isConfig
141 * @throws \SmartyException
143 public function __construct($template_resource, Smarty $smarty, Smarty_Internal_Data $_parent = null,
144 $_cache_id = null, $_compile_id = null, $_caching = null, $_cache_lifetime = null,
147 $this->smarty = $smarty;
149 $this->cache_id = $_cache_id === null ? $this->smarty->cache_id : $_cache_id;
150 $this->compile_id = $_compile_id === null ? $this->smarty->compile_id : $_compile_id;
151 $this->caching = $_caching === null ? $this->smarty->caching : $_caching;
152 if ($this->caching === true) {
153 $this->caching = Smarty::CACHING_LIFETIME_CURRENT;
155 $this->cache_lifetime = $_cache_lifetime === null ? $this->smarty->cache_lifetime : $_cache_lifetime;
156 $this->parent = $_parent;
158 $this->template_resource = $template_resource;
159 $this->source = $_isConfig ? Smarty_Template_Config::load($this) : Smarty_Template_Source::load($this);
160 parent::__construct();
161 if ($smarty->security_policy && method_exists($smarty->security_policy, 'registerCallBacks')) {
162 $smarty->security_policy->registerCallBacks($this);
169 * @param bool $no_output_filter if true do not run output filter
170 * @param null|bool $display true: display, false: fetch null: sub-template
173 * @throws \SmartyException
175 public function render($no_output_filter = true, $display = null)
177 if ($this->smarty->debugging) {
178 if (!isset($this->smarty->_debug)) {
179 $this->smarty->_debug = new Smarty_Internal_Debug();
181 $this->smarty->_debug->start_template($this, $display);
183 // checks if template exists
184 if (!$this->source->exists) {
185 throw new SmartyException("Unable to load template '{$this->source->type}:{$this->source->name}'" .
186 ($this->_isSubTpl() ? " in '{$this->parent->template_resource}'" : ''));
188 // disable caching for evaluated code
189 if ($this->source->handler->recompiled) {
190 $this->caching = false;
192 // read from cache or render
194 $this->caching == Smarty::CACHING_LIFETIME_CURRENT || $this->caching == Smarty::CACHING_LIFETIME_SAVED;
196 if (!isset($this->cached) || $this->cached->cache_id !== $this->cache_id ||
197 $this->cached->compile_id !== $this->compile_id
199 $this->loadCached(true);
201 $this->cached->render($this, $no_output_filter);
203 if (!isset($this->compiled) || $this->compiled->compile_id !== $this->compile_id) {
204 $this->loadCompiled(true);
206 $this->compiled->render($this);
211 if ($this->caching && $this->smarty->cache_modified_check) {
212 $this->smarty->ext->_cacheModify->cacheModifiedCheck($this->cached, $this,
213 isset($content) ? $content : ob_get_clean());
215 if ((!$this->caching || $this->cached->has_nocache_code || $this->source->handler->recompiled) &&
216 !$no_output_filter && (isset($this->smarty->autoload_filters[ 'output' ]) ||
217 isset($this->smarty->registered_filters[ 'output' ]))
219 echo $this->smarty->ext->_filterHandler->runFilter('output', ob_get_clean(), $this);
224 if ($this->smarty->debugging) {
225 $this->smarty->_debug->end_template($this);
227 $this->smarty->_debug->display_debug($this, true);
231 if ($this->smarty->debugging) {
232 $this->smarty->_debug->end_template($this);
233 if ($this->smarty->debugging === 2 && $display === false) {
234 $this->smarty->_debug->display_debug($this, true);
237 if ($this->_isSubTpl()) {
238 foreach ($this->compiled->required_plugins as $code => $tmp1) {
239 foreach ($tmp1 as $name => $tmp) {
240 foreach ($tmp as $type => $data) {
241 $this->parent->compiled->required_plugins[ $code ][ $name ][ $type ] = $data;
246 if (!$no_output_filter &&
247 (!$this->caching || $this->cached->has_nocache_code || $this->source->handler->recompiled) &&
248 (isset($this->smarty->autoload_filters[ 'output' ]) ||
249 isset($this->smarty->registered_filters[ 'output' ]))
251 return $this->smarty->ext->_filterHandler->runFilter('output', ob_get_clean(), $this);
253 // return cache content
259 * Runtime function to render sub-template
261 * @param string $template template name
262 * @param mixed $cache_id cache id
263 * @param mixed $compile_id compile id
264 * @param integer $caching cache mode
265 * @param integer $cache_lifetime life time of cache data
266 * @param array $data passed parameter template variables
267 * @param int $scope scope in which {include} should execute
268 * @param bool $forceTplCache cache template object
269 * @param string $uid file dependency uid
270 * @param string $content_func function name
273 public function _subTemplateRender($template, $cache_id, $compile_id, $caching, $cache_lifetime, $data, $scope,
274 $forceTplCache, $uid = null, $content_func = null)
277 $tpl->parent = $this;
278 $smarty = &$this->smarty;
279 $_templateId = $smarty->_getTemplateId($template, $cache_id, $compile_id, $caching, $tpl);
281 if (isset($tpl->templateId) ? $tpl->templateId : $tpl->_getTemplateId() != $_templateId) {
282 // already in template cache?
283 if (isset(self::$tplObjCache[ $_templateId ])) {
284 // copy data from cached object
285 $cachedTpl = &self::$tplObjCache[ $_templateId ];
286 $tpl->templateId = $cachedTpl->templateId;
287 $tpl->template_resource = $cachedTpl->template_resource;
288 $tpl->cache_id = $cachedTpl->cache_id;
289 $tpl->compile_id = $cachedTpl->compile_id;
290 $tpl->source = $cachedTpl->source;
291 if (isset($cachedTpl->compiled)) {
292 $tpl->compiled = $cachedTpl->compiled;
294 unset($tpl->compiled);
296 if ($caching != 9999 && isset($cachedTpl->cached)) {
297 $tpl->cached = $cachedTpl->cached;
302 $tpl->templateId = $_templateId;
303 $tpl->template_resource = $template;
304 $tpl->cache_id = $cache_id;
305 $tpl->compile_id = $compile_id;
307 // for inline templates we can get all resource information from file dependency
308 list($filepath, $timestamp, $type) = $tpl->compiled->file_dependency[ $uid ];
309 $tpl->source = new Smarty_Template_Source($smarty, $filepath, $type, $filepath);
310 $tpl->source->filepath = $filepath;
311 $tpl->source->timestamp = $timestamp;
312 $tpl->source->exists = true;
313 $tpl->source->uid = $uid;
315 $tpl->source = Smarty_Template_Source::load($tpl);
316 unset($tpl->compiled);
318 if ($caching != 9999) {
323 // on recursive calls force caching
324 $forceTplCache = true;
326 $tpl->caching = $caching;
327 $tpl->cache_lifetime = $cache_lifetime;
328 // set template scope
329 $tpl->scope = $scope;
330 if (!isset(self::$tplObjCache[ $tpl->templateId ]) && !$tpl->source->handler->recompiled) {
331 // check if template object should be cached
332 if ($forceTplCache || (isset(self::$subTplInfo[ $tpl->template_resource ]) &&
333 self::$subTplInfo[ $tpl->template_resource ] > 1) ||
334 ($tpl->_isSubTpl() && isset(self::$tplObjCache[ $tpl->parent->templateId ]))
336 self::$tplObjCache[ $tpl->templateId ] = $tpl;
341 // set up variable values
342 foreach ($data as $_key => $_val) {
343 $tpl->tpl_vars[ $_key ] = new Smarty_Variable($_val, $this->isRenderingCache);
346 if ($tpl->caching == 9999) {
347 if (!isset($tpl->compiled)) {
348 $this->loadCompiled(true);
350 if ($tpl->compiled->has_nocache_code) {
351 $this->cached->hashes[ $tpl->compiled->nocache_hash ] = true;
354 $tpl->_cache = array();
356 if ($smarty->debugging) {
357 if (!isset($smarty->_debug)) {
358 $smarty->_debug = new Smarty_Internal_Debug();
360 $smarty->_debug->start_template($tpl);
361 $smarty->_debug->start_render($tpl);
363 $tpl->compiled->getRenderedTemplateCode($tpl, $content_func);
364 if ($smarty->debugging) {
365 $smarty->_debug->end_template($tpl);
366 $smarty->_debug->end_render($tpl);
369 if (isset($tpl->compiled)) {
370 $tpl->compiled->render($tpl);
378 * Get called sub-templates and save call count
381 public function _subTemplateRegister()
383 foreach ($this->compiled->includes as $name => $count) {
384 if (isset(self::$subTplInfo[ $name ])) {
385 self::$subTplInfo[ $name ] += $count;
387 self::$subTplInfo[ $name ] = $count;
393 * Check if this is a sub template
395 * @return bool true is sub template
397 public function _isSubTpl()
399 return isset($this->parent) && $this->parent->_isTplObj();
403 * Assign variable in scope
405 * @param string $varName variable name
406 * @param mixed $value value
407 * @param bool $nocache nocache flag
408 * @param int $scope scope into which variable shall be assigned
411 public function _assignInScope($varName, $value, $nocache = false, $scope = 0)
413 if (isset($this->tpl_vars[ $varName ])) {
414 $this->tpl_vars[ $varName ] = clone $this->tpl_vars[ $varName ];
415 $this->tpl_vars[ $varName ]->value = $value;
416 if ($nocache || $this->isRenderingCache) {
417 $this->tpl_vars[ $varName ]->nocache = true;
420 $this->tpl_vars[ $varName ] = new Smarty_Variable($value, $nocache || $this->isRenderingCache);
423 if ($scope > 0 || $this->scope > 0) {
424 $this->smarty->ext->_updateScope->_updateScope($this, $varName, $scope);
430 * This function is executed automatically when a compiled or cached template file is included
431 * - Decode saved properties from compiled template and cache files
432 * - Check if compiled or cache file is valid
434 * @param \Smarty_Internal_Template $tpl
435 * @param array $properties special template properties
436 * @param bool $cache flag if called from cache file
438 * @return bool flag if compiled or cache file is valid
439 * @throws \SmartyException
441 public function _decodeProperties(Smarty_Internal_Template $tpl, $properties, $cache = false)
443 // on cache resources other than file check version stored in cache code
444 if (!isset($properties[ 'version' ]) || Smarty::SMARTY_VERSION !== $properties[ 'version' ]) {
446 $tpl->smarty->clearAllCache();
448 $tpl->smarty->clearCompiledTemplate();
453 if (!empty($properties[ 'file_dependency' ]) &&
454 ((!$cache && $tpl->smarty->compile_check) || $tpl->smarty->compile_check == 1)
456 // check file dependencies at compiled code
457 foreach ($properties[ 'file_dependency' ] as $_file_to_check) {
458 if ($_file_to_check[ 2 ] == 'file' || $_file_to_check[ 2 ] == 'php') {
459 if ($tpl->source->filepath == $_file_to_check[ 0 ]) {
460 // do not recheck current template
462 //$mtime = $tpl->source->getTimeStamp();
464 // file and php types can be checked without loading the respective resource handlers
465 $mtime = is_file($_file_to_check[ 0 ]) ? filemtime($_file_to_check[ 0 ]) : false;
468 $handler = Smarty_Resource::load($tpl->smarty, $_file_to_check[ 2 ]);
469 if ($handler->checkTimestamps()) {
470 $source = Smarty_Template_Source::load($tpl, $tpl->smarty, $_file_to_check[ 0 ]);
471 $mtime = $source->getTimeStamp();
476 if ($mtime === false || $mtime > $_file_to_check[ 1 ]) {
483 // CACHING_LIFETIME_SAVED cache expiry has to be validated here since otherwise we'd define the unifunc
484 if ($tpl->caching === Smarty::CACHING_LIFETIME_SAVED && $properties[ 'cache_lifetime' ] >= 0 &&
485 (time() > ($tpl->cached->timestamp + $properties[ 'cache_lifetime' ]))
489 $tpl->cached->cache_lifetime = $properties[ 'cache_lifetime' ];
490 $tpl->cached->valid = $is_valid;
491 $resource = $tpl->cached;
493 $tpl->mustCompile = !$is_valid;
494 $resource = $tpl->compiled;
495 $resource->includes = isset($properties[ 'includes' ]) ? $properties[ 'includes' ] : array();
498 $resource->unifunc = $properties[ 'unifunc' ];
499 $resource->has_nocache_code = $properties[ 'has_nocache_code' ];
500 // $tpl->compiled->nocache_hash = $properties['nocache_hash'];
501 $resource->file_dependency = $properties[ 'file_dependency' ];
503 return $is_valid && !function_exists($properties[ 'unifunc' ]);
507 * Compiles the template
508 * If the template is not evaluated the compiled template is saved on disk
510 public function compileTemplateSource()
512 return $this->compiled->compileTemplateSource($this);
516 * Writes the content to cache resource
518 * @param string $content
522 public function writeCachedContent($content)
524 return $this->smarty->ext->_updateCache->writeCachedContent($this->cached, $this, $content);
528 * Get unique template id
532 public function _getTemplateId()
534 return isset($this->templateId) ? $this->templateId : $this->templateId =
535 $this->smarty->_getTemplateId($this->template_resource, $this->cache_id, $this->compile_id);
539 * runtime error not matching capture tags
541 public function capture_error()
543 throw new SmartyException("Not matching {capture} open/close in \"{$this->template_resource}\"");
547 * Load compiled object
549 * @param bool $force force new compiled object
551 public function loadCompiled($force = false)
553 if ($force || !isset($this->compiled)) {
554 $this->compiled = Smarty_Template_Compiled::load($this);
561 * @param bool $force force new cached object
563 public function loadCached($force = false)
565 if ($force || !isset($this->cached)) {
566 $this->cached = Smarty_Template_Cached::load($this);
571 * Load inheritance object
574 public function _loadInheritance()
576 if (!isset($this->inheritance)) {
577 $this->inheritance = new Smarty_Internal_Runtime_Inheritance();
582 * Unload inheritance object
585 public function _cleanUp()
587 $this->startRenderCallbacks = array();
588 $this->endRenderCallbacks = array();
589 $this->inheritance = null;
593 * Load compiler object
595 * @throws \SmartyException
597 public function loadCompiler()
599 if (!class_exists($this->source->compiler_class)) {
600 $this->smarty->loadPlugin($this->source->compiler_class);
603 new $this->source->compiler_class($this->source->template_lexer_class, $this->source->template_parser_class,
608 * Handle unknown class methods
610 * @param string $name unknown method-name
611 * @param array $args argument array
614 * @throws SmartyException
616 public function __call($name, $args)
618 // method of Smarty object?
619 if (method_exists($this->smarty, $name)) {
620 return call_user_func_array(array($this->smarty, $name), $args);
623 return parent::__call($name, $args);
627 * set Smarty property in template context
629 * @param string $property_name property name
630 * @param mixed $value value
632 * @throws SmartyException
634 public function __set($property_name, $value)
636 switch ($property_name) {
640 $this->$property_name = $value;
644 if (property_exists($this->smarty, $property_name)) {
645 $this->smarty->$property_name = $value;
649 throw new SmartyException("invalid template property '$property_name'.");
653 * get Smarty property in template context
655 * @param string $property_name property name
657 * @return mixed|Smarty_Template_Cached
658 * @throws SmartyException
660 public function __get($property_name)
662 switch ($property_name) {
664 $this->loadCompiled();
665 return $this->compiled;
669 return $this->cached;
672 $this->loadCompiler();
673 return $this->compiler;
676 if (property_exists($this->smarty, $property_name)) {
677 return $this->smarty->$property_name;
680 throw new SmartyException("template property '$property_name' does not exist.");
684 * Template data object destructor
686 public function __destruct()
688 if ($this->smarty->cache_locking && isset($this->cached) && $this->cached->is_locked) {
689 $this->cached->handler->releaseLock($this->smarty, $this->cached);