From d88175de3e66f28e34a263e333ab28610ede4bbf Mon Sep 17 00:00:00 2001 From: Simon Wisselink Date: Thu, 22 Dec 2022 15:43:20 +0100 Subject: [PATCH] Refactored all _runtime_* by merging them into the proper classes or by transforming them into Runtime Extensions. --- TODO.txt | 4 +- src/Cacheresource/Base.php | 4 +- src/Cacheresource/Custom.php | 6 +- src/Cacheresource/File.php | 121 +++++- src/Cacheresource/KeyValueStore.php | 4 +- src/Compile/BlockClose.php | 1 - src/Compile/Call.php | 4 +- src/Compile/Capture.php | 4 +- src/Compile/CaptureClose.php | 2 +- src/Compile/ConfigLoad.php | 2 +- src/Compile/ForeachSection.php | 2 +- src/Compile/ForeachTag.php | 4 +- src/Compile/FunctionClose.php | 4 +- src/Compile/MakeNocache.php | 2 +- src/{Template => Compiler}/CodeFrame.php | 4 - src/Data.php | 24 -- src/Resource/FilePlugin.php | 2 +- src/Runtime/CaptureRuntime.php | 163 ++++++++ src/Runtime/ForeachRuntime.php | 163 ++++++++ src/Runtime/InheritanceRuntime.php | 250 ++++++++++++ src/Runtime/MakeNocacheRuntime.php | 55 +++ src/Runtime/TplFunctionRuntime.php | 173 ++++++++ .../smarty_internal_runtime_cachemodify.php | 68 ---- ...rty_internal_runtime_cacheresourcefile.php | 139 ------- .../smarty_internal_runtime_capture.php | 168 -------- .../smarty_internal_runtime_filterhandler.php | 44 --- .../smarty_internal_runtime_foreach.php | 162 -------- ...smarty_internal_runtime_getincludepath.php | 181 --------- .../smarty_internal_runtime_inheritance.php | 251 ------------ .../smarty_internal_runtime_make_nocache.php | 54 --- .../smarty_internal_runtime_tplfunction.php | 177 --------- .../smarty_internal_runtime_updatecache.php | 184 --------- .../smarty_internal_runtime_updatescope.php | 115 ------ .../smarty_internal_runtime_writefile.php | 91 ----- src/Smarty.php | 368 +++++++++++++++++- src/Template/smarty_template_cached.php | 139 ++++++- src/Template/smarty_template_compiled.php | 2 +- src/TestInstall.php | 4 +- src/smarty_internal_extension_handler.php | 195 ---------- src/smarty_internal_template.php | 140 ++++++- src/sysplugins/smarty_security.php | 2 +- 41 files changed, 1571 insertions(+), 1911 deletions(-) rename src/{Template => Compiler}/CodeFrame.php (97%) create mode 100644 src/Runtime/CaptureRuntime.php create mode 100644 src/Runtime/ForeachRuntime.php create mode 100644 src/Runtime/InheritanceRuntime.php create mode 100644 src/Runtime/MakeNocacheRuntime.php create mode 100644 src/Runtime/TplFunctionRuntime.php delete mode 100644 src/Runtime/smarty_internal_runtime_cachemodify.php delete mode 100644 src/Runtime/smarty_internal_runtime_cacheresourcefile.php delete mode 100644 src/Runtime/smarty_internal_runtime_capture.php delete mode 100644 src/Runtime/smarty_internal_runtime_filterhandler.php delete mode 100644 src/Runtime/smarty_internal_runtime_foreach.php delete mode 100644 src/Runtime/smarty_internal_runtime_getincludepath.php delete mode 100644 src/Runtime/smarty_internal_runtime_inheritance.php delete mode 100644 src/Runtime/smarty_internal_runtime_make_nocache.php delete mode 100644 src/Runtime/smarty_internal_runtime_tplfunction.php delete mode 100644 src/Runtime/smarty_internal_runtime_updatecache.php delete mode 100644 src/Runtime/smarty_internal_runtime_updatescope.php delete mode 100644 src/Runtime/smarty_internal_runtime_writefile.php delete mode 100644 src/smarty_internal_extension_handler.php diff --git a/TODO.txt b/TODO.txt index dc8f9e6a..26d5cc67 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,3 +1,3 @@ -- [ ] find ->ext-> calls, rewrite and remove first function call param - [ ] review usages of ->_getSmartyObj and ->smarty: maybe change this so we can hide more -- [ ] ->_objType and ->objMap \ No newline at end of file +- [ ] ->_objType and ->objMap +- [ ] refactor _isTplObj, _isDataObj diff --git a/src/Cacheresource/Base.php b/src/Cacheresource/Base.php index 93fb17d5..33a9070b 100644 --- a/src/Cacheresource/Base.php +++ b/src/Cacheresource/Base.php @@ -71,7 +71,7 @@ abstract class Base * * @return boolean success */ - abstract public function writeCachedContent(Smarty_Internal_Template $_template, $content); + abstract public function storeCachedContent(Smarty_Internal_Template $_template, $content); /** * Read cached template from cache @@ -80,7 +80,7 @@ abstract class Base * * @return string content */ - abstract public function readCachedContent(Smarty_Internal_Template $_template); + abstract public function retrieveCachedContent(Smarty_Internal_Template $_template); /** * Return cached content diff --git a/src/Cacheresource/Custom.php b/src/Cacheresource/Custom.php index 1555c644..4f28f933 100644 --- a/src/Cacheresource/Custom.php +++ b/src/Cacheresource/Custom.php @@ -9,7 +9,7 @@ namespace Smarty\Cacheresource; * @subpackage Cacher */ -use Smarty\Cacheresource\Base; +use Smarty_Internal_Template; /** * Cache Handler API @@ -171,7 +171,7 @@ abstract class Custom extends Base * * @return boolean success */ - public function writeCachedContent(Smarty_Internal_Template $_template, $content) + public function storeCachedContent(Smarty_Internal_Template $_template, $content) { return $this->save( $_template->cached->filepath, @@ -190,7 +190,7 @@ abstract class Custom extends Base * * @return string|boolean content */ - public function readCachedContent(Smarty_Internal_Template $_template) + public function retrieveCachedContent(Smarty_Internal_Template $_template) { $content = $_template->cached->content ?: null; if ($content === null) { diff --git a/src/Cacheresource/File.php b/src/Cacheresource/File.php index 28a1aee7..0ff1fc4c 100644 --- a/src/Cacheresource/File.php +++ b/src/Cacheresource/File.php @@ -124,14 +124,9 @@ class File extends Base * @return bool success * @throws \SmartyException */ - public function writeCachedContent(Smarty_Internal_Template $_template, $content) + public function storeCachedContent(Smarty_Internal_Template $_template, $content) { - if ($_template->smarty->ext->_writeFile->writeFile( - $_template->cached->filepath, - $content, - $_template->smarty - ) === true - ) { + if ($_template->smarty->writeFile($_template->cached->filepath, $content) === true) { if (function_exists('opcache_invalidate') && (!function_exists('ini_get') || strlen(ini_get('opcache.restrict_api'))) < 1 ) { @@ -156,7 +151,7 @@ class File extends Base * * @return string content */ - public function readCachedContent(Smarty_Internal_Template $_template) + public function retrieveCachedContent(Smarty_Internal_Template $_template) { if (is_file($_template->cached->filepath)) { return file_get_contents($_template->cached->filepath); @@ -174,7 +169,7 @@ class File extends Base */ public function clearAll(Smarty $smarty, $exp_time = null) { - return $smarty->ext->_cacheResourceFile->clear($smarty, null, null, null, $exp_time); + return $this->clear($smarty, null, null, null, $exp_time); } /** @@ -190,7 +185,113 @@ class File extends Base */ public function clear(Smarty $smarty, $resource_name, $cache_id, $compile_id, $exp_time) { - return $smarty->ext->_cacheResourceFile->clear($smarty, $resource_name, $cache_id, $compile_id, $exp_time); + $_cache_id = isset($cache_id) ? preg_replace('![^\w\|]+!', '_', $cache_id) : null; + $_compile_id = isset($compile_id) ? preg_replace('![^\w]+!', '_', $compile_id) : null; + $_dir_sep = $smarty->use_sub_dirs ? '/' : '^'; + $_compile_id_offset = $smarty->use_sub_dirs ? 3 : 0; + $_dir = $smarty->getCacheDir(); + if ($_dir === '/') { //We should never want to delete this! + return 0; + } + $_dir_length = strlen($_dir); + if (isset($_cache_id)) { + $_cache_id_parts = explode('|', $_cache_id); + $_cache_id_parts_count = count($_cache_id_parts); + if ($smarty->use_sub_dirs) { + foreach ($_cache_id_parts as $id_part) { + $_dir .= $id_part . '/'; + } + } + } + if (isset($resource_name)) { + $_save_stat = $smarty->caching; + $smarty->caching = \Smarty\Smarty::CACHING_LIFETIME_CURRENT; + $tpl = new $smarty->template_class($resource_name, $smarty); + $smarty->caching = $_save_stat; + // remove from template cache + $tpl->source; // have the template registered before unset() + if ($tpl->source->exists) { + $_resourcename_parts = basename(str_replace('^', '/', $tpl->cached->filepath)); + } else { + return 0; + } + } + $_count = 0; + $_time = time(); + if (file_exists($_dir)) { + $_cacheDirs = new RecursiveDirectoryIterator($_dir); + $_cache = new RecursiveIteratorIterator($_cacheDirs, RecursiveIteratorIterator::CHILD_FIRST); + foreach ($_cache as $_file) { + if (substr(basename($_file->getPathname()), 0, 1) === '.') { + continue; + } + $_filepath = (string)$_file; + // directory ? + if ($_file->isDir()) { + if (!$_cache->isDot()) { + // delete folder if empty + @rmdir($_file->getPathname()); + } + } else { + // delete only php files + if (substr($_filepath, -4) !== '.php') { + continue; + } + $_parts = explode($_dir_sep, str_replace('\\', '/', substr($_filepath, $_dir_length))); + $_parts_count = count($_parts); + // check name + if (isset($resource_name)) { + if ($_parts[ $_parts_count - 1 ] !== $_resourcename_parts) { + continue; + } + } + // check compile id + if (isset($_compile_id) && (!isset($_parts[ $_parts_count - 2 - $_compile_id_offset ]) + || $_parts[ $_parts_count - 2 - $_compile_id_offset ] !== $_compile_id) + ) { + continue; + } + // check cache id + if (isset($_cache_id)) { + // count of cache id parts + $_parts_count = (isset($_compile_id)) ? $_parts_count - 2 - $_compile_id_offset : + $_parts_count - 1 - $_compile_id_offset; + if ($_parts_count < $_cache_id_parts_count) { + continue; + } + for ($i = 0; $i < $_cache_id_parts_count; $i++) { + if ($_parts[ $i ] !== $_cache_id_parts[ $i ]) { + continue 2; + } + } + } + if (is_file($_filepath)) { + // expired ? + if (isset($exp_time)) { + if ($exp_time < 0) { + preg_match('#\'cache_lifetime\' =>\s*(\d*)#', file_get_contents($_filepath), $match); + if ($_time < (filemtime($_filepath) + $match[ 1 ])) { + continue; + } + } else { + if ($_time - filemtime($_filepath) < $exp_time) { + continue; + } + } + } + $_count += @unlink($_filepath) ? 1 : 0; + if (function_exists('opcache_invalidate') + && (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1) + ) { + opcache_invalidate($_filepath, true); + } elseif (function_exists('apc_delete_file')) { + apc_delete_file($_filepath); + } + } + } + } + } + return $_count; } /** diff --git a/src/Cacheresource/KeyValueStore.php b/src/Cacheresource/KeyValueStore.php index 8ad29ece..9ca05854 100644 --- a/src/Cacheresource/KeyValueStore.php +++ b/src/Cacheresource/KeyValueStore.php @@ -140,7 +140,7 @@ abstract class KeyValueStore extends Base * * @return boolean success */ - public function writeCachedContent(Smarty_Internal_Template $_template, $content) + public function storeCachedContent(Smarty_Internal_Template $_template, $content) { $this->addMetaTimestamp($content); return $this->write(array($_template->cached->filepath => $content), $_template->cache_lifetime); @@ -153,7 +153,7 @@ abstract class KeyValueStore extends Base * * @return string|false content */ - public function readCachedContent(Smarty_Internal_Template $_template) + public function retrieveCachedContent(Smarty_Internal_Template $_template) { $content = $_template->cached->content ? $_template->cached->content : null; $timestamp = null; diff --git a/src/Compile/BlockClose.php b/src/Compile/BlockClose.php index e4eda84a..e71a22d4 100644 --- a/src/Compile/BlockClose.php +++ b/src/Compile/BlockClose.php @@ -3,7 +3,6 @@ namespace Smarty\Compile; use Smarty\ParseTree\Template; -use Smarty\Compiler\Template; /** * Smarty Internal Plugin Compile BlockClose Class diff --git a/src/Compile/Call.php b/src/Compile/Call.php index 0d10e8a2..452c5a0a 100644 --- a/src/Compile/Call.php +++ b/src/Compile/Call.php @@ -81,10 +81,10 @@ class Call extends Base // was there an assign attribute if (isset($_assign)) { $_output = - "smarty->ext->_tplFunction->callTemplateFunction(\$_smarty_tpl, {$_name}, {$_params}, {$_nocache});\n\$_smarty_tpl->assign({$_assign}, ob_get_clean());?>\n"; + "smarty->getRuntime('TplFunction')->callTemplateFunction(\$_smarty_tpl, {$_name}, {$_params}, {$_nocache});\n\$_smarty_tpl->assign({$_assign}, ob_get_clean());?>\n"; } else { $_output = - "smarty->ext->_tplFunction->callTemplateFunction(\$_smarty_tpl, {$_name}, {$_params}, {$_nocache});?>\n"; + "smarty->getRuntime('TplFunction')->callTemplateFunction(\$_smarty_tpl, {$_name}, {$_params}, {$_nocache});?>\n"; } return $_output; } diff --git a/src/Compile/Capture.php b/src/Compile/Capture.php index b152122a..38ecd58a 100644 --- a/src/Compile/Capture.php +++ b/src/Compile/Capture.php @@ -38,7 +38,7 @@ class Capture extends Base { \Smarty\Compiler\Template $compiler, $parameter = null ) { - return '$_smarty_tpl->smarty->ext->_capture->getBuffer($_smarty_tpl' . + return '$_smarty_tpl->smarty->getRuntime(\'Capture\')->getBuffer($_smarty_tpl' . (isset($parameter[1]) ? ", {$parameter[ 1 ]})" : ')'); } @@ -60,7 +60,7 @@ class Capture extends Base { $compiler->_cache['capture_stack'][] = [$compiler->nocache]; // maybe nocache because of nocache variables $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; - $_output = "smarty->ext->_capture->open(\$_smarty_tpl, $buffer, $assign, $append);?>"; + $_output = "smarty->getRuntime('Capture')->open(\$_smarty_tpl, $buffer, $assign, $append);?>"; return $_output; } } \ No newline at end of file diff --git a/src/Compile/CaptureClose.php b/src/Compile/CaptureClose.php index f1dded01..270af0d4 100644 --- a/src/Compile/CaptureClose.php +++ b/src/Compile/CaptureClose.php @@ -37,6 +37,6 @@ class CaptureClose extends Base { $compiler->tag_nocache = true; } [$compiler->nocache] = array_pop($compiler->_cache['capture_stack']); - return "smarty->ext->_capture->close(\$_smarty_tpl);?>"; + return "smarty->getRuntime('Capture')->close(\$_smarty_tpl);?>"; } } diff --git a/src/Compile/ConfigLoad.php b/src/Compile/ConfigLoad.php index 70d0d966..1734ba25 100644 --- a/src/Compile/ConfigLoad.php +++ b/src/Compile/ConfigLoad.php @@ -89,6 +89,6 @@ class ConfigLoad extends Base { $_scope = $compiler->convertScope($_attr, $this->valid_scopes); } // create config object - return "smarty->ext->configLoad->_loadConfigFile(\$_smarty_tpl, {$conf_file}, {$section}, {$_scope});\n?>\n"; + return "_loadConfigFile({$conf_file}, {$section}, {$_scope});\n?>\n"; } } diff --git a/src/Compile/ForeachSection.php b/src/Compile/ForeachSection.php index 8c8e8cc9..4951a487 100644 --- a/src/Compile/ForeachSection.php +++ b/src/Compile/ForeachSection.php @@ -175,7 +175,7 @@ abstract class ForeachSection extends Base { if ($_content !== '') { // run pre filter if required if (isset($nextCompiler->smarty->registered_filters['pre'])) { - $_content = $nextCompiler->smarty->ext->_filterHandler->runFilter( + $_content = $nextCompiler->smarty->runFilter( 'pre', $_content, $nextCompiler->template diff --git a/src/Compile/ForeachTag.php b/src/Compile/ForeachTag.php index b0c91672..95fff826 100644 --- a/src/Compile/ForeachTag.php +++ b/src/Compile/ForeachTag.php @@ -193,7 +193,7 @@ class ForeachTag extends ForeachSection { $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; // generate output code $output = "smarty->ext->_foreach->init(\$_smarty_tpl, $from, " . + $output .= "\$_from = \$_smarty_tpl->smarty->getRuntime('Foreach')->init(\$_smarty_tpl, $from, " . var_export($item, true); if ($name || $needTotal || $key) { $output .= ', ' . var_export($needTotal, true); @@ -261,6 +261,6 @@ class ForeachTag extends ForeachSection { * @return string compiled code */ public function compileRestore($levels) { - return "\$_smarty_tpl->smarty->ext->_foreach->restore(\$_smarty_tpl, {$levels});"; + return "\$_smarty_tpl->smarty->getRuntime('Foreach')->restore(\$_smarty_tpl, {$levels});"; } } \ No newline at end of file diff --git a/src/Compile/FunctionClose.php b/src/Compile/FunctionClose.php index add2482d..af93ff07 100644 --- a/src/Compile/FunctionClose.php +++ b/src/Compile/FunctionClose.php @@ -77,7 +77,7 @@ class FunctionClose extends Base { $output .= "foreach (\$params as \$key => \$value) {\n\$_smarty_tpl->tpl_vars[\$key] = new \\Smarty\\Variable(\$value, \$_smarty_tpl->isRenderingCache);\n}\n"; $output .= "\$params = var_export(\$params, true);\n"; $output .= "echo \"/*%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/smarty->ext->_tplFunction->saveTemplateVariables(\\\$_smarty_tpl, '{$_name}');\nforeach (\$params as \\\$key => \\\$value) {\n\\\$_smarty_tpl->tpl_vars[\\\$key] = new \\Smarty\\Variable(\\\$value, \\\$_smarty_tpl->isRenderingCache);\n}\n?>"; + $output .= "\\\$_smarty_tpl->smarty->getRuntime('TplFunction')->saveTemplateVariables(\\\$_smarty_tpl, '{$_name}');\nforeach (\$params as \\\$key => \\\$value) {\n\\\$_smarty_tpl->tpl_vars[\\\$key] = new \\Smarty\\Variable(\\\$value, \\\$_smarty_tpl->isRenderingCache);\n}\n?>"; $output .= "/*/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\";?>"; $compiler->parser->current_buffer->append_subtree( $compiler->parser, @@ -88,7 +88,7 @@ class FunctionClose extends Base { ); $compiler->parser->current_buffer->append_subtree($compiler->parser, $_functionCode); $output = "template->compiled->nocache_hash}%%*/smarty->ext->_tplFunction->restoreTemplateVariables(\\\$_smarty_tpl, '{$_name}');?>\n"; + $output .= "\\\$_smarty_tpl->smarty->getRuntime('TplFunction')->restoreTemplateVariables(\\\$_smarty_tpl, '{$_name}');?>\n"; $output .= "/*/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\";\n?>"; $output .= "template->compiled->nocache_hash}', \$_smarty_tpl->compiled->nocache_hash ?? '', ob_get_clean());\n"; $output .= "}\n}\n"; diff --git a/src/Compile/MakeNocache.php b/src/Compile/MakeNocache.php index 47f5d3b8..c034b799 100644 --- a/src/Compile/MakeNocache.php +++ b/src/Compile/MakeNocache.php @@ -55,7 +55,7 @@ class MakeNocache extends Base { // check and get attributes $_attr = $this->getAttributes($compiler, $args); if ($compiler->template->caching) { - $output = "smarty->ext->_make_nocache->save(\$_smarty_tpl, {$_attr[ 'var' ]});\n?>\n"; + $output = "smarty->getRuntime('MakeNocache')->save(\$_smarty_tpl, {$_attr[ 'var' ]});\n?>\n"; $compiler->template->compiled->has_nocache_code = true; $compiler->suppressNocacheProcessing = true; return $output; diff --git a/src/Template/CodeFrame.php b/src/Compiler/CodeFrame.php similarity index 97% rename from src/Template/CodeFrame.php rename to src/Compiler/CodeFrame.php index 974ead50..f9bd012c 100644 --- a/src/Template/CodeFrame.php +++ b/src/Compiler/CodeFrame.php @@ -5,14 +5,10 @@ namespace Smarty\Template; /** * Smarty Internal Extension * This file contains the Smarty template extension to create a code frame - * - * @package Smarty - * @subpackage Template * @author Uwe Tews */ /** - * Class Smarty_Internal_Extension_CodeFrame * Create code frame for compiled and cached templates */ class CodeFrame diff --git a/src/Data.php b/src/Data.php index 6f85ea59..731b66dd 100644 --- a/src/Data.php +++ b/src/Data.php @@ -59,22 +59,11 @@ abstract class Data */ public $config_vars = array(); - /** - * extension handler - * - * @var Smarty_Internal_Extension_Handler - */ - public $ext = null; - /** * \Smarty\Data constructor. - * - * Install extension handler */ public function __construct() { - $this->ext = new Smarty_Internal_Extension_Handler(); - $this->ext->objType = $this->_objType; } /** @@ -379,19 +368,6 @@ abstract class Data return $this->smarty; } - /** - * Handle unknown class methods - * - * @param string $name unknown method-name - * @param array $args argument array - * - * @return mixed - */ - public function __call($name, $args) - { - return $this->ext->_callExternalMethod($this, $name, $args); - } - /** * clear the given assigned template variable(s). * diff --git a/src/Resource/FilePlugin.php b/src/Resource/FilePlugin.php index af649d0f..f8e5b8ea 100644 --- a/src/Resource/FilePlugin.php +++ b/src/Resource/FilePlugin.php @@ -172,7 +172,7 @@ class FilePlugin extends Smarty\Resource\BasePlugin { } // Use include path ? if ($source->smarty->use_include_path) { - return $source->smarty->ext->_getIncludePath->getIncludePath($_directories, $file, $source->smarty); + return $source->smarty->getIncludePath($_directories, $file); } return false; } diff --git a/src/Runtime/CaptureRuntime.php b/src/Runtime/CaptureRuntime.php new file mode 100644 index 00000000..89c49860 --- /dev/null +++ b/src/Runtime/CaptureRuntime.php @@ -0,0 +1,163 @@ +isRegistered) { + $this->register($_template); + } + $this->captureStack[] = [ + $buffer, + $assign, + $append, + ]; + $this->captureCount++; + ob_start(); + } + + /** + * Register callbacks in template class + * + * @param \Smarty_Internal_Template $_template + */ + private function register(Smarty_Internal_Template $_template) { + $_template->startRenderCallbacks[] = [ + $this, + 'startRender', + ]; + $_template->endRenderCallbacks[] = [ + $this, + 'endRender', + ]; + $this->startRender($_template); + $this->isRegistered = true; + } + + /** + * Start render callback + * + * @param \Smarty_Internal_Template $_template + */ + public function startRender(Smarty_Internal_Template $_template) { + $this->countStack[] = $this->captureCount; + $this->captureCount = 0; + } + + /** + * Close capture section + * + * @param \Smarty_Internal_Template $_template + * + * @throws \SmartyException + */ + public function close(Smarty_Internal_Template $_template) { + if ($this->captureCount) { + [$buffer, $assign, $append] = array_pop($this->captureStack); + $this->captureCount--; + if (isset($assign)) { + $_template->assign($assign, ob_get_contents()); + } + if (isset($append)) { + $_template->append($append, ob_get_contents()); + } + $this->namedBuffer[$buffer] = ob_get_clean(); + } else { + $this->error($_template); + } + } + + /** + * Error exception on not matching {capture}{/capture} + * + * @param \Smarty_Internal_Template $_template + * + * @throws \SmartyException + */ + public function error(Smarty_Internal_Template $_template) { + throw new SmartyException("Not matching {capture}{/capture} in '{$_template->template_resource}'"); + } + + /** + * Return content of named capture buffer by key or as array + * + * @param \Smarty_Internal_Template $_template + * @param string|null $name + * + * @return string|string[]|null + */ + public function getBuffer(Smarty_Internal_Template $_template, $name = null) { + if (isset($name)) { + return $this->namedBuffer[$name] ?? null; + } else { + return $this->namedBuffer; + } + } + + /** + * End render callback + * + * @param \Smarty_Internal_Template $_template + * + * @throws \SmartyException + */ + public function endRender(Smarty_Internal_Template $_template) { + if ($this->captureCount) { + $this->error($_template); + } else { + $this->captureCount = array_pop($this->countStack); + } + } +} diff --git a/src/Runtime/ForeachRuntime.php b/src/Runtime/ForeachRuntime.php new file mode 100644 index 00000000..e1a99de3 --- /dev/null +++ b/src/Runtime/ForeachRuntime.php @@ -0,0 +1,163 @@ +count($from); + } + } else { + settype($from, 'array'); + } + } + if (!isset($total)) { + $total = empty($from) ? 0 : ($needTotal ? count($from) : 1); + } + if (isset($tpl->tpl_vars[$item])) { + $saveVars['item'] = [ + $item, + $tpl->tpl_vars[$item], + ]; + } + $tpl->tpl_vars[$item] = new \Smarty\Variable(null, $tpl->isRenderingCache); + if ($total === 0) { + $from = null; + } else { + if ($key) { + if (isset($tpl->tpl_vars[$key])) { + $saveVars['key'] = [ + $key, + $tpl->tpl_vars[$key], + ]; + } + $tpl->tpl_vars[$key] = new \Smarty\Variable(null, $tpl->isRenderingCache); + } + } + if ($needTotal) { + $tpl->tpl_vars[$item]->total = $total; + } + if ($name) { + $namedVar = "__smarty_foreach_{$name}"; + if (isset($tpl->tpl_vars[$namedVar])) { + $saveVars['named'] = [ + $namedVar, + $tpl->tpl_vars[$namedVar], + ]; + } + $namedProp = []; + if (isset($properties['total'])) { + $namedProp['total'] = $total; + } + if (isset($properties['iteration'])) { + $namedProp['iteration'] = 0; + } + if (isset($properties['index'])) { + $namedProp['index'] = -1; + } + if (isset($properties['show'])) { + $namedProp['show'] = ($total > 0); + } + $tpl->tpl_vars[$namedVar] = new \Smarty\Variable($namedProp); + } + $this->stack[] = $saveVars; + return $from; + } + + /** + * [util function] counts an array, arrayAccess/traversable or PDOStatement object + * + * @param mixed $value + * + * @return int the count for arrays and objects that implement countable, 1 for other objects that don't, and 0 + * for empty elements + */ + public function count($value) { + if ($value instanceof IteratorAggregate) { + // Note: getIterator() returns a Traversable, not an Iterator + // thus rewind() and valid() methods may not be present + return iterator_count($value->getIterator()); + } elseif ($value instanceof Iterator) { + return $value instanceof Generator ? 1 : iterator_count($value); + } elseif ($value instanceof Countable) { + return count($value); + } elseif ($value instanceof PDOStatement) { + return $value->rowCount(); + } elseif ($value instanceof Traversable) { + return iterator_count($value); + } + return count((array)$value); + } + + /** + * Restore saved variables + * + * will be called by {break n} or {continue n} for the required number of levels + * + * @param \Smarty_Internal_Template $tpl + * @param int $levels number of levels + */ + public function restore(Smarty_Internal_Template $tpl, $levels = 1) { + while ($levels) { + $saveVars = array_pop($this->stack); + if (!empty($saveVars)) { + if (isset($saveVars['item'])) { + $item = &$saveVars['item']; + $tpl->tpl_vars[$item[0]]->value = $item[1]->value; + } + if (isset($saveVars['key'])) { + $tpl->tpl_vars[$saveVars['key'][0]] = $saveVars['key'][1]; + } + if (isset($saveVars['named'])) { + $tpl->tpl_vars[$saveVars['named'][0]] = $saveVars['named'][1]; + } + } + $levels--; + } + } +} diff --git a/src/Runtime/InheritanceRuntime.php b/src/Runtime/InheritanceRuntime.php new file mode 100644 index 00000000..11c1dfcc --- /dev/null +++ b/src/Runtime/InheritanceRuntime.php @@ -0,0 +1,250 @@ +state === 3 && (strpos($tpl->template_resource, 'extendsall') === false)) { + $tpl->inheritance = new InheritanceRuntime(); + $tpl->inheritance->init($tpl, $initChild, $blockNames); + return; + } + ++$this->tplIndex; + $this->sources[$this->tplIndex] = $tpl->source; + // start of child sub template(s) + if ($initChild) { + $this->state = 1; + if (!$this->inheritanceLevel) { + //grab any output of child templates + ob_start(); + } + ++$this->inheritanceLevel; + // $tpl->startRenderCallbacks[ 'inheritance' ] = array($this, 'subTemplateStart'); + // $tpl->endRenderCallbacks[ 'inheritance' ] = array($this, 'subTemplateEnd'); + } + // if state was waiting for parent change state to parent + if ($this->state === 2) { + $this->state = 3; + } + } + + /** + * End of child template(s) + * - if outer level is reached flush output buffer and switch to wait for parent template state + * + * @param \Smarty_Internal_Template $tpl + * @param null|string $template optional name of inheritance parent template + * @param null|string $uid uid of inline template + * @param null|string $func function call name of inline template + * + * @throws \Exception + * @throws \SmartyException + */ + public function endChild(Smarty_Internal_Template $tpl, $template = null, $uid = null, $func = null) { + --$this->inheritanceLevel; + if (!$this->inheritanceLevel) { + ob_end_clean(); + $this->state = 2; + } + if (isset($template) && (($tpl->parent->_isTplObj() && $tpl->parent->source->type !== 'extends') + || $tpl->smarty->extends_recursion) + ) { + $tpl->_subTemplateRender( + $template, + $tpl->cache_id, + $tpl->compile_id, + $tpl->caching ? 9999 : 0, + $tpl->cache_lifetime, + [], + 2, + false, + $uid, + $func + ); + } + } + + /** + * \Smarty\Block constructor. + * - if outer level {block} of child template ($state === 1) save it as child root block + * - otherwise process inheritance and render + * + * @param \Smarty_Internal_Template $tpl + * @param $className + * @param string $name + * @param int|null $tplIndex index of outer level {block} if nested + * + * @throws \SmartyException + */ + public function instanceBlock(Smarty_Internal_Template $tpl, $className, $name, $tplIndex = null) { + $block = new $className($name, isset($tplIndex) ? $tplIndex : $this->tplIndex); + if (isset($this->childRoot[$name])) { + $block->child = $this->childRoot[$name]; + } + if ($this->state === 1) { + $this->childRoot[$name] = $block; + return; + } + // make sure we got child block of child template of current block + while ($block->child && $block->child->child && $block->tplIndex <= $block->child->tplIndex) { + $block->child = $block->child->child; + } + $this->process($tpl, $block); + } + + /** + * Goto child block or render this + * + * @param Smarty_Internal_Template $tpl + * @param \Smarty\Block $block + * @param \Smarty\Block|null $parent + * + * @throws SmartyException + */ + public function process( + Smarty_Internal_Template $tpl, + \Smarty\Block $block, + \Smarty\Block $parent = null + ) { + if ($block->hide && !isset($block->child)) { + return; + } + if (isset($block->child) && $block->child->hide && !isset($block->child->child)) { + $block->child = null; + } + $block->parent = $parent; + if ($block->append && !$block->prepend && isset($parent)) { + $this->callParent($tpl, $block, '\'{block append}\''); + } + if ($block->callsChild || !isset($block->child) || ($block->child->hide && !isset($block->child->child))) { + $this->callBlock($block, $tpl); + } else { + $this->process($tpl, $block->child, $block); + } + if ($block->prepend && isset($parent)) { + $this->callParent($tpl, $block, '{block prepend}'); + if ($block->append) { + if ($block->callsChild || !isset($block->child) + || ($block->child->hide && !isset($block->child->child)) + ) { + $this->callBlock($block, $tpl); + } else { + $this->process($tpl, $block->child, $block); + } + } + } + $block->parent = null; + } + + /** + * Render child on \$smarty.block.child + * + * @param Smarty_Internal_Template $tpl + * @param \Smarty\Block $block + * + * @return null|string block content + * @throws SmartyException + */ + public function callChild(Smarty_Internal_Template $tpl, \Smarty\Block $block) { + if (isset($block->child)) { + $this->process($tpl, $block->child, $block); + } + } + + /** + * Render parent block on \$smarty.block.parent or {block append/prepend} + * + * @param Smarty_Internal_Template $tpl + * @param \Smarty\Block $block + * @param string $tag + * + * @return null|string block content + * @throws SmartyException + */ + public function callParent(Smarty_Internal_Template $tpl, \Smarty\Block $block, $tag) { + if (isset($block->parent)) { + $this->callBlock($block->parent, $tpl); + } else { + throw new SmartyException("inheritance: illegal '{$tag}' used in child template '{$tpl->inheritance->sources[$block->tplIndex]->filepath}' block '{$block->name}'"); + } + } + + /** + * render block + * + * @param \Smarty\Block $block + * @param Smarty_Internal_Template $tpl + */ + public function callBlock(\Smarty\Block $block, Smarty_Internal_Template $tpl) { + $this->sourceStack[] = $tpl->source; + $tpl->source = $this->sources[$block->tplIndex]; + $block->callBlock($tpl); + $tpl->source = array_pop($this->sourceStack); + } +} diff --git a/src/Runtime/MakeNocacheRuntime.php b/src/Runtime/MakeNocacheRuntime.php new file mode 100644 index 00000000..8ffc8860 --- /dev/null +++ b/src/Runtime/MakeNocacheRuntime.php @@ -0,0 +1,55 @@ +tpl_vars[$var])) { + $export = + preg_replace('/^\\\\Smarty\\\\Variable::__set_state[(]|[)]$/', '', var_export($tpl->tpl_vars[$var], true)); + if (preg_match('/(\w+)::__set_state/', $export, $match)) { + throw new SmartyException("{make_nocache \${$var}} in template '{$tpl->source->name}': variable does contain object '{$match[1]}' not implementing method '__set_state'"); + } + echo "/*%%SmartyNocache:{$tpl->compiled->nocache_hash}%%*/smarty->getRuntime('MakeNocache')->store(\$_smarty_tpl, '{$var}', ", '\\') . + $export . ");?>\n/*/%%SmartyNocache:{$tpl->compiled->nocache_hash}%%*/"; + } + } + + /** + * Store variable value saved while rendering compiled template in cached template context + * + * @param \Smarty_Internal_Template $tpl + * @param string $var variable name + * @param array $properties + */ + public function store(Smarty_Internal_Template $tpl, $var, $properties) { + // do not overwrite existing nocache variables + if (!isset($tpl->tpl_vars[$var]) || !$tpl->tpl_vars[$var]->nocache) { + $newVar = new \Smarty\Variable(); + unset($properties['nocache']); + foreach ($properties as $k => $v) { + $newVar->$k = $v; + } + $tpl->tpl_vars[$var] = $newVar; + } + } +} diff --git a/src/Runtime/TplFunctionRuntime.php b/src/Runtime/TplFunctionRuntime.php new file mode 100644 index 00000000..b408f813 --- /dev/null +++ b/src/Runtime/TplFunctionRuntime.php @@ -0,0 +1,173 @@ +tplFunctions[$name] ?? ($tpl->smarty->tplFunctions[$name] ?? null); + if (isset($funcParam)) { + if (!$tpl->caching || ($tpl->caching && $nocache)) { + $function = $funcParam['call_name']; + } else { + if (isset($funcParam['call_name_caching'])) { + $function = $funcParam['call_name_caching']; + } else { + $function = $funcParam['call_name']; + } + } + if (function_exists($function)) { + $this->saveTemplateVariables($tpl, $name); + $function($tpl, $params); + $this->restoreTemplateVariables($tpl, $name); + return; + } + // try to load template function dynamically + if ($this->addTplFuncToCache($tpl, $name, $function)) { + $this->saveTemplateVariables($tpl, $name); + $function($tpl, $params); + $this->restoreTemplateVariables($tpl, $name); + return; + } + } + throw new SmartyException("Unable to find template function '{$name}'"); + } + + /** + * Register template functions defined by template + * + * @param \Smarty|\Smarty_Internal_Template|\Smarty_Internal_TemplateBase $obj + * @param array $tplFunctions source information array of + * template functions defined + * in template + * @param bool $override if true replace existing + * functions with same name + */ + public function registerTplFunctions(Smarty_Internal_TemplateBase $obj, $tplFunctions, $override = true) { + $obj->tplFunctions = + $override ? array_merge($obj->tplFunctions, $tplFunctions) : array_merge($tplFunctions, $obj->tplFunctions); + // make sure that the template functions are known in parent templates + if ($obj->_isSubTpl()) { + $this->registerTplFunctions($obj->parent, $tplFunctions, false); + } else { + $obj->smarty->tplFunctions = $override ? array_merge($obj->smarty->tplFunctions, $tplFunctions) : + array_merge($tplFunctions, $obj->smarty->tplFunctions); + } + } + + /** + * Return source parameter array for single or all template functions + * + * @param \Smarty_Internal_Template $tpl template object + * @param null|string $name template function name + * + * @return array|bool|mixed + */ + public function getTplFunction(Smarty_Internal_Template $tpl, $name = null) { + if (isset($name)) { + return $tpl->tplFunctions[$name] ?? ($tpl->smarty->tplFunctions[$name] ?? false); + } else { + return empty($tpl->tplFunctions) ? $tpl->smarty->tplFunctions : $tpl->tplFunctions; + } + } + + /** + * Add template function to cache file for nocache calls + * + * @param Smarty_Internal_Template $tpl + * @param string $_name template function name + * @param string $_function PHP function name + * + * @return bool + */ + public function addTplFuncToCache(Smarty_Internal_Template $tpl, $_name, $_function) { + $funcParam = $tpl->tplFunctions[$_name]; + if (is_file($funcParam['compiled_filepath'])) { + // read compiled file + $code = file_get_contents($funcParam['compiled_filepath']); + // grab template function + if (preg_match("/\/\* {$_function} \*\/([\S\s]*?)\/\*\/ {$_function} \*\//", $code, $match)) { + // grab source info from file dependency + preg_match("/\s*'{$funcParam['uid']}'([\S\s]*?)\),/", $code, $match1); + unset($code); + // make PHP function known + eval($match[0]); + if (function_exists($_function)) { + // search cache file template + $tplPtr = $tpl; + while (!isset($tplPtr->cached) && isset($tplPtr->parent)) { + $tplPtr = $tplPtr->parent; + } + // add template function code to cache file + if (isset($tplPtr->cached)) { + $content = $tplPtr->cached->readCache($tplPtr); + if ($content) { + // check if we must update file dependency + if (!preg_match("/'{$funcParam['uid']}'(.*?)'nocache_hash'/", $content, $match2)) { + $content = preg_replace("/('file_dependency'(.*?)\()/", "\\1{$match1[0]}", $content); + } + $tplPtr->cached->writeCache( + $tplPtr, + preg_replace('/\s*\?>\s*$/', "\n", $content) . + "\n" . preg_replace( + [ + '/^\s*<\?php\s+/', + '/\s*\?>\s*$/', + ], + "\n", + $match[0] + ) + ); + } + } + return true; + } + } + } + return false; + } + + /** + * Save current template variables on stack + * + * @param \Smarty_Internal_Template $tpl + * @param string $name stack name + */ + public function saveTemplateVariables(Smarty_Internal_Template $tpl, $name) { + $tpl->_var_stack[] = + ['tpl' => $tpl->tpl_vars, 'config' => $tpl->config_vars, 'name' => "_tplFunction_{$name}"]; + } + + /** + * Restore saved variables into template objects + * + * @param \Smarty_Internal_Template $tpl + * @param string $name stack name + */ + public function restoreTemplateVariables(Smarty_Internal_Template $tpl, $name) { + if (isset($tpl->_var_stack)) { + $vars = array_pop($tpl->_var_stack); + $tpl->tpl_vars = $vars['tpl']; + $tpl->config_vars = $vars['config']; + } + } +} diff --git a/src/Runtime/smarty_internal_runtime_cachemodify.php b/src/Runtime/smarty_internal_runtime_cachemodify.php deleted file mode 100644 index 6e12d2ae..00000000 --- a/src/Runtime/smarty_internal_runtime_cachemodify.php +++ /dev/null @@ -1,68 +0,0 @@ -isCached() && !$_template->compiled->has_nocache_code; - $_last_modified_date = - @substr($_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ], 0, strpos($_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ], 'GMT') + 3); - if ($_isCached && $cached->timestamp <= strtotime($_last_modified_date)) { - switch (PHP_SAPI) { - case 'cgi': // php-cgi < 5.3 - case 'cgi-fcgi': // php-cgi >= 5.3 - case 'fpm-fcgi': // php-fpm >= 5.3.3 - header('Status: 304 Not Modified'); - break; - case 'cli': - if (/* ^phpunit */ - !empty($_SERVER[ 'SMARTY_PHPUNIT_DISABLE_HEADERS' ]) /* phpunit$ */ - ) { - $_SERVER[ 'SMARTY_PHPUNIT_HEADERS' ][] = '304 Not Modified'; - } - break; - default: - if (/* ^phpunit */ - !empty($_SERVER[ 'SMARTY_PHPUNIT_DISABLE_HEADERS' ]) /* phpunit$ */ - ) { - $_SERVER[ 'SMARTY_PHPUNIT_HEADERS' ][] = '304 Not Modified'; - } else { - header($_SERVER[ 'SERVER_PROTOCOL' ] . ' 304 Not Modified'); - } - break; - } - } else { - switch (PHP_SAPI) { - case 'cli': - if (/* ^phpunit */ - !empty($_SERVER[ 'SMARTY_PHPUNIT_DISABLE_HEADERS' ]) /* phpunit$ */ - ) { - $_SERVER[ 'SMARTY_PHPUNIT_HEADERS' ][] = - 'Last-Modified: ' . gmdate('D, d M Y H:i:s', $cached->timestamp) . ' GMT'; - } - break; - default: - header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $cached->timestamp) . ' GMT'); - break; - } - echo $content; - } - } -} diff --git a/src/Runtime/smarty_internal_runtime_cacheresourcefile.php b/src/Runtime/smarty_internal_runtime_cacheresourcefile.php deleted file mode 100644 index 93e46513..00000000 --- a/src/Runtime/smarty_internal_runtime_cacheresourcefile.php +++ /dev/null @@ -1,139 +0,0 @@ -use_sub_dirs ? '/' : '^'; - $_compile_id_offset = $smarty->use_sub_dirs ? 3 : 0; - $_dir = $smarty->getCacheDir(); - if ($_dir === '/') { //We should never want to delete this! - return 0; - } - $_dir_length = strlen($_dir); - if (isset($_cache_id)) { - $_cache_id_parts = explode('|', $_cache_id); - $_cache_id_parts_count = count($_cache_id_parts); - if ($smarty->use_sub_dirs) { - foreach ($_cache_id_parts as $id_part) { - $_dir .= $id_part . '/'; - } - } - } - if (isset($resource_name)) { - $_save_stat = $smarty->caching; - $smarty->caching = \Smarty\Smarty::CACHING_LIFETIME_CURRENT; - $tpl = new $smarty->template_class($resource_name, $smarty); - $smarty->caching = $_save_stat; - // remove from template cache - $tpl->source; // have the template registered before unset() - if ($tpl->source->exists) { - $_resourcename_parts = basename(str_replace('^', '/', $tpl->cached->filepath)); - } else { - return 0; - } - } - $_count = 0; - $_time = time(); - if (file_exists($_dir)) { - $_cacheDirs = new RecursiveDirectoryIterator($_dir); - $_cache = new RecursiveIteratorIterator($_cacheDirs, RecursiveIteratorIterator::CHILD_FIRST); - foreach ($_cache as $_file) { - if (substr(basename($_file->getPathname()), 0, 1) === '.') { - continue; - } - $_filepath = (string)$_file; - // directory ? - if ($_file->isDir()) { - if (!$_cache->isDot()) { - // delete folder if empty - @rmdir($_file->getPathname()); - } - } else { - // delete only php files - if (substr($_filepath, -4) !== '.php') { - continue; - } - $_parts = explode($_dir_sep, str_replace('\\', '/', substr($_filepath, $_dir_length))); - $_parts_count = count($_parts); - // check name - if (isset($resource_name)) { - if ($_parts[ $_parts_count - 1 ] !== $_resourcename_parts) { - continue; - } - } - // check compile id - if (isset($_compile_id) && (!isset($_parts[ $_parts_count - 2 - $_compile_id_offset ]) - || $_parts[ $_parts_count - 2 - $_compile_id_offset ] !== $_compile_id) - ) { - continue; - } - // check cache id - if (isset($_cache_id)) { - // count of cache id parts - $_parts_count = (isset($_compile_id)) ? $_parts_count - 2 - $_compile_id_offset : - $_parts_count - 1 - $_compile_id_offset; - if ($_parts_count < $_cache_id_parts_count) { - continue; - } - for ($i = 0; $i < $_cache_id_parts_count; $i++) { - if ($_parts[ $i ] !== $_cache_id_parts[ $i ]) { - continue 2; - } - } - } - if (is_file($_filepath)) { - // expired ? - if (isset($exp_time)) { - if ($exp_time < 0) { - preg_match('#\'cache_lifetime\' =>\s*(\d*)#', file_get_contents($_filepath), $match); - if ($_time < (filemtime($_filepath) + $match[ 1 ])) { - continue; - } - } else { - if ($_time - filemtime($_filepath) < $exp_time) { - continue; - } - } - } - $_count += @unlink($_filepath) ? 1 : 0; - if (function_exists('opcache_invalidate') - && (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1) - ) { - opcache_invalidate($_filepath, true); - } elseif (function_exists('apc_delete_file')) { - apc_delete_file($_filepath); - } - } - } - } - } - return $_count; - } -} diff --git a/src/Runtime/smarty_internal_runtime_capture.php b/src/Runtime/smarty_internal_runtime_capture.php deleted file mode 100644 index 8e74c093..00000000 --- a/src/Runtime/smarty_internal_runtime_capture.php +++ /dev/null @@ -1,168 +0,0 @@ -isRegistered) { - $this->register($_template); - } - $this->captureStack[] = array( - $buffer, - $assign, - $append - ); - $this->captureCount++; - ob_start(); - } - - /** - * Register callbacks in template class - * - * @param \Smarty_Internal_Template $_template - */ - private function register(Smarty_Internal_Template $_template) - { - $_template->startRenderCallbacks[] = array( - $this, - 'startRender' - ); - $_template->endRenderCallbacks[] = array( - $this, - 'endRender' - ); - $this->startRender($_template); - $this->isRegistered = true; - } - - /** - * Start render callback - * - * @param \Smarty_Internal_Template $_template - */ - public function startRender(Smarty_Internal_Template $_template) - { - $this->countStack[] = $this->captureCount; - $this->captureCount = 0; - } - - /** - * Close capture section - * - * @param \Smarty_Internal_Template $_template - * - * @throws \SmartyException - */ - public function close(Smarty_Internal_Template $_template) - { - if ($this->captureCount) { - list($buffer, $assign, $append) = array_pop($this->captureStack); - $this->captureCount--; - if (isset($assign)) { - $_template->assign($assign, ob_get_contents()); - } - if (isset($append)) { - $_template->append($append, ob_get_contents()); - } - $this->namedBuffer[ $buffer ] = ob_get_clean(); - } else { - $this->error($_template); - } - } - - /** - * Error exception on not matching {capture}{/capture} - * - * @param \Smarty_Internal_Template $_template - * - * @throws \SmartyException - */ - public function error(Smarty_Internal_Template $_template) - { - throw new SmartyException("Not matching {capture}{/capture} in '{$_template->template_resource}'"); - } - - /** - * Return content of named capture buffer by key or as array - * - * @param \Smarty_Internal_Template $_template - * @param string|null $name - * - * @return string|string[]|null - */ - public function getBuffer(Smarty_Internal_Template $_template, $name = null) - { - if (isset($name)) { - return isset($this->namedBuffer[ $name ]) ? $this->namedBuffer[ $name ] : null; - } else { - return $this->namedBuffer; - } - } - - /** - * End render callback - * - * @param \Smarty_Internal_Template $_template - * - * @throws \SmartyException - */ - public function endRender(Smarty_Internal_Template $_template) - { - if ($this->captureCount) { - $this->error($_template); - } else { - $this->captureCount = array_pop($this->countStack); - } - } -} diff --git a/src/Runtime/smarty_internal_runtime_filterhandler.php b/src/Runtime/smarty_internal_runtime_filterhandler.php deleted file mode 100644 index 859fe848..00000000 --- a/src/Runtime/smarty_internal_runtime_filterhandler.php +++ /dev/null @@ -1,44 +0,0 @@ -smarty->registered_filters[ $type ])) { - foreach ($template->smarty->registered_filters[ $type ] as $key => $name) { - $content = call_user_func($template->smarty->registered_filters[ $type ][ $key ], $content, $template); - } - } - // return filtered output - return $content; - } -} diff --git a/src/Runtime/smarty_internal_runtime_foreach.php b/src/Runtime/smarty_internal_runtime_foreach.php deleted file mode 100644 index 62fa22bd..00000000 --- a/src/Runtime/smarty_internal_runtime_foreach.php +++ /dev/null @@ -1,162 +0,0 @@ -count($from); - } - } else { - settype($from, 'array'); - } - } - if (!isset($total)) { - $total = empty($from) ? 0 : ($needTotal ? count($from) : 1); - } - if (isset($tpl->tpl_vars[ $item ])) { - $saveVars[ 'item' ] = array( - $item, - $tpl->tpl_vars[ $item ] - ); - } - $tpl->tpl_vars[ $item ] = new \Smarty\Variable(null, $tpl->isRenderingCache); - if ($total === 0) { - $from = null; - } else { - if ($key) { - if (isset($tpl->tpl_vars[ $key ])) { - $saveVars[ 'key' ] = array( - $key, - $tpl->tpl_vars[ $key ] - ); - } - $tpl->tpl_vars[ $key ] = new \Smarty\Variable(null, $tpl->isRenderingCache); - } - } - if ($needTotal) { - $tpl->tpl_vars[ $item ]->total = $total; - } - if ($name) { - $namedVar = "__smarty_foreach_{$name}"; - if (isset($tpl->tpl_vars[ $namedVar ])) { - $saveVars[ 'named' ] = array( - $namedVar, - $tpl->tpl_vars[ $namedVar ] - ); - } - $namedProp = array(); - if (isset($properties[ 'total' ])) { - $namedProp[ 'total' ] = $total; - } - if (isset($properties[ 'iteration' ])) { - $namedProp[ 'iteration' ] = 0; - } - if (isset($properties[ 'index' ])) { - $namedProp[ 'index' ] = -1; - } - if (isset($properties[ 'show' ])) { - $namedProp[ 'show' ] = ($total > 0); - } - $tpl->tpl_vars[ $namedVar ] = new \Smarty\Variable($namedProp); - } - $this->stack[] = $saveVars; - return $from; - } - - /** - * [util function] counts an array, arrayAccess/traversable or PDOStatement object - * - * @param mixed $value - * - * @return int the count for arrays and objects that implement countable, 1 for other objects that don't, and 0 - * for empty elements - */ - public function count($value) - { - if ($value instanceof IteratorAggregate) { - // Note: getIterator() returns a Traversable, not an Iterator - // thus rewind() and valid() methods may not be present - return iterator_count($value->getIterator()); - } elseif ($value instanceof Iterator) { - return $value instanceof Generator ? 1 : iterator_count($value); - } elseif ($value instanceof Countable) { - return count($value); - } elseif ($value instanceof PDOStatement) { - return $value->rowCount(); - } elseif ($value instanceof Traversable) { - return iterator_count($value); - } - return count((array)$value); - } - - /** - * Restore saved variables - * - * will be called by {break n} or {continue n} for the required number of levels - * - * @param \Smarty_Internal_Template $tpl - * @param int $levels number of levels - */ - public function restore(Smarty_Internal_Template $tpl, $levels = 1) - { - while ($levels) { - $saveVars = array_pop($this->stack); - if (!empty($saveVars)) { - if (isset($saveVars[ 'item' ])) { - $item = &$saveVars[ 'item' ]; - $tpl->tpl_vars[ $item[ 0 ] ]->value = $item[ 1 ]->value; - } - if (isset($saveVars[ 'key' ])) { - $tpl->tpl_vars[ $saveVars[ 'key' ][ 0 ] ] = $saveVars[ 'key' ][ 1 ]; - } - if (isset($saveVars[ 'named' ])) { - $tpl->tpl_vars[ $saveVars[ 'named' ][ 0 ] ] = $saveVars[ 'named' ][ 1 ]; - } - } - $levels--; - } - } -} diff --git a/src/Runtime/smarty_internal_runtime_getincludepath.php b/src/Runtime/smarty_internal_runtime_getincludepath.php deleted file mode 100644 index 5ae98304..00000000 --- a/src/Runtime/smarty_internal_runtime_getincludepath.php +++ /dev/null @@ -1,181 +0,0 @@ -_include_path !== $_i_path) { - $this->_include_dirs = array(); - $this->_include_path = $_i_path; - $_dirs = (array)explode(PATH_SEPARATOR, $_i_path); - foreach ($_dirs as $_path) { - if (is_dir($_path)) { - $this->_include_dirs[] = $smarty->_realpath($_path . DIRECTORY_SEPARATOR, true); - } - } - return true; - } - return false; - } - - /** - * return array with include path directories - * - * @param \Smarty $smarty - * - * @return array - */ - public function getIncludePathDirs(Smarty $smarty) - { - $this->isNewIncludePath($smarty); - return $this->_include_dirs; - } - - /** - * Return full file path from PHP include_path - * - * @param string[] $dirs - * @param string $file - * @param \Smarty $smarty - * - * @return bool|string full filepath or false - */ - public function getIncludePath($dirs, $file, Smarty $smarty) - { - //if (!(isset($this->_has_stream_include) ? $this->_has_stream_include : $this->_has_stream_include = false)) { - if (!(isset($this->_has_stream_include) ? $this->_has_stream_include : - $this->_has_stream_include = function_exists('stream_resolve_include_path')) - ) { - $this->isNewIncludePath($smarty); - } - // try PHP include_path - foreach ($dirs as $dir) { - $dir_n = isset($this->number[ $dir ]) ? $this->number[ $dir ] : $this->number[ $dir ] = $this->counter++; - if (isset($this->isFile[ $dir_n ][ $file ])) { - if ($this->isFile[ $dir_n ][ $file ]) { - return $this->isFile[ $dir_n ][ $file ]; - } else { - continue; - } - } - if (isset($this->_user_dirs[ $dir_n ])) { - if (false === $this->_user_dirs[ $dir_n ]) { - continue; - } else { - $dir = $this->_user_dirs[ $dir_n ]; - } - } else { - if ($dir[ 0 ] === '/' || $dir[ 1 ] === ':') { - $dir = str_ireplace(getcwd(), '.', $dir); - if ($dir[ 0 ] === '/' || $dir[ 1 ] === ':') { - $this->_user_dirs[ $dir_n ] = false; - continue; - } - } - $dir = substr($dir, 2); - $this->_user_dirs[ $dir_n ] = $dir; - } - if ($this->_has_stream_include) { - $path = stream_resolve_include_path($dir . (isset($file) ? $file : '')); - if ($path) { - return $this->isFile[ $dir_n ][ $file ] = $path; - } - } else { - foreach ($this->_include_dirs as $key => $_i_path) { - $path = isset($this->isPath[ $key ][ $dir_n ]) ? $this->isPath[ $key ][ $dir_n ] : - $this->isPath[ $key ][ $dir_n ] = is_dir($_dir_path = $_i_path . $dir) ? $_dir_path : false; - if ($path === false) { - continue; - } - if (isset($file)) { - $_file = $this->isFile[ $dir_n ][ $file ] = (is_file($path . $file)) ? $path . $file : false; - if ($_file) { - return $_file; - } - } else { - // no file was given return directory path - return $path; - } - } - } - } - return false; - } -} diff --git a/src/Runtime/smarty_internal_runtime_inheritance.php b/src/Runtime/smarty_internal_runtime_inheritance.php deleted file mode 100644 index 5bd65e1f..00000000 --- a/src/Runtime/smarty_internal_runtime_inheritance.php +++ /dev/null @@ -1,251 +0,0 @@ -state === 3 && (strpos($tpl->template_resource, 'extendsall') === false)) { - $tpl->inheritance = new Smarty_Internal_Runtime_Inheritance(); - $tpl->inheritance->init($tpl, $initChild, $blockNames); - return; - } - ++$this->tplIndex; - $this->sources[ $this->tplIndex ] = $tpl->source; - // start of child sub template(s) - if ($initChild) { - $this->state = 1; - if (!$this->inheritanceLevel) { - //grab any output of child templates - ob_start(); - } - ++$this->inheritanceLevel; - // $tpl->startRenderCallbacks[ 'inheritance' ] = array($this, 'subTemplateStart'); - // $tpl->endRenderCallbacks[ 'inheritance' ] = array($this, 'subTemplateEnd'); - } - // if state was waiting for parent change state to parent - if ($this->state === 2) { - $this->state = 3; - } - } - - /** - * End of child template(s) - * - if outer level is reached flush output buffer and switch to wait for parent template state - * - * @param \Smarty_Internal_Template $tpl - * @param null|string $template optional name of inheritance parent template - * @param null|string $uid uid of inline template - * @param null|string $func function call name of inline template - * - * @throws \Exception - * @throws \SmartyException - */ - public function endChild(Smarty_Internal_Template $tpl, $template = null, $uid = null, $func = null) - { - --$this->inheritanceLevel; - if (!$this->inheritanceLevel) { - ob_end_clean(); - $this->state = 2; - } - if (isset($template) && (($tpl->parent->_isTplObj() && $tpl->parent->source->type !== 'extends') - || $tpl->smarty->extends_recursion) - ) { - $tpl->_subTemplateRender( - $template, - $tpl->cache_id, - $tpl->compile_id, - $tpl->caching ? 9999 : 0, - $tpl->cache_lifetime, - array(), - 2, - false, - $uid, - $func - ); - } - } - - /** - * \Smarty\Block constructor. - * - if outer level {block} of child template ($state === 1) save it as child root block - * - otherwise process inheritance and render - * - * @param \Smarty_Internal_Template $tpl - * @param $className - * @param string $name - * @param int|null $tplIndex index of outer level {block} if nested - * - * @throws \SmartyException - */ - public function instanceBlock(Smarty_Internal_Template $tpl, $className, $name, $tplIndex = null) - { - $block = new $className($name, isset($tplIndex) ? $tplIndex : $this->tplIndex); - if (isset($this->childRoot[ $name ])) { - $block->child = $this->childRoot[ $name ]; - } - if ($this->state === 1) { - $this->childRoot[ $name ] = $block; - return; - } - // make sure we got child block of child template of current block - while ($block->child && $block->child->child && $block->tplIndex <= $block->child->tplIndex) { - $block->child = $block->child->child; - } - $this->process($tpl, $block); - } - - /** - * Goto child block or render this - * - * @param Smarty_Internal_Template $tpl - * @param \Smarty\Block $block - * @param \Smarty\Block|null $parent - * - * @throws SmartyException - */ - public function process( - Smarty_Internal_Template $tpl, - \Smarty\Block $block, - \Smarty\Block $parent = null - ) { - if ($block->hide && !isset($block->child)) { - return; - } - if (isset($block->child) && $block->child->hide && !isset($block->child->child)) { - $block->child = null; - } - $block->parent = $parent; - if ($block->append && !$block->prepend && isset($parent)) { - $this->callParent($tpl, $block, '\'{block append}\''); - } - if ($block->callsChild || !isset($block->child) || ($block->child->hide && !isset($block->child->child))) { - $this->callBlock($block, $tpl); - } else { - $this->process($tpl, $block->child, $block); - } - if ($block->prepend && isset($parent)) { - $this->callParent($tpl, $block, '{block prepend}'); - if ($block->append) { - if ($block->callsChild || !isset($block->child) - || ($block->child->hide && !isset($block->child->child)) - ) { - $this->callBlock($block, $tpl); - } else { - $this->process($tpl, $block->child, $block); - } - } - } - $block->parent = null; - } - - /** - * Render child on \$smarty.block.child - * - * @param Smarty_Internal_Template $tpl - * @param \Smarty\Block $block - * - * @return null|string block content - * @throws SmartyException - */ - public function callChild(Smarty_Internal_Template $tpl, \Smarty\Block $block) - { - if (isset($block->child)) { - $this->process($tpl, $block->child, $block); - } - } - - /** - * Render parent block on \$smarty.block.parent or {block append/prepend} - * - * @param Smarty_Internal_Template $tpl - * @param \Smarty\Block $block - * @param string $tag - * - * @return null|string block content - * @throws SmartyException - */ - public function callParent(Smarty_Internal_Template $tpl, \Smarty\Block $block, $tag) - { - if (isset($block->parent)) { - $this->callBlock($block->parent, $tpl); - } else { - throw new SmartyException("inheritance: illegal '{$tag}' used in child template '{$tpl->inheritance->sources[$block->tplIndex]->filepath}' block '{$block->name}'"); - } - } - - /** - * render block - * - * @param \Smarty\Block $block - * @param Smarty_Internal_Template $tpl - */ - public function callBlock(\Smarty\Block $block, Smarty_Internal_Template $tpl) - { - $this->sourceStack[] = $tpl->source; - $tpl->source = $this->sources[ $block->tplIndex ]; - $block->callBlock($tpl); - $tpl->source = array_pop($this->sourceStack); - } -} diff --git a/src/Runtime/smarty_internal_runtime_make_nocache.php b/src/Runtime/smarty_internal_runtime_make_nocache.php deleted file mode 100644 index fe931cc1..00000000 --- a/src/Runtime/smarty_internal_runtime_make_nocache.php +++ /dev/null @@ -1,54 +0,0 @@ -tpl_vars[ $var ])) { - $export = - preg_replace('/^\\\\Smarty\\\\Variable::__set_state[(]|[)]$/', '', var_export($tpl->tpl_vars[ $var ], true)); - if (preg_match('/(\w+)::__set_state/', $export, $match)) { - throw new SmartyException("{make_nocache \${$var}} in template '{$tpl->source->name}': variable does contain object '{$match[1]}' not implementing method '__set_state'"); - } - echo "/*%%SmartyNocache:{$tpl->compiled->nocache_hash}%%*/smarty->ext->_make_nocache->store(\$_smarty_tpl, '{$var}', ", '\\') . - $export . ");?>\n/*/%%SmartyNocache:{$tpl->compiled->nocache_hash}%%*/"; - } - } - - /** - * Store variable value saved while rendering compiled template in cached template context - * - * @param \Smarty_Internal_Template $tpl - * @param string $var variable name - * @param array $properties - */ - public function store(Smarty_Internal_Template $tpl, $var, $properties) - { - // do not overwrite existing nocache variables - if (!isset($tpl->tpl_vars[ $var ]) || !$tpl->tpl_vars[ $var ]->nocache) { - $newVar = new \Smarty\Variable(); - unset($properties[ 'nocache' ]); - foreach ($properties as $k => $v) { - $newVar->$k = $v; - } - $tpl->tpl_vars[ $var ] = $newVar; - } - } -} diff --git a/src/Runtime/smarty_internal_runtime_tplfunction.php b/src/Runtime/smarty_internal_runtime_tplfunction.php deleted file mode 100644 index 511f03b1..00000000 --- a/src/Runtime/smarty_internal_runtime_tplfunction.php +++ /dev/null @@ -1,177 +0,0 @@ -tplFunctions[ $name ]) ? $tpl->tplFunctions[ $name ] : - (isset($tpl->smarty->tplFunctions[ $name ]) ? $tpl->smarty->tplFunctions[ $name ] : null); - if (isset($funcParam)) { - if (!$tpl->caching || ($tpl->caching && $nocache)) { - $function = $funcParam[ 'call_name' ]; - } else { - if (isset($funcParam[ 'call_name_caching' ])) { - $function = $funcParam[ 'call_name_caching' ]; - } else { - $function = $funcParam[ 'call_name' ]; - } - } - if (function_exists($function)) { - $this->saveTemplateVariables($tpl, $name); - $function($tpl, $params); - $this->restoreTemplateVariables($tpl, $name); - return; - } - // try to load template function dynamically - if ($this->addTplFuncToCache($tpl, $name, $function)) { - $this->saveTemplateVariables($tpl, $name); - $function($tpl, $params); - $this->restoreTemplateVariables($tpl, $name); - return; - } - } - throw new SmartyException("Unable to find template function '{$name}'"); - } - - /** - * Register template functions defined by template - * - * @param \Smarty|\Smarty_Internal_Template|\Smarty_Internal_TemplateBase $obj - * @param array $tplFunctions source information array of - * template functions defined - * in template - * @param bool $override if true replace existing - * functions with same name - */ - public function registerTplFunctions(Smarty_Internal_TemplateBase $obj, $tplFunctions, $override = true) - { - $obj->tplFunctions = - $override ? array_merge($obj->tplFunctions, $tplFunctions) : array_merge($tplFunctions, $obj->tplFunctions); - // make sure that the template functions are known in parent templates - if ($obj->_isSubTpl()) { - $obj->smarty->ext->_tplFunction->registerTplFunctions($obj->parent, $tplFunctions, false); - } else { - $obj->smarty->tplFunctions = $override ? array_merge($obj->smarty->tplFunctions, $tplFunctions) : - array_merge($tplFunctions, $obj->smarty->tplFunctions); - } - } - - /** - * Return source parameter array for single or all template functions - * - * @param \Smarty_Internal_Template $tpl template object - * @param null|string $name template function name - * - * @return array|bool|mixed - */ - public function getTplFunction(Smarty_Internal_Template $tpl, $name = null) - { - if (isset($name)) { - return isset($tpl->tplFunctions[ $name ]) ? $tpl->tplFunctions[ $name ] : - (isset($tpl->smarty->tplFunctions[ $name ]) ? $tpl->smarty->tplFunctions[ $name ] : false); - } else { - return empty($tpl->tplFunctions) ? $tpl->smarty->tplFunctions : $tpl->tplFunctions; - } - } - - /** - * Add template function to cache file for nocache calls - * - * @param Smarty_Internal_Template $tpl - * @param string $_name template function name - * @param string $_function PHP function name - * - * @return bool - */ - public function addTplFuncToCache(Smarty_Internal_Template $tpl, $_name, $_function) - { - $funcParam = $tpl->tplFunctions[ $_name ]; - if (is_file($funcParam[ 'compiled_filepath' ])) { - // read compiled file - $code = file_get_contents($funcParam[ 'compiled_filepath' ]); - // grab template function - if (preg_match("/\/\* {$_function} \*\/([\S\s]*?)\/\*\/ {$_function} \*\//", $code, $match)) { - // grab source info from file dependency - preg_match("/\s*'{$funcParam['uid']}'([\S\s]*?)\),/", $code, $match1); - unset($code); - // make PHP function known - eval($match[ 0 ]); - if (function_exists($_function)) { - // search cache file template - $tplPtr = $tpl; - while (!isset($tplPtr->cached) && isset($tplPtr->parent)) { - $tplPtr = $tplPtr->parent; - } - // add template function code to cache file - if (isset($tplPtr->cached)) { - $content = $tplPtr->cached->read($tplPtr); - if ($content) { - // check if we must update file dependency - if (!preg_match("/'{$funcParam['uid']}'(.*?)'nocache_hash'/", $content, $match2)) { - $content = preg_replace("/('file_dependency'(.*?)\()/", "\\1{$match1[0]}", $content); - } - $tplPtr->smarty->ext->_updateCache->write( - $tplPtr, - preg_replace('/\s*\?>\s*$/', "\n", $content) . - "\n" . preg_replace( - array( - '/^\s*<\?php\s+/', - '/\s*\?>\s*$/', - ), - "\n", - $match[ 0 ] - ) - ); - } - } - return true; - } - } - } - return false; - } - - /** - * Save current template variables on stack - * - * @param \Smarty_Internal_Template $tpl - * @param string $name stack name - */ - public function saveTemplateVariables(Smarty_Internal_Template $tpl, $name) - { - $tpl->_var_stack[] = - array('tpl' => $tpl->tpl_vars, 'config' => $tpl->config_vars, 'name' => "_tplFunction_{$name}"); - } - - /** - * Restore saved variables into template objects - * - * @param \Smarty_Internal_Template $tpl - * @param string $name stack name - */ - public function restoreTemplateVariables(Smarty_Internal_Template $tpl, $name) - { - if (isset($tpl->_var_stack)) { - $vars = array_pop($tpl->_var_stack); - $tpl->tpl_vars = $vars[ 'tpl' ]; - $tpl->config_vars = $vars[ 'config' ]; - } - } -} diff --git a/src/Runtime/smarty_internal_runtime_updatecache.php b/src/Runtime/smarty_internal_runtime_updatecache.php deleted file mode 100644 index c4511345..00000000 --- a/src/Runtime/smarty_internal_runtime_updatecache.php +++ /dev/null @@ -1,184 +0,0 @@ -compiled)) { - $_template->loadCompiled(); - } - $_template->compiled->render($_template); - if ($_template->smarty->debugging) { - $_template->smarty->_debug->start_cache($_template); - } - $this->removeNoCacheHash($cached, $_template, $no_output_filter); - $compile_check = (int)$_template->compile_check; - $_template->compile_check = \Smarty\Smarty::COMPILECHECK_OFF; - if ($_template->_isSubTpl()) { - $_template->compiled->unifunc = $_template->parent->compiled->unifunc; - } - if (!$_template->cached->processed) { - $_template->cached->process($_template, true); - } - $_template->compile_check = $compile_check; - $cached->getRenderedTemplateCode($_template); - if ($_template->smarty->debugging) { - $_template->smarty->_debug->end_cache($_template); - } - } - - /** - * Sanitize content and write it to cache resource - * - * @param \Smarty_Template_Cached $cached - * @param Smarty_Internal_Template $_template - * @param bool $no_output_filter - * - * @throws \SmartyException - */ - private function removeNoCacheHash( - Smarty_Template_Cached $cached, - Smarty_Internal_Template $_template, - $no_output_filter - ) { - $php_pattern = '/(<%|%>|<\?php|<\?|\?>|)/'; - $content = ob_get_clean(); - $hash_array = $cached->hashes; - $hash_array[ $_template->compiled->nocache_hash ] = true; - $hash_array = array_keys($hash_array); - $nocache_hash = '(' . implode('|', $hash_array) . ')'; - $_template->cached->has_nocache_code = false; - // get text between non-cached items - $cache_split = - preg_split( - "!/\*%%SmartyNocache:{$nocache_hash}%%\*\/(.+?)/\*/%%SmartyNocache:{$nocache_hash}%%\*/!s", - $content - ); - // get non-cached items - preg_match_all( - "!/\*%%SmartyNocache:{$nocache_hash}%%\*\/(.+?)/\*/%%SmartyNocache:{$nocache_hash}%%\*/!s", - $content, - $cache_parts - ); - $content = ''; - // loop over items, stitch back together - foreach ($cache_split as $curr_idx => $curr_split) { - if (preg_match($php_pattern, $curr_split)) { - // escape PHP tags in template content - $php_split = preg_split( - $php_pattern, - $curr_split - ); - preg_match_all( - $php_pattern, - $curr_split, - $php_parts - ); - foreach ($php_split as $idx_php => $curr_php) { - $content .= $curr_php; - if (isset($php_parts[ 0 ][ $idx_php ])) { - $content .= "\n"; - } - } - } else { - $content .= $curr_split; - } - if (isset($cache_parts[ 0 ][ $curr_idx ])) { - $_template->cached->has_nocache_code = true; - $content .= $cache_parts[ 2 ][ $curr_idx ]; - } - } - if ( - !$no_output_filter - && !$_template->cached->has_nocache_code - && isset($_template->smarty->registered_filters[ 'output' ]) - ) { - $content = $_template->smarty->ext->_filterHandler->runFilter('output', $content, $_template); - } - // write cache file content - $this->writeCachedContent($_template, $content); - } - - /** - * Writes the content to cache resource - * - * @param Smarty_Internal_Template $_template - * @param string $content - * - * @return bool - */ - public function writeCachedContent(Smarty_Internal_Template $_template, $content) - { - if ($_template->source->handler->recompiled || !$_template->caching - ) { - // don't write cache file - return false; - } - if (!isset($_template->cached)) { - $_template->loadCached(); - } - $content = $_template->createCodeFrame($content, '', true); - return $this->write($_template, $content); - } - - /** - * Write this cache object to handler - * - * @param Smarty_Internal_Template $_template template object - * @param string $content content to cache - * - * @return bool success - */ - public function write(Smarty_Internal_Template $_template, $content) - { - if (!$_template->source->handler->recompiled) { - $cached = $_template->cached; - if ($cached->handler->writeCachedContent($_template, $content)) { - $cached->content = null; - $cached->timestamp = time(); - $cached->exists = true; - $cached->valid = true; - $cached->cache_lifetime = $_template->cache_lifetime; - $cached->processed = false; - if ($_template->smarty->cache_locking) { - $cached->handler->releaseLock($_template->smarty, $cached); - } - return true; - } - $cached->content = null; - $cached->timestamp = false; - $cached->exists = false; - $cached->valid = false; - $cached->processed = false; - } - return false; - } -} diff --git a/src/Runtime/smarty_internal_runtime_updatescope.php b/src/Runtime/smarty_internal_runtime_updatescope.php deleted file mode 100644 index 8cda8c8a..00000000 --- a/src/Runtime/smarty_internal_runtime_updatescope.php +++ /dev/null @@ -1,115 +0,0 @@ -_updateVarStack($tpl, $varName); - $tagScope = $tagScope & ~\Smarty\Smarty::SCOPE_LOCAL; - if (!$tpl->scope && !$tagScope) { - return; - } - } - $mergedScope = $tagScope | $tpl->scope; - if ($mergedScope) { - if ($mergedScope & \Smarty\Smarty::SCOPE_GLOBAL && $varName) { - \Smarty\Smarty::$global_tpl_vars[ $varName ] = $tpl->tpl_vars[ $varName ]; - } - // update scopes - foreach ($this->_getAffectedScopes($tpl, $mergedScope) as $ptr) { - $this->_updateVariableInOtherScope($ptr->tpl_vars, $tpl, $varName); - if ($tagScope && $ptr->_isTplObj() && isset($tpl->_var_stack)) { - $this->_updateVarStack($ptr, $varName); - } - } - } - } - - /** - * Get array of objects which needs to be updated by given scope value - * - * @param Smarty_Internal_Template $tpl - * @param int $mergedScope merged tag and template scope to which bubble up variable value - * - * @return array - */ - public function _getAffectedScopes(Smarty_Internal_Template $tpl, $mergedScope) - { - $_stack = array(); - $ptr = $tpl->parent; - if ($mergedScope && isset($ptr) && $ptr->_isTplObj()) { - $_stack[] = $ptr; - $mergedScope = $mergedScope & ~\Smarty\Smarty::SCOPE_PARENT; - if (!$mergedScope) { - // only parent was set, we are done - return $_stack; - } - $ptr = $ptr->parent; - } - while (isset($ptr) && $ptr->_isTplObj()) { - $_stack[] = $ptr; - $ptr = $ptr->parent; - } - if ($mergedScope & \Smarty\Smarty::SCOPE_SMARTY) { - if (isset($tpl->smarty)) { - $_stack[] = $tpl->smarty; - } - } elseif ($mergedScope & \Smarty\Smarty::SCOPE_ROOT) { - while (isset($ptr)) { - if (!$ptr->_isTplObj()) { - $_stack[] = $ptr; - break; - } - $ptr = $ptr->parent; - } - } - return $_stack; - } - - /** - * Update variable in other scope - * - * @param array $tpl_vars template variable array - * @param \Smarty_Internal_Template $from - * @param string $varName variable name - */ - public function _updateVariableInOtherScope(&$tpl_vars, Smarty_Internal_Template $from, $varName) - { - if (!isset($tpl_vars[ $varName ])) { - $tpl_vars[ $varName ] = clone $from->tpl_vars[ $varName ]; - } else { - $tpl_vars[ $varName ] = clone $tpl_vars[ $varName ]; - $tpl_vars[ $varName ]->value = $from->tpl_vars[ $varName ]->value; - } - } - - /** - * Update variable in template local variable stack - * - * @param \Smarty_Internal_Template $tpl - * @param string|null $varName variable name or null for config variables - */ - public function _updateVarStack(Smarty_Internal_Template $tpl, $varName) - { - $i = 0; - while (isset($tpl->_var_stack[ $i ])) { - $this->_updateVariableInOtherScope($tpl->_var_stack[ $i ][ 'tpl' ], $tpl, $varName); - $i++; - } - } -} diff --git a/src/Runtime/smarty_internal_runtime_writefile.php b/src/Runtime/smarty_internal_runtime_writefile.php deleted file mode 100644 index 8d8e7ba2..00000000 --- a/src/Runtime/smarty_internal_runtime_writefile.php +++ /dev/null @@ -1,91 +0,0 @@ -_cache = array(); - $_smarty->ext = new Smarty_Internal_Extension_Handler(); - $_smarty->ext->objType = $_smarty->_objType; $_smarty->force_compile = $force_compile; try { /* @var Smarty_Internal_Template $_tpl */ @@ -1499,4 +1503,364 @@ class Smarty extends \Smarty_Internal_TemplateBase return $_count; } + /** + * check client side cache + * + * @param \Smarty_Template_Cached $cached + * @param \Smarty_Internal_Template $_template + * @param string $content + * + * @throws \Exception + * @throws \SmartyException + */ + public function cacheModifiedCheck(\Smarty_Template_Cached $cached, \Smarty_Internal_Template $_template, $content) + { + $_isCached = $_template->isCached() && !$_template->compiled->has_nocache_code; + $_last_modified_date = + @substr($_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ], 0, strpos($_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ], 'GMT') + 3); + if ($_isCached && $cached->timestamp <= strtotime($_last_modified_date)) { + switch (PHP_SAPI) { + case 'cgi': // php-cgi < 5.3 + case 'cgi-fcgi': // php-cgi >= 5.3 + case 'fpm-fcgi': // php-fpm >= 5.3.3 + header('Status: 304 Not Modified'); + break; + case 'cli': + if (/* ^phpunit */ + !empty($_SERVER[ 'SMARTY_PHPUNIT_DISABLE_HEADERS' ]) /* phpunit$ */ + ) { + $_SERVER[ 'SMARTY_PHPUNIT_HEADERS' ][] = '304 Not Modified'; + } + break; + default: + if (/* ^phpunit */ + !empty($_SERVER[ 'SMARTY_PHPUNIT_DISABLE_HEADERS' ]) /* phpunit$ */ + ) { + $_SERVER[ 'SMARTY_PHPUNIT_HEADERS' ][] = '304 Not Modified'; + } else { + header($_SERVER[ 'SERVER_PROTOCOL' ] . ' 304 Not Modified'); + } + break; + } + } else { + switch (PHP_SAPI) { + case 'cli': + if (/* ^phpunit */ + !empty($_SERVER[ 'SMARTY_PHPUNIT_DISABLE_HEADERS' ]) /* phpunit$ */ + ) { + $_SERVER[ 'SMARTY_PHPUNIT_HEADERS' ][] = + 'Last-Modified: ' . gmdate('D, d M Y H:i:s', $cached->timestamp) . ' GMT'; + } + break; + default: + header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $cached->timestamp) . ' GMT'); + break; + } + echo $content; + } + } + + /** + * Run filters over content + * The filters will be lazy loaded if required + * class name format: Smarty_FilterType_FilterName + * plugin filename format: filtertype.filtername.php + * Smarty2 filter plugins could be used + * + * @param string $type the type of filter ('pre','post','output') which shall run + * @param string $content the content which shall be processed by the filters + * @param Smarty_Internal_Template $template template object + * + * @throws SmartyException + * @return string the filtered content + */ + public function runFilter($type, $content, Smarty_Internal_Template $template) + { + // loop over registered filters of specified type + if (!empty($this->registered_filters[ $type ])) { + foreach ($this->registered_filters[ $type ] as $key => $name) { + $content = call_user_func($this->registered_filters[ $type ][ $key ], $content, $template); + } + } + // return filtered output + return $content; + } + + /** + * include path cache + * + * @var string + */ + private $_include_path = ''; + + /** + * include path directory cache + * + * @var array + */ + private $_include_dirs = array(); + + /** + * include path directory cache + * + * @var array + */ + private $_user_dirs = array(); + + /** + * stream cache + * + * @var string[][] + */ + private $isFile = array(); + + /** + * stream cache + * + * @var string[] + */ + private $isPath = array(); + + /** + * stream cache + * + * @var int[] + */ + private $number = array(); + + /** + * status cache + * + * @var bool + */ + private $_has_stream_include = null; + + /** + * Number for array index + * + * @var int + */ + private $counter = 0; + + /** + * Check if include path was updated + * + * @return bool + */ + private function isNewIncludePath() + { + $_i_path = get_include_path(); + if ($this->_include_path !== $_i_path) { + $this->_include_dirs = array(); + $this->_include_path = $_i_path; + $_dirs = (array)explode(PATH_SEPARATOR, $_i_path); + foreach ($_dirs as $_path) { + if (is_dir($_path)) { + $this->_include_dirs[] = $this->_realpath($_path . DIRECTORY_SEPARATOR, true); + } + } + return true; + } + return false; + } + + /** + * return array with include path directories + * + * @return array + */ + public function getIncludePathDirs() + { + $this->isNewIncludePath(); + return $this->_include_dirs; + } + + /** + * Return full file path from PHP include_path + * + * @param string[] $dirs + * @param string $file + * + * @return bool|string full filepath or false + */ + public function getIncludePath($dirs, $file) + { + if (!($this->_has_stream_include ?? $this->_has_stream_include = function_exists('stream_resolve_include_path')) + ) { + $this->isNewIncludePath(); + } + // try PHP include_path + foreach ($dirs as $dir) { + $dir_n = $this->number[$dir] ?? $this->number[$dir] = $this->counter++; + if (isset($this->isFile[ $dir_n ][ $file ])) { + if ($this->isFile[ $dir_n ][ $file ]) { + return $this->isFile[ $dir_n ][ $file ]; + } else { + continue; + } + } + if (isset($this->_user_dirs[ $dir_n ])) { + if (false === $this->_user_dirs[ $dir_n ]) { + continue; + } else { + $dir = $this->_user_dirs[ $dir_n ]; + } + } else { + if ($dir[ 0 ] === '/' || $dir[ 1 ] === ':') { + $dir = str_ireplace(getcwd(), '.', $dir); + if ($dir[ 0 ] === '/' || $dir[ 1 ] === ':') { + $this->_user_dirs[ $dir_n ] = false; + continue; + } + } + $dir = substr($dir, 2); + $this->_user_dirs[ $dir_n ] = $dir; + } + if ($this->_has_stream_include) { + $path = stream_resolve_include_path($dir . ($file ?? '')); + if ($path) { + return $this->isFile[ $dir_n ][ $file ] = $path; + } + } else { + foreach ($this->_include_dirs as $key => $_i_path) { + $path = $this->isPath[$key][$dir_n] ?? $this->isPath[$key][$dir_n] = is_dir($_dir_path = $_i_path . $dir) ? $_dir_path : false; + if ($path === false) { + continue; + } + if (isset($file)) { + $_file = $this->isFile[ $dir_n ][ $file ] = (is_file($path . $file)) ? $path . $file : false; + if ($_file) { + return $_file; + } + } else { + // no file was given return directory path + return $path; + } + } + } + } + return false; + } + + /** + * Writes file in a safe way to disk + * + * @param string $_filepath complete filepath + * @param string $_contents file content + * + * @throws SmartyException + * @return boolean true + */ + public function writeFile($_filepath, $_contents) + { + $_error_reporting = error_reporting(); + error_reporting($_error_reporting & ~E_NOTICE & ~E_WARNING); + $_dirpath = dirname($_filepath); + // if subdirs, create dir structure + if ($_dirpath !== '.') { + $i = 0; + // loop if concurrency problem occurs + // see https://bugs.php.net/bug.php?id=35326 + while (!is_dir($_dirpath)) { + if (@mkdir($_dirpath, 0777, true)) { + break; + } + clearstatcache(); + if (++$i === 3) { + error_reporting($_error_reporting); + throw new SmartyException("unable to create directory {$_dirpath}"); + } + sleep(1); + } + } + // write to tmp file, then move to overt file lock race condition + $_tmp_file = $_dirpath . DIRECTORY_SEPARATOR . str_replace(array('.', ','), '_', uniqid('wrt', true)); + if (!file_put_contents($_tmp_file, $_contents)) { + error_reporting($_error_reporting); + throw new SmartyException("unable to write file {$_tmp_file}"); + } + /* + * Windows' rename() fails if the destination exists, + * Linux' rename() properly handles the overwrite. + * Simply unlink()ing a file might cause other processes + * currently reading that file to fail, but linux' rename() + * seems to be smart enough to handle that for us. + */ + if (\Smarty\Smarty::$_IS_WINDOWS) { + // remove original file + if (is_file($_filepath)) { + @unlink($_filepath); + } + // rename tmp file + $success = @rename($_tmp_file, $_filepath); + } else { + // rename tmp file + $success = @rename($_tmp_file, $_filepath); + if (!$success) { + // remove original file + if (is_file($_filepath)) { + @unlink($_filepath); + } + // rename tmp file + $success = @rename($_tmp_file, $_filepath); + } + } + if (!$success) { + error_reporting($_error_reporting); + throw new SmartyException("unable to write file {$_filepath}"); + } + // set file permissions + @chmod($_filepath, 0666 & ~umask()); + error_reporting($_error_reporting); + return true; + } + + + private $runtimes = []; + + /** + * Loads and returns a runtime extension or null if not found + * @param string $type + * + * @return object|null + */ + public function getRuntime(string $type) { + + if (isset($this->runtimes[$type])) { + return $this->runtimes[$type]; + } + + // Lazy load runtimes when/if needed + switch ($type) { + case 'Capture': + return $this->runtimes[$type] = new Smarty\Runtime\CaptureRuntime(); + case 'Foreach': + return $this->runtimes[$type] = new Smarty\Runtime\ForeachRuntime(); + case 'Inheritance': + return $this->runtimes[$type] = new Smarty\Runtime\InheritanceRuntime(); + case 'MakeNocache': + return $this->runtimes[$type] = new Smarty\Runtime\MakeNocacheRuntime(); + case 'TplFunction': + return $this->runtimes[$type] = new Smarty\Runtime\TplFunctionRuntime(); + } + + throw new \SmartyException('Trying to load invalid runtime ' . $type); + } + + /** + * Indicates if a runtime is available. + * + * @param string $type + * + * @return bool + */ + public function hasRuntime(string $type): bool { + try { + $this->getRuntime($type); + return true; + } catch (\SmartyException $e) { + return false; + } + } + } diff --git a/src/Template/smarty_template_cached.php b/src/Template/smarty_template_cached.php index 4ff1f019..c6ca154f 100644 --- a/src/Template/smarty_template_cached.php +++ b/src/Template/smarty_template_cached.php @@ -137,7 +137,7 @@ class Smarty_Template_Cached extends Smarty_Template_Resource_Base } return; } else { - $_template->smarty->ext->_updateCache->updateCache($this, $_template, $no_output_filter); + $this->updateCache($_template, $no_output_filter); } } @@ -244,11 +244,144 @@ class Smarty_Template_Cached extends Smarty_Template_Resource_Base * * @return string|false content */ - public function read(Smarty_Internal_Template $_template) + public function readCache(Smarty_Internal_Template $_template) { if (!$_template->source->handler->recompiled) { - return $this->handler->readCachedContent($_template); + return $this->handler->retrieveCachedContent($_template); } return false; } + + /** + * Write this cache object to handler + * + * @param string $content content to cache + * + * @return bool success + */ + public function writeCache(Smarty_Internal_Template $_template, $content) + { + if (!$_template->source->handler->recompiled) { + if ($this->handler->storeCachedContent($_template, $content)) { + $this->content = null; + $this->timestamp = time(); + $this->exists = true; + $this->valid = true; + $this->cache_lifetime = $_template->cache_lifetime; + $this->processed = false; + if ($_template->smarty->cache_locking) { + $this->handler->releaseLock($_template->smarty, $this); + } + return true; + } + $this->content = null; + $this->timestamp = false; + $this->exists = false; + $this->valid = false; + $this->processed = false; + } + return false; + } + + /** + * Cache was invalid , so render from compiled and write to cache + * + * @param \Smarty_Template_Cached $cached + * @param $no_output_filter + * + * @throws \Exception + */ + public function updateCache(Smarty_Internal_Template $_template, $no_output_filter) + { + ob_start(); + if (!isset($_template->compiled)) { + $_template->loadCompiled(); + } + $_template->compiled->render($_template); + if ($_template->smarty->debugging) { + $_template->smarty->_debug->start_cache($_template); + } + $this->removeNoCacheHash($_template, $no_output_filter); + $compile_check = (int)$_template->compile_check; + $_template->compile_check = \Smarty\Smarty::COMPILECHECK_OFF; + if ($_template->_isSubTpl()) { + $_template->compiled->unifunc = $_template->parent->compiled->unifunc; + } + if (!$_template->cached->processed) { + $_template->cached->process($_template, true); + } + $_template->compile_check = $compile_check; + $this->getRenderedTemplateCode($_template); + if ($_template->smarty->debugging) { + $_template->smarty->_debug->end_cache($_template); + } + } + + /** + * Sanitize content and write it to cache resource + * + * @param Smarty_Internal_Template $_template + * @param bool $no_output_filter + * + * @throws \SmartyException + */ + private function removeNoCacheHash(Smarty_Internal_Template $_template, $no_output_filter) { + $php_pattern = '/(<%|%>|<\?php|<\?|\?>|)/'; + $content = ob_get_clean(); + $hash_array = $this->hashes; + $hash_array[ $_template->compiled->nocache_hash ] = true; + $hash_array = array_keys($hash_array); + $nocache_hash = '(' . implode('|', $hash_array) . ')'; + $_template->cached->has_nocache_code = false; + // get text between non-cached items + $cache_split = + preg_split( + "!/\*%%SmartyNocache:{$nocache_hash}%%\*\/(.+?)/\*/%%SmartyNocache:{$nocache_hash}%%\*/!s", + $content + ); + // get non-cached items + preg_match_all( + "!/\*%%SmartyNocache:{$nocache_hash}%%\*\/(.+?)/\*/%%SmartyNocache:{$nocache_hash}%%\*/!s", + $content, + $cache_parts + ); + $content = ''; + // loop over items, stitch back together + foreach ($cache_split as $curr_idx => $curr_split) { + if (preg_match($php_pattern, $curr_split)) { + // escape PHP tags in template content + $php_split = preg_split( + $php_pattern, + $curr_split + ); + preg_match_all( + $php_pattern, + $curr_split, + $php_parts + ); + foreach ($php_split as $idx_php => $curr_php) { + $content .= $curr_php; + if (isset($php_parts[ 0 ][ $idx_php ])) { + $content .= "\n"; + } + } + } else { + $content .= $curr_split; + } + if (isset($cache_parts[ 0 ][ $curr_idx ])) { + $_template->cached->has_nocache_code = true; + $content .= $cache_parts[ 2 ][ $curr_idx ]; + } + } + if ( + !$no_output_filter + && !$_template->cached->has_nocache_code + && isset($_template->smarty->registered_filters[ 'output' ]) + ) { + $content = $_template->smarty->runFilter('output', $content, $_template); + } + // write cache file content + $_template->writeCachedContent($content); + } + } diff --git a/src/Template/smarty_template_compiled.php b/src/Template/smarty_template_compiled.php index 5c2fc9b0..3c633b82 100644 --- a/src/Template/smarty_template_compiled.php +++ b/src/Template/smarty_template_compiled.php @@ -206,7 +206,7 @@ class Smarty_Template_Compiled extends Smarty_Template_Resource_Base public function write(Smarty_Internal_Template $_template, $code) { if (!$_template->source->handler->recompiled) { - if ($_template->smarty->ext->_writeFile->writeFile($this->filepath, $code, $_template->smarty) === true) { + if ($_template->smarty->writeFile($this->filepath, $code) === true) { $this->timestamp = $this->exists = is_file($this->filepath); if ($this->exists) { $this->timestamp = filemtime($this->filepath); diff --git a/src/TestInstall.php b/src/TestInstall.php index c5819fb6..dfcf3b47 100644 --- a/src/TestInstall.php +++ b/src/TestInstall.php @@ -48,7 +48,7 @@ class TestInstall if ($_stream_resolve_include_path) { $template_dir = stream_resolve_include_path($_template_dir); } else { - $template_dir = $smarty->ext->_getIncludePath->getIncludePath($_template_dir, null, $smarty); + $template_dir = $smarty->getIncludePath($_template_dir, null); } if ($template_dir !== false) { if ($errors === null) { @@ -201,7 +201,7 @@ class TestInstall if ($_stream_resolve_include_path) { $config_dir = stream_resolve_include_path($_config_dir); } else { - $config_dir = $smarty->ext->_getIncludePath->getIncludePath($_config_dir, null, $smarty); + $config_dir = $smarty->getIncludePath($_config_dir, null); } if ($config_dir !== false) { if ($errors === null) { diff --git a/src/smarty_internal_extension_handler.php b/src/smarty_internal_extension_handler.php deleted file mode 100644 index 9ffa35f9..00000000 --- a/src/smarty_internal_extension_handler.php +++ /dev/null @@ -1,195 +0,0 @@ - 0, 'ConfigVars' => 0, - 'DebugTemplate' => 0, 'RegisteredObject' => 0, 'StreamVariable' => 0, - 'TemplateVars' => 0, 'Literals' => 'Literals', - );// - - private $resolvedProperties = array(); - - /** - * Call external Method - * - * @param \Smarty\Data $data - * @param string $name external method names - * @param array $args argument array - * - * @return mixed - */ - public function _callExternalMethod(\Smarty\Data $data, $name, $args) - { - /* @var Smarty $data ->smarty */ - $smarty = isset($data->smarty) ? $data->smarty : $data; - if (!isset($smarty->ext->$name)) { - if (preg_match('/^((set|get)|(.*?))([A-Z].*)$/', $name, $match)) { - $basename = $this->upperCase($match[ 4 ]); - if (!isset($smarty->ext->$basename) && isset($this->_property_info[ $basename ]) - && is_string($this->_property_info[ $basename ]) - ) { - $class = 'Smarty_Internal_Method_' . $this->_property_info[ $basename ]; - if (class_exists($class)) { - $classObj = new $class(); - $methodes = get_class_methods($classObj); - foreach ($methodes as $method) { - $smarty->ext->$method = $classObj; - } - } - } - if (!empty($match[ 2 ]) && !isset($smarty->ext->$name)) { - $class = 'Smarty_Internal_Method_' . $this->upperCase($name); - if (!class_exists($class)) { - $objType = $data->_objType; - $propertyType = false; - if (!isset($this->resolvedProperties[ $match[ 0 ] ][ $objType ])) { - $property = $this->resolvedProperties['property'][$basename] ?? - $this->resolvedProperties['property'][$basename] = smarty_strtolower_ascii( - join( - '_', - preg_split( - '/([A-Z][^A-Z]*)/', - $basename, - -1, - PREG_SPLIT_NO_EMPTY | - PREG_SPLIT_DELIM_CAPTURE - ) - ) - ); - if ($property !== false) { - if (property_exists($data, $property)) { - $propertyType = $this->resolvedProperties[ $match[ 0 ] ][ $objType ] = 1; - } elseif (property_exists($smarty, $property)) { - $propertyType = $this->resolvedProperties[ $match[ 0 ] ][ $objType ] = 2; - } else { - $this->resolvedProperties[ 'property' ][ $basename ] = $property = false; - } - } - } else { - $propertyType = $this->resolvedProperties[ $match[ 0 ] ][ $objType ]; - $property = $this->resolvedProperties[ 'property' ][ $basename ]; - } - if ($propertyType) { - $obj = $propertyType === 1 ? $data : $smarty; - if ($match[ 2 ] === 'get') { - return $obj->$property; - } elseif ($match[ 2 ] === 'set') { - return $obj->$property = $args[ 0 ]; - } - } - } - } - } - } - $callback = array($smarty->ext->$name, $name); - array_unshift($args, $data); - if (isset($callback) && $callback[ 0 ]->objMap | $data->_objType) { - return call_user_func_array($callback, $args); - } - return call_user_func_array(array(new Smarty_Internal_Undefined(), $name), $args); - } - - /** - * Make first character of name parts upper case - * - * @param string $name - * - * @return string - */ - public function upperCase($name) - { - $_name = explode('_', $name); - $_name = array_map('smarty_ucfirst_ascii', $_name); - return implode('_', $_name); - } - - /** - * get extension object - * - * @param string $property_name property name - * - * @return mixed|Smarty_Template_Cached - */ - public function __get($property_name) - { - // object properties of runtime template extensions will start with '_' - if ($property_name[ 0 ] === '_') { - $class = 'Smarty_Internal_Runtime' . $this->upperCase($property_name); - } else { - $class = 'Smarty_Internal_Method_' . $this->upperCase($property_name); - } - if (!class_exists($class)) { - return $this->$property_name = new Smarty_Internal_Undefined($class); - } - return $this->$property_name = new $class(); - } - - /** - * set extension property - * - * @param string $property_name property name - * @param mixed $value value - * - */ - public function __set($property_name, $value) - { - $this->$property_name = $value; - } - - /** - * Call error handler for undefined method - * - * @param string $name unknown method-name - * @param array $args argument array - * - * @return mixed - */ - public function __call($name, $args) - { - return call_user_func_array(array(new Smarty_Internal_Undefined(), $name), array($this)); - } -} diff --git a/src/smarty_internal_template.php b/src/smarty_internal_template.php index 0b1b5c2a..fcb7ebe2 100644 --- a/src/smarty_internal_template.php +++ b/src/smarty_internal_template.php @@ -9,6 +9,7 @@ */ use Smarty\Data; +use Smarty\Runtime\InheritanceRuntime; /** * Main class with template data structures and methods @@ -61,7 +62,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase /** * Inheritance runtime extension * - * @var Smarty_Internal_Runtime_Inheritance + * @var InheritanceRuntime */ public $inheritance = null; @@ -113,8 +114,12 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase * @var callback[] */ public $endRenderCallbacks = array(); + /** + * @var \Smarty\Compiler\CodeFrame + */ + private $codeFrameCompiler; - /** + /** * Create template data object * Some of the global Smarty settings copied to template scope * It load the required template resources and caching plugins @@ -158,6 +163,8 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase if ($smarty->security_policy && method_exists($smarty->security_policy, 'registerCallBacks')) { $smarty->security_policy->registerCallBacks($this); } + + $this->codeFrameCompiler = new Smarty\Compiler\CodeFrame($this); } /** @@ -206,7 +213,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase // display or fetch if ($display) { if ($this->caching && $this->smarty->cache_modified_check) { - $this->smarty->ext->_cacheModify->cacheModifiedCheck( + $this->smarty->cacheModifiedCheck( $this->cached, $this, isset($content) ? $content : ob_get_clean() @@ -215,7 +222,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase if ((!$this->caching || $this->cached->has_nocache_code || $this->source->handler->recompiled) && !$no_output_filter && isset($this->smarty->registered_filters[ 'output' ]) ) { - echo $this->smarty->ext->_filterHandler->runFilter('output', ob_get_clean(), $this); + echo $this->smarty->runFilter('output', ob_get_clean(), $this); } else { echo ob_get_clean(); } @@ -238,7 +245,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase && (!$this->caching || $this->cached->has_nocache_code || $this->source->handler->recompiled) && isset($this->smarty->registered_filters[ 'output' ]) ) { - return $this->smarty->ext->_filterHandler->runFilter('output', ob_get_clean(), $this); + return $this->smarty->runFilter('output', ob_get_clean(), $this); } // return cache content return null; @@ -419,7 +426,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase } if ($scope >= 0) { if ($scope > 0 || $this->scope > 0) { - $this->smarty->ext->_updateScope->_updateScope($this, $varName, $scope); + $this->_updateScope($varName, $scope); } } } @@ -520,7 +527,16 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase */ public function writeCachedContent($content) { - return $this->smarty->ext->_updateCache->writeCachedContent($this, $content); + if ($this->source->handler->recompiled || !$this->caching + ) { + // don't write cache file + return false; + } + if (!isset($this->cached)) { + $this->loadCached(); + } + $codeframe = $this->createCodeFrame($content, '', true); + return $this->cached->writeCache($this, $codeframe); } /** @@ -531,8 +547,8 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase */ public function _getTemplateId() { - return isset($this->templateId) ? $this->templateId : $this->templateId = - $this->smarty->_getTemplateId($this->template_resource, $this->cache_id, $this->compile_id); + return $this->templateId ?? $this->templateId = + $this->smarty->_getTemplateId($this->template_resource, $this->cache_id, $this->compile_id); } /** @@ -575,7 +591,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase public function _loadInheritance() { if (!isset($this->inheritance)) { - $this->inheritance = new Smarty_Internal_Runtime_Inheritance(); + $this->inheritance = new InheritanceRuntime(); } } @@ -615,7 +631,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase * @return string */ public function createCodeFrame($content = '', $functions = '', $cache = false, \Smarty\Compiler\Template $compiler = null) { - return \Smarty\Template\CodeFrame::create($this, $content, $functions, $cache, $compiler); + return $this->codeFrameCompiler->create($content, $functions, $cache, $compiler); } /** @@ -725,7 +741,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase if ($mergedScope) { // update scopes /* @var \Smarty_Internal_Template|\Smarty|Data $ptr */ - foreach ($this->smarty->ext->_updateScope->_getAffectedScopes($this->parent, $mergedScope) as $ptr) { + foreach ($this->parent->_getAffectedScopes($mergedScope) as $ptr) { $this->_assignConfigVars($ptr->config_vars, $new_config_vars); if ($tagScope && $ptr->_isTplObj() && isset($this->_var_stack)) { $this->_updateConfigVarStack($new_config_vars); @@ -811,4 +827,104 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase return $this->mustCompile; } + /** + * Update new assigned template or config variable in other effected scopes + * + * @param string|null $varName variable name + * @param int $tagScope tag scope to which bubble up variable value + */ + protected function _updateScope($varName, $tagScope = 0) + { + if ($tagScope) { + $this->_updateVarStack($this, $varName); + $tagScope = $tagScope & ~\Smarty\Smarty::SCOPE_LOCAL; + if (!$this->scope && !$tagScope) { + return; + } + } + $mergedScope = $tagScope | $this->scope; + if ($mergedScope) { + if ($mergedScope & \Smarty\Smarty::SCOPE_GLOBAL && $varName) { + \Smarty\Smarty::$global_tpl_vars[ $varName ] = $this->tpl_vars[ $varName ]; + } + // update scopes + foreach ($this->_getAffectedScopes($mergedScope) as $ptr) { + $this->_updateVariableInOtherScope($ptr->tpl_vars, $varName); + if ($tagScope && $ptr->_isTplObj() && isset($this->_var_stack)) { + $this->_updateVarStack($ptr, $varName); + } + } + } + } + + /** + * Get array of objects which needs to be updated by given scope value + * + * @param int $mergedScope merged tag and template scope to which bubble up variable value + * + * @return array + */ + private function _getAffectedScopes($mergedScope) + { + $_stack = array(); + $ptr = $this->parent; + if ($mergedScope && isset($ptr) && $ptr->_isTplObj()) { + $_stack[] = $ptr; + $mergedScope = $mergedScope & ~\Smarty\Smarty::SCOPE_PARENT; + if (!$mergedScope) { + // only parent was set, we are done + return $_stack; + } + $ptr = $ptr->parent; + } + while (isset($ptr) && $ptr->_isTplObj()) { + $_stack[] = $ptr; + $ptr = $ptr->parent; + } + if ($mergedScope & \Smarty\Smarty::SCOPE_SMARTY) { + if (isset($this->smarty)) { + $_stack[] = $this->smarty; + } + } elseif ($mergedScope & \Smarty\Smarty::SCOPE_ROOT) { + while (isset($ptr)) { + if (!$ptr->_isTplObj()) { + $_stack[] = $ptr; + break; + } + $ptr = $ptr->parent; + } + } + return $_stack; + } + + /** + * Update variable in other scope + * + * @param array $tpl_vars template variable array + * @param string $varName variable name + */ + private function _updateVariableInOtherScope(&$tpl_vars, $varName) + { + if (!isset($tpl_vars[ $varName ])) { + $tpl_vars[ $varName ] = clone $this->tpl_vars[ $varName ]; + } else { + $tpl_vars[ $varName ] = clone $tpl_vars[ $varName ]; + $tpl_vars[ $varName ]->value = $this->tpl_vars[ $varName ]->value; + } + } + + /** + * Update variable in template local variable stack + * + * @param \Smarty_Internal_Template $tpl + * @param string|null $varName variable name or null for config variables + */ + private function _updateVarStack(Smarty_Internal_Template $tpl, $varName) + { + $i = 0; + while (isset($tpl->_var_stack[ $i ])) { + $this->_updateVariableInOtherScope($tpl->_var_stack[ $i ][ 'tpl' ], $varName); + $i++; + } + } } diff --git a/src/sysplugins/smarty_security.php b/src/sysplugins/smarty_security.php index 97cd0521..59655c7b 100644 --- a/src/sysplugins/smarty_security.php +++ b/src/sysplugins/smarty_security.php @@ -498,7 +498,7 @@ class Smarty_Security { if ($this->_include_path_status !== $this->smarty->use_include_path) { $_dir = - $this->smarty->use_include_path ? $this->smarty->ext->_getIncludePath->getIncludePathDirs($this->smarty) : array(); + $this->smarty->use_include_path ? $this->smarty->getIncludePathDirs() : array(); if ($this->_include_dir !== $_dir) { $this->_updateResourceDir($this->_include_dir, $_dir); $this->_include_dir = $_dir;