diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06b5ebd3..e4d0cc96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed direct access to `$smarty->cache_dir`. Use `$smarty->setCacheDir()`.
- Removed `$smarty->loadPlugin()`, use `$smarty->registerPlugin()` instead.
- Removed `$smarty->appendByRef()` and `$smarty->assignByRef()`.
+- Removed `$smarty->use_sub_dirs`. Cached files are now automatically stored in subdirs for maximum performance.
### Fixed
- `$smarty->muteUndefinedOrNullWarnings()` now also mutes PHP7 notices for undefined array indexes [#736](https://github.com/smarty-php/smarty/issues/736)
diff --git a/TODO.txt b/TODO.txt
index d01b2fdf..53c16f0a 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -7,9 +7,8 @@
- Re-introduce merge_compiled_includes and the {include inline} attribute?
-## Output buffering
-- Fix ob_ output buffering commands being scattered around the codebase
-
+## Beatify output
+- strip tags from generated output
## Review public static vars
- such as _CHARSET and _IS_WINDOWS
@@ -28,8 +27,6 @@
## Plugin system
- fix template security checks in one place in compiler
-## Beatify output
-- compiled templates could be proper classes, possibly using [nette/php-generator](https://packagist.org/packages/nette/php-generator)
## Documentation
- beautify and review docs, possibly using [
diff --git a/src/Cacheresource/Base.php b/src/Cacheresource/Base.php
index 54a14192..5bcf843f 100644
--- a/src/Cacheresource/Base.php
+++ b/src/Cacheresource/Base.php
@@ -38,14 +38,12 @@ abstract class Base
*
* @param Template $_template template object
* @param Cached|null $cached cached object
- * @param boolean $update flag if called because cache update
*
* @return boolean true or false if the cached content does not exist
*/
abstract public function process(
Template $_template,
- Cached $cached = null,
- $update = false
+ Cached $cached = null
);
/**
diff --git a/src/Cacheresource/Custom.php b/src/Cacheresource/Custom.php
index c049246c..6081da87 100644
--- a/src/Cacheresource/Custom.php
+++ b/src/Cacheresource/Custom.php
@@ -9,6 +9,7 @@ namespace Smarty\Cacheresource;
*/
+use Smarty\Exception;
use Smarty\Smarty;
use Smarty\Template;
use Smarty\Template\Cached;
@@ -124,23 +125,13 @@ abstract class Custom extends Base
$cached->content,
$timestamp
);
- $cached->timestamp = isset($timestamp) ? $timestamp : false;
+ $cached->timestamp = $timestamp ?? false;
$cached->exists = !!$cached->timestamp;
}
- /**
- * Read the cached template and process the header
- *
- * @param Template $_smarty_tpl do not change variable name, is used by compiled template
- * @param Cached|null $cached cached object
- * @param boolean $update flag if called because cache update
- *
- * @return boolean true or false if the cached content does not exist
- */
public function process(
Template $_smarty_tpl,
- \Smarty\Template\Cached $cached = null,
- $update = false
+ \Smarty\Template\Cached $cached = null
) {
if (!$cached) {
$cached = $_smarty_tpl->getCached();
diff --git a/src/Cacheresource/File.php b/src/Cacheresource/File.php
index 54070013..a2cebee8 100644
--- a/src/Cacheresource/File.php
+++ b/src/Cacheresource/File.php
@@ -4,6 +4,7 @@ namespace Smarty\Cacheresource;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
+use Smarty\Exception;
use Smarty\Smarty;
use Smarty\Template;
use Smarty\Template\Cached;
@@ -35,7 +36,7 @@ class File extends Base
{
$source = $_template->getSource();
$smarty = $_template->getSmarty();
- $_compile_dir_sep = $smarty->use_sub_dirs ? DIRECTORY_SEPARATOR : '^';
+
$_filepath = sha1($source->uid . $smarty->_joined_template_dir);
$cached->filepath = $smarty->getCacheDir();
if (isset($_template->cache_id)) {
@@ -46,21 +47,17 @@ class File extends Base
),
array(
'_',
- $_compile_dir_sep
+ DIRECTORY_SEPARATOR
),
- $_template->cache_id
- ) . $_compile_dir_sep;
+ $_template->cache_id
+ ) . DIRECTORY_SEPARATOR;
}
if (isset($_template->compile_id)) {
- $cached->filepath .= preg_replace('![^\w]+!', '_', $_template->compile_id) . $_compile_dir_sep;
- }
- // if use_sub_dirs, break file into directories
- if ($smarty->use_sub_dirs) {
- $cached->filepath .= $_filepath[ 0 ] . $_filepath[ 1 ] . DIRECTORY_SEPARATOR . $_filepath[ 2 ] .
- $_filepath[ 3 ] .
- DIRECTORY_SEPARATOR .
- $_filepath[ 4 ] . $_filepath[ 5 ] . DIRECTORY_SEPARATOR;
+ $cached->filepath .= preg_replace('![^\w]+!', '_', $_template->compile_id) . DIRECTORY_SEPARATOR;
}
+ // break file into directories
+ $cached->filepath .= $_filepath[0] . $_filepath[1] . DIRECTORY_SEPARATOR;
+
$cached->filepath .= $_filepath;
$basename = $source->getBasename();
if (!empty($basename)) {
@@ -91,27 +88,11 @@ class File extends Base
}
}
- /**
- * Read the cached template and process its header
- *
- * @param Template $_smarty_tpl do not change variable name, is used by compiled template
- * @param Cached|null $cached cached object
- * @param bool $update flag if called because cache update
- *
- * @return boolean true or false if the cached content does not exist
- */
public function process(
Template $_smarty_tpl,
- Cached $cached = null,
- $update = false
+ Cached $cached = null
) {
- $_smarty_tpl->getCached()->setValid(false);
- if ($update && defined('HHVM_VERSION')) {
- eval('?>' . file_get_contents($_smarty_tpl->getCached()->filepath));
- return true;
- } else {
- return @include $_smarty_tpl->getCached()->filepath;
- }
+ return @include $_smarty_tpl->getCached()->filepath;
}
/**
@@ -186,8 +167,7 @@ class File extends Base
{
$_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;
+ $_compile_id_offset = 1;
$_dir = $smarty->getCacheDir();
if ($_dir === '/') { //We should never want to delete this!
return 0;
@@ -196,10 +176,8 @@ class File extends Base
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 . '/';
- }
+ foreach ($_cache_id_parts as $id_part) {
+ $_dir .= $id_part . DIRECTORY_SEPARATOR;
}
}
if (isset($resource_name)) {
@@ -235,7 +213,7 @@ class File extends Base
if (substr($_filepath, -4) !== '.php') {
continue;
}
- $_parts = explode($_dir_sep, str_replace('\\', '/', substr($_filepath, $_dir_length)));
+ $_parts = explode(DIRECTORY_SEPARATOR, str_replace('\\', '/', substr($_filepath, $_dir_length)));
$_parts_count = count($_parts);
// check name
if (isset($resource_name)) {
diff --git a/src/Cacheresource/KeyValueStore.php b/src/Cacheresource/KeyValueStore.php
index 733d2776..3e85a583 100644
--- a/src/Cacheresource/KeyValueStore.php
+++ b/src/Cacheresource/KeyValueStore.php
@@ -2,6 +2,7 @@
namespace Smarty\Cacheresource;
+use Smarty\Exception;
use Smarty\Smarty;
use Smarty\Template;
use Smarty\Template\Cached;
@@ -92,19 +93,9 @@ abstract class KeyValueStore extends Base
$cached->exists = !!$cached->timestamp;
}
- /**
- * Read the cached template and process the header
- *
- * @param Template $_smarty_tpl do not change variable name, is used by compiled template
- * @param Cached|null $cached cached object
- * @param boolean $update flag if called because cache update
- *
- * @return boolean true or false if the cached content does not exist
- */
public function process(
Template $_smarty_tpl,
- Cached $cached = null,
- $update = false
+ Cached $cached = null
) {
if (!$cached) {
$cached = $_smarty_tpl->getCached();
diff --git a/src/CodeFrame/Base.php b/src/CodeFrame/Base.php
index 061244e5..d9acd471 100644
--- a/src/CodeFrame/Base.php
+++ b/src/CodeFrame/Base.php
@@ -4,22 +4,5 @@ namespace Smarty\CodeFrame;
abstract class Base {
- /**
- * @var \Smarty\Smarty
- */
- private $smarty;
-
- public function __construct(\Smarty\Smarty $smarty) {
- $this->smarty = $smarty;
- }
-
- public function isFresh(\Smarty\Template $template): bool {
- return $template->isFresh($this->getProperties(), $this->isCache());
- }
-
- protected function isCache(): bool {
- return false;
- }
-
- abstract protected function getProperties(): array;
+ abstract public function renderContent(\Smarty\Template $_smarty_tpl): void;
}
\ No newline at end of file
diff --git a/src/CodeFrame/Cached.php b/src/CodeFrame/Cached.php
index d08b1001..1e40afb0 100644
--- a/src/CodeFrame/Cached.php
+++ b/src/CodeFrame/Cached.php
@@ -4,7 +4,29 @@ namespace Smarty\CodeFrame;
abstract class Cached extends Base {
- protected function isCache(): bool {
+ // @TODO move
+ public function isFresh(\Smarty\Template $template): bool {
+
+ $properties = $this->getProperties();
+
+ // on cache resources other than file check version stored in cache code
+ if (\Smarty\Smarty::SMARTY_VERSION !== $properties['version']) {
+ return false;
+ }
+
+ if ($template->getSmarty()->getCompileCheck() === \Smarty\Smarty::COMPILECHECK_ON) {
+ if (!$this->checkFileDependencies($properties['file_dependency'], $template)) {
+ return false;
+ }
+ }
+
+ // CACHING_LIFETIME_SAVED cache expiry has to be validated here since otherwise we'd define the unifunc
+ if ($template->caching === \Smarty\Smarty::CACHING_LIFETIME_SAVED && $properties['cache_lifetime'] >= 0
+ && (time() > ($this->timestamp + $properties['cache_lifetime']))
+ ) {
+ return false;
+ }
+
return true;
}
}
\ No newline at end of file
diff --git a/src/CodeFrame/Compiled.php b/src/CodeFrame/Compiled.php
new file mode 100644
index 00000000..0fa25989
--- /dev/null
+++ b/src/CodeFrame/Compiled.php
@@ -0,0 +1,7 @@
+getParentCompiler();
- $parentCompiler->tpl_function[$_name]['compiled_filepath'] =
- $parentCompiler->getTemplate()->getCompiled()->filepath;
$parentCompiler->tpl_function[$_name]['uid'] = $compiler->getTemplate()->getSource()->uid;
$_parameter = $_attr;
unset($_parameter['name']);
diff --git a/src/Compiler/CodeFrame.php b/src/Compiler/CodeFrame.php
index 10544752..73f11fd7 100644
--- a/src/Compiler/CodeFrame.php
+++ b/src/Compiler/CodeFrame.php
@@ -2,6 +2,7 @@
namespace Smarty\Compiler;
+use Nette\PhpGenerator\Dumper;
use Smarty\Exception;
/**
@@ -32,7 +33,6 @@ class CodeFrame
* @param string $content optional template content
* @param string $functions compiled template function and block code
* @param bool $cache flag for cache file
- * @param Template|null $compiler
*
* @return string
* @throws Exception
@@ -40,106 +40,122 @@ class CodeFrame
public function create(
$content = '',
$functions = '',
- $cache = false,
- \Smarty\Compiler\Template $compiler = null
+ $cache = false
) {
- $className = ($cache ? 'Cached' : 'Compiled') . str_replace(array('.', ','), '_', uniqid('', true));
$properties = [];
$properties['version'] = \Smarty\Smarty::SMARTY_VERSION;
+ $properties['codeFrameClass'] = '__CodeFrame_' . str_replace(array('.', ','), '_', uniqid('', true));
+ if (!$cache) {
+ $properties[ 'has_nocache_code' ] = $this->_template->getCompiled()->getNocacheCode();
+ $properties[ 'file_dependency' ] = $this->_template->getCompiled()->file_dependency;
+ $properties[ 'includes' ] = $this->_template->getCompiled()->includes;
+ } else {
+ $properties[ 'file_dependency' ] = $this->_template->getCached()->file_dependency;
+ $properties[ 'cache_lifetime' ] = $this->_template->cache_lifetime;
+ }
$file = new \Nette\PhpGenerator\PhpFile;
$file->addComment('This file is auto-generated.');
- $class = $file->addClass($className);
+ $file->addComment($functions); //@TODO
+ $output = (string) $file;
+ $dumper = new Dumper;
+
+ $output .= 'if (!class_exists(' . $dumper->dump($properties['codeFrameClass']) . ')) {' . "\n";
+ $output .= $this->generateCodeFrameClass($properties['codeFrameClass'], $cache, $content);
+ $output .= "}\n";
+ $output .= 'return ' . $dumper->dump($properties) . ";\n";
+
+ return $output;
+ }
+
+ private function revertPHPTags(string $content) {
+
+ static $PHPSTART = '<' . '?php';
+ static $PHPEND = '?' . '>';
+
+ //\<\?\php echo "
hello world";\?\>
+ if (substr($content, 0, 5) === $PHPSTART
+ && substr($content, -3) === $PHPEND . "\n"
+ ) {
+ return substr($content, 5, -3);
+ }
+
+ if (false) {
+ // remove unneeded PHP tags
+ if (preg_match('/\s*\?>[\n]?<\?php\s*/', $content)) {
+ $curr_split = preg_split(
+ '/\s*\?>[\n]?<\?php\s*/',
+ $content
+ );
+ preg_match_all(
+ '/\s*\?>[\n]?<\?php\s*/',
+ $content,
+ $curr_parts
+ );
+ $content = '';
+ foreach ($curr_split as $idx => $curr_output) {
+ $content .= $curr_output;
+ if (isset($curr_parts[ 0 ][ $idx ])) {
+ $content .= "\n";
+ }
+ }
+ }
+ if (preg_match('/\?>\s*$/', $content)) {
+ $curr_split = preg_split(
+ '/\?>\s*$/',
+ $content
+ );
+ $content = '';
+ foreach ($curr_split as $idx => $curr_output) {
+ $content .= $curr_output;
+ }
+ }
+ }
+
+ return $PHPEND . $content . $PHPSTART;
+ }
+
+ /**
+ * @param $codeFrameClass
+ * @param bool $cache
+ * @param string $content
+ *
+ * @return string
+ */
+ private function generateCodeFrameClass($codeFrameClass, bool $cache, string $content): string {
+ $class = new \Nette\PhpGenerator\ClassType($codeFrameClass);
$class
- ->setFinal()
- ->setExtends($cache ? \Smarty\CodeFrame\Cached::class : \Smarty\CodeFrame\Base::class)
+ ->setExtends($cache ? \Smarty\CodeFrame\Cached::class : \Smarty\CodeFrame\Compiled::class)
->addComment(sprintf(
"Created on %s from '%s'",
- $properties[ 'version' ],
date("Y-m-d H:i:s"),
str_replace('*/', '* /', $this->_template->getSource()->filepath)
));
- $dumper = new \Nette\PhpGenerator\Dumper;
-
- // build property code
- $properties[ 'unifunc' ] = 'content_' . str_replace(array('.', ','), '_', uniqid('', true));
- if (!$cache) {
- $properties[ 'has_nocache_code' ] = $this->_template->getCompiled()->getNocacheCode();
- $properties[ 'file_dependency' ] = $this->_template->getCompiled()->file_dependency;
- $properties[ 'includes' ] = $this->_template->getCompiled()->includes;
- } else {
- $properties[ 'has_nocache_code' ] = $this->_template->getCached()->getNocacheCode();
- $properties[ 'file_dependency' ] = $this->_template->getCached()->file_dependency;
- $properties[ 'cache_lifetime' ] = $this->_template->cache_lifetime;
- }
-
- $class->addMethod('getProperties')
- ->setProtected()
- ->setReturnType('array') // method return type
- ->setBody('return ' . $dumper->dump($properties) . ';');
-
- $output = (string) $file;
-
- $output .= sprintf(
- "\n/* Created on %s\n from '%s' */\n\n",
- $properties[ 'version' ],
- date("Y-m-d H:i:s"),
- str_replace('*/', '* /', $this->_template->getSource()->filepath)
- );
- $output .= "/* @var \\Smarty\\Template \$_smarty_tpl */\n";
- $dec = "\$_smarty_tpl->" . ($cache ? "getCached()" : "getCompiled()");
- $dec .= "->isFresh(\$_smarty_tpl, " . var_export($properties, true) . ')';
- $output .= "if ({$dec}) {\n";
- $output .= "function {$properties['unifunc']} (\\Smarty\\Template \$_smarty_tpl) {\n";
- if (!$cache && !empty($compiler->tpl_function)) {
+ /* @TODO
+ * if (!$cache && !empty($compiler->tpl_function)) {
$output .= '$_smarty_tpl->getSmarty()->getRuntime(\'TplFunction\')->registerTplFunctions($_smarty_tpl, ';
$output .= var_export($compiler->tpl_function, true);
$output .= ");\n";
}
- if ($cache && $this->_template->getSmarty()->hasRuntime('TplFunction')) {
+ if ($cache && $this->_template->getSmarty()->hasRuntime('TplFunction')) {
if ($tplfunctions = $this->_template->getSmarty()->getRuntime('TplFunction')->getTplFunction($this->_template)) {
$output .= "\$_smarty_tpl->getSmarty()->getRuntime('TplFunction')->registerTplFunctions(\$_smarty_tpl, " .
var_export($tplfunctions, true) . ");\n";
}
}
- $output .= "?>";
- $output .= $content;
- $output .= "";
- $output .= $functions;
- $output .= "[\n]?<\?php\s*/', $output)) {
- $curr_split = preg_split(
- '/\s*\?>[\n]?<\?php\s*/',
- $output
- );
- preg_match_all(
- '/\s*\?>[\n]?<\?php\s*/',
- $output,
- $curr_parts
- );
- $output = '';
- foreach ($curr_split as $idx => $curr_output) {
- $output .= $curr_output;
- if (isset($curr_parts[ 0 ][ $idx ])) {
- $output .= "\n";
- }
- }
- }
- if (preg_match('/\?>\s*$/', $output)) {
- $curr_split = preg_split(
- '/\?>\s*$/',
- $output
- );
- $output = '';
- foreach ($curr_split as $idx => $curr_output) {
- $output .= $curr_output;
- }
- }
- return $output;
- }
+*/
+ $method = $class->addMethod('renderContent');
+ $method->setPublic()
+ ->addParameter('_smarty_tpl')->setType(\Smarty\Template::class);
+
+ $method->setReturnType('void') // method return type
+ ->setBody($this->revertPHPTags($content));
+
+ return (new CompactPrinter())->printClass($class);
+ }
+
}
diff --git a/src/Compiler/CompactPrinter.php b/src/Compiler/CompactPrinter.php
new file mode 100644
index 00000000..ff32b057
--- /dev/null
+++ b/src/Compiler/CompactPrinter.php
@@ -0,0 +1,10 @@
+config_data['vars'] = [];
}
+ /**
+ * Method to compile a Smarty template
+ *
+ * @param \Smarty\Template $template template object to compile
+ *
+ * @return string code
+ * @throws Exception
+ */
+ public function compileTemplate(\Smarty\Template $template) {
+ return $template->createCodeFrame(
+ $this->compileTemplateSource($template),
+ '',
+ false
+ );
+ }
+
/**
* Method to compile Smarty config source.
*
@@ -80,7 +97,7 @@ class Configfile extends BaseCompiler {
* @return bool true if compiling succeeded, false if it failed
* @throws \Smarty\Exception
*/
- public function compileTemplate(Template $template) {
+ private function compileTemplateSource(Template $template) {
$this->template = $template;
$this->template->getCompiled()->file_dependency[$this->template->getSource()->uid] =
[
@@ -133,16 +150,10 @@ class Configfile extends BaseCompiler {
if ($this->smarty->debugging) {
$this->smarty->getDebug()->end_compile($this->template);
}
- // template header code
- $template_header = sprintf(
- "\n",
- \Smarty\Smarty::SMARTY_VERSION,
- date("Y-m-d H:i:s"),
- str_replace('*/', '* /', $this->template->getSource()->filepath)
- );
+
$code = 'parent->assignConfigVars(' .
var_export($this->config_data, true) . ', $_smarty_tpl->getValue("sections")); ?>';
- return $template_header . $this->template->createCodeFrame($code);
+ return $code;
}
/**
diff --git a/src/Compiler/Template.php b/src/Compiler/Template.php
index 9bf22fc8..5ed20dff 100644
--- a/src/Compiler/Template.php
+++ b/src/Compiler/Template.php
@@ -93,20 +93,6 @@ class Template extends BaseCompiler {
*/
private $template = null;
- /**
- * merged included sub template data
- *
- * @var array
- */
- public $mergedSubTemplatesData = [];
-
- /**
- * merged sub template code
- *
- * @var array
- */
- public $mergedSubTemplatesCode = [];
-
/**
* source line offset for error messages
*
@@ -358,10 +344,8 @@ class Template extends BaseCompiler {
public function compileTemplate(\Smarty\Template $template) {
return $template->createCodeFrame(
$this->compileTemplateSource($template),
- $this->smarty->runPostFilters($this->blockOrFunctionCode, $this->template) .
- join('', $this->mergedSubTemplatesCode),
- false,
- $this
+ $this->smarty->runPostFilters($this->blockOrFunctionCode, $this->template),
+ false
);
}
diff --git a/src/Data.php b/src/Data.php
index cd4fa3e7..a809a591 100644
--- a/src/Data.php
+++ b/src/Data.php
@@ -459,7 +459,7 @@ class Data
$template->caching = Smarty::CACHING_OFF;
$template->assign('sections', (array) $sections ?? []);
// trigger a call to $this->assignConfigVars
- $template->getCompiled(true)->render($template);
+ $template->getCompiled()->fetch();
return $this;
}
diff --git a/src/Debug.php b/src/Debug.php
index 4591ac46..bab05003 100644
--- a/src/Debug.php
+++ b/src/Debug.php
@@ -203,7 +203,6 @@ class Debug extends Data
$debObj = new \Smarty\Smarty();
// copy the working dirs from application
$debObj->setCompileDir($smarty->getCompileDir());
- $debObj->compile_check = \Smarty::COMPILECHECK_ON;
$debObj->security_policy = null;
$debObj->debugging = false;
$debObj->debugging_ctrl = 'NONE';
@@ -213,8 +212,7 @@ class Debug extends Data
$debObj->registered_filters = array();
$debObj->escape_html = true;
$debObj->caching = \Smarty::CACHING_OFF;
- $debObj->compile_id = null;
- $debObj->cache_id = null;
+
// prepare information of assigned variables
$ptr = $this->get_debug_vars($obj);
$_assigned_vars = $ptr->tpl_vars;
diff --git a/src/Runtime/TplFunctionRuntime.php b/src/Runtime/TplFunctionRuntime.php
index 905defb8..93e7c6b6 100644
--- a/src/Runtime/TplFunctionRuntime.php
+++ b/src/Runtime/TplFunctionRuntime.php
@@ -42,13 +42,6 @@ class TplFunctionRuntime {
$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 \Smarty\Exception("Unable to find template function '{$name}'");
}
@@ -91,61 +84,6 @@ class TplFunctionRuntime {
}
}
- /**
- * Add template function to cache file for nocache calls
- *
- * @param Template $tpl
- * @param string $_name template function name
- * @param string $_function PHP function name
- *
- * @return bool
- * @throws Exception
- */
- private function addTplFuncToCache(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)) {
-
- // Some magic code existed here, testing if the cached property had been set
- // and then bubbling up until it found a parent template that had the cached property.
- // This is no longer possible, so somehow this might break.
-
- // add template function code to cache file
- $content = $tpl->getCached()->readCache($tpl);
- 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);
- }
- $tpl->getCached()->writeCache(
- $tpl,
- 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
*
diff --git a/src/Smarty.php b/src/Smarty.php
index 48fb0d47..ed0b0089 100644
--- a/src/Smarty.php
+++ b/src/Smarty.php
@@ -191,12 +191,12 @@ class Smarty extends \Smarty\TemplateBase
*/
public $force_compile = false;
- /**
- * use sub dirs for compiled/cached files?
- *
- * @var boolean
- */
- public $use_sub_dirs = false;
+ /**
+ * Check template for modifications?
+ * Set to Smarty::COMPILECHECK_OFF when templates won't change in production.
+ * @var int
+ */
+ public $compile_check = self::COMPILECHECK_ON;
/**
* allow ambiguous resources (that are made unique by the resource handler)
@@ -238,11 +238,11 @@ class Smarty extends \Smarty\TemplateBase
*
* @var array string
*/
- public $literals = array();
+ private $literals = array();
/**
* class name
- * This should be instance of \Smarty\Security.
+ * This should be an instance of \Smarty\Security.
*
* @var string
* @see \Smarty\Security
@@ -579,7 +579,7 @@ class Smarty extends \Smarty\TemplateBase
* @param string $resource_name template name
*
* @return bool status
- * @throws \Smarty\Exception
+ * @throws Exception
*/
public function templateExists($resource_name)
{
@@ -594,7 +594,7 @@ class Smarty extends \Smarty\TemplateBase
* @param string|\Smarty\Security $security_class if a string is used, it must be class-name
*
* @return Smarty current Smarty instance for chaining
- * @throws \Smarty\Exception
+ * @throws Exception
*/
public function enableSecurity($security_class = null)
{
@@ -749,7 +749,7 @@ class Smarty extends \Smarty\TemplateBase
* @param bool $cacheable if true (default) this function is cache able
*
* @return $this
- * @throws \Smarty\Exception
+ * @throws Exception
* @link https://www.smarty.net/docs/en/api.register.plugin.tpl
*
* @api Smarty::registerPlugin()
@@ -854,7 +854,7 @@ class Smarty extends \Smarty\TemplateBase
{
trigger_error('Using Smarty::getPluginsDir() is deprecated and will be ' .
'removed in a future release. For now, it will remove the DefaultExtension from the extensions list and ' .
- 'proceed to call Smartyy::addPluginsDir..', E_USER_DEPRECATED);
+ 'proceed to call Smarty::addPluginsDir..', E_USER_DEPRECATED);
$this->extensions = array_filter(
$this->extensions,
@@ -1044,7 +1044,7 @@ class Smarty extends \Smarty\TemplateBase
$nameIsDotted = !empty($name) && $name[0] === '.' && ($name[1] === '.' || $name[1] === '/');
$id_parts[] = $type;
- $id_parts[] = $this->getSmarty()->_joined_template_dir;
+ $id_parts[] = $this->_joined_template_dir;
// handle relative template names
if ($baseFilePath && $nameIsDotted) {
@@ -1111,14 +1111,6 @@ class Smarty extends \Smarty\TemplateBase
return $realpath !== false ? $parts[ 'root' ] . $path : str_ireplace(getcwd(), '.', $parts[ 'root' ] . $path);
}
- /**
- * @param boolean $use_sub_dirs
- */
- public function setUseSubDirs($use_sub_dirs)
- {
- $this->use_sub_dirs = $use_sub_dirs;
- }
-
/**
* @param int $error_reporting
*/
@@ -1271,7 +1263,7 @@ class Smarty extends \Smarty\TemplateBase
/**
* Get Smarty object
- * // @TODO this is silly, remove?
+ * This is required for methods defined in base class Data trying to find Smarty
* @return Smarty
*/
public function getSmarty()
@@ -1345,7 +1337,7 @@ class Smarty extends \Smarty\TemplateBase
* @param string $type resource type
*
* @return int number of cache files deleted
- * @throws \Smarty\Exception
+ * @throws Exception
* @link https://www.smarty.net/docs/en/api.clear.cache.tpl
*
* @api Smarty::clearCache()
@@ -1383,10 +1375,12 @@ class Smarty extends \Smarty\TemplateBase
* @param integer $exp_time expiration time
*
* @return int number of template files deleted
- * @throws \Smarty\Exception
+ * @throws Exception
*@link https://www.smarty.net/docs/en/api.clear.compiled.template.tpl
*
* @api Smarty::clearCompiledTemplate()
+ *
+ * @TODO can we remove this? It probably won't work anymore for specific files anyway
*/
public function clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null)
{
@@ -1395,7 +1389,6 @@ class Smarty extends \Smarty\TemplateBase
return 0;
}
$_compile_id = isset($compile_id) ? preg_replace('![^\w]+!', '_', $compile_id) : null;
- $_dir_sep = $this->use_sub_dirs ? DIRECTORY_SEPARATOR : '^';
if (isset($resource_name)) {
$_save_stat = $this->caching;
$this->caching = \Smarty\Smarty::CACHING_OFF;
@@ -1412,11 +1405,11 @@ class Smarty extends \Smarty\TemplateBase
$_resource_part_2_length = strlen($_resource_part_2);
}
$_dir = $_compile_dir;
- if ($this->use_sub_dirs && isset($_compile_id)) {
- $_dir .= $_compile_id . $_dir_sep;
+ if (isset($_compile_id)) {
+ $_dir .= $_compile_id . DIRECTORY_SEPARATOR;
}
if (isset($_compile_id)) {
- $_compile_id_part = $_compile_dir . $_compile_id . $_dir_sep;
+ $_compile_id_part = $_compile_dir . $_compile_id . DIRECTORY_SEPARATOR;
$_compile_id_part_length = strlen($_compile_id_part);
}
$_count = 0;
@@ -1614,7 +1607,7 @@ class Smarty extends \Smarty\TemplateBase
* @param string $content
*
* @throws \Exception
- * @throws \Smarty\Exception
+ * @throws Exception
*/
public function cacheModifiedCheck(Template\Cached $cached, Template $_template, $content)
{
@@ -1832,8 +1825,18 @@ class Smarty extends \Smarty\TemplateBase
error_reporting($_error_reporting);
throw new Exception("unable to write file {$_filepath}");
}
+
// set file permissions
@chmod($_filepath, 0666 & ~umask());
+
+ if (function_exists('opcache_invalidate')
+ && (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1)
+ ) {
+ opcache_invalidate($_filepath, true);
+ } elseif (function_exists('apc_compile_file')) {
+ apc_compile_file($_filepath);
+ }
+
error_reporting($_error_reporting);
return true;
}
@@ -1869,7 +1872,7 @@ class Smarty extends \Smarty\TemplateBase
);
}
- throw new \Smarty\Exception('Trying to load invalid runtime ' . $type);
+ throw new Exception('Trying to load invalid runtime ' . $type);
}
/**
@@ -1883,7 +1886,7 @@ class Smarty extends \Smarty\TemplateBase
try {
$this->getRuntime($type);
return true;
- } catch (\Smarty\Exception $e) {
+ } catch (Exception $e) {
return false;
}
}
@@ -1902,7 +1905,7 @@ class Smarty extends \Smarty\TemplateBase
* @param string $name filter name
*
* @return bool
- * @throws \Smarty\Exception
+ * @throws Exception
* @api Smarty::loadFilter()
* @link https://www.smarty.net/docs/en/api.load.filter.tpl
*
@@ -1955,7 +1958,7 @@ class Smarty extends \Smarty\TemplateBase
* @param string $name filter name
*
* @return TemplateBase
- * @throws \Smarty\Exception
+ * @throws Exception
* @api Smarty::unloadFilter()
*
* @link https://www.smarty.net/docs/en/api.unload.filter.tpl
@@ -2057,7 +2060,7 @@ class Smarty extends \Smarty\TemplateBase
* @param string|null $name optional filter name
*
* @return TemplateBase
- * @throws \Smarty\Exception
+ * @throws Exception
* @link https://www.smarty.net/docs/en/api.register.filter.tpl
*
* @api Smarty::registerFilter()
@@ -2118,7 +2121,7 @@ class Smarty extends \Smarty\TemplateBase
* @param callback|string $name the name previously used in ::registerFilter
*
* @return TemplateBase
- * @throws \Smarty\Exception
+ * @throws Exception
* @api Smarty::unregisterFilter()
*
* @link https://www.smarty.net/docs/en/api.unregister.filter.tpl
@@ -2237,7 +2240,7 @@ class Smarty extends \Smarty\TemplateBase
* @param mixed $compile_id compile id to be used with this template
*
* @throws \Exception
- * @throws \Smarty\Exception
+ * @throws Exception
*/
public function display($template = null, $cache_id = null, $compile_id = null) {
return $this->returnOrCreateTemplate($template, $cache_id, $compile_id)->display();
@@ -2253,7 +2256,7 @@ class Smarty extends \Smarty\TemplateBase
*
* @return bool cache status
* @throws \Exception
- * @throws \Smarty\Exception
+ * @throws Exception
* @link https://www.smarty.net/docs/en/api.is.cached.tpl
*
* @api Smarty::isCached()
@@ -2279,5 +2282,326 @@ class Smarty extends \Smarty\TemplateBase
return $template;
}
+ /**
+ * @return int
+ */
+ public function getCompileCheck(): int {
+ return $this->compile_check;
+ }
+
+ /**
+ * @param int $compile_check
+ */
+ public function setCompileCheck($compile_check) {
+ $this->compile_check = (int)$compile_check;
+ }
+
+ /**
+ * @var Debug
+ */
+ private $debug;
+
+ /**
+ * Registers object to be used in templates
+ *
+ * @param string $object_name
+ * @param object $object the referenced PHP object to register
+ * @param array $allowed_methods_properties list of allowed methods (empty = all)
+ * @param bool $format smarty argument format, else traditional
+ * @param array $block_methods list of block-methods
+ *
+ * @return Smarty
+ * @throws Exception
+ * @link https://www.smarty.net/docs/en/api.register.object.tpl
+ *
+ * @api Smarty::registerObject()
+ */
+ public function registerObject(
+ $object_name,
+ $object,
+ $allowed_methods_properties = [],
+ $format = true,
+ $block_methods = []
+ ): Smarty {
+ // test if allowed methods callable
+ if (!empty($allowed_methods_properties)) {
+ foreach ((array)$allowed_methods_properties as $method) {
+ if (!is_callable([$object, $method]) && !property_exists($object, $method)) {
+ throw new Exception("Undefined method or property '$method' in registered object");
+ }
+ }
+ }
+ // test if block methods callable
+ if (!empty($block_methods)) {
+ foreach ((array)$block_methods as $method) {
+ if (!is_callable([$object, $method])) {
+ throw new Exception("Undefined method '$method' in registered object");
+ }
+ }
+ }
+ // register the object
+ $this->registered_objects[$object_name] =
+ [$object, (array)$allowed_methods_properties, (boolean)$format, (array)$block_methods];
+ return $this;
+ }
+
+ /**
+ * Registers plugin to be used in templates
+ *
+ * @param string $object_name name of object
+ *
+ * @return Smarty
+ * @api Smarty::unregisterObject()
+ * @link https://www.smarty.net/docs/en/api.unregister.object.tpl
+ */
+ public function unregisterObject($object_name): Smarty {
+ if (isset($this->registered_objects[$object_name])) {
+ unset($this->registered_objects[$object_name]);
+ }
+ return $this;
+ }
+
+
+ /**
+ * creates a data object
+ *
+ * @param Data|null $parent next higher level of Smarty variables
+ * @param null $name optional data block name
+ *
+ * @return Data data object
+ * @throws Exception
+ * @api Smarty::createData()
+ * @link https://www.smarty.net/docs/en/api.create.data.tpl
+ *
+ */
+ public function createData(Data $parent = null, $name = null): Data {
+ $dataObj = new Data($this, $this, $name);
+ if ($this->debugging) {
+ $this->getDebug()->register_data($dataObj);
+ }
+ return $dataObj;
+ }
+
+ /**
+ * return name of debugging template
+ *
+ * @return string|null
+ * @api Smarty::getDebugTemplate()
+ */
+ public function getDebugTemplate(): ?string {
+ return $this->debug_tpl;
+ }
+
+ /**
+ * @return Debug
+ */
+ public function getDebug(): Debug {
+ if (!isset($this->debug)) {
+ $this->debug = new \Smarty\Debug();
+ }
+ return $this->debug;
+ }
+
+
+ /**
+ * return a reference to a registered object
+ *
+ * @param string $object_name object name
+ *
+ * @return object
+ * @throws Exception if no such object is found
+ * @link https://www.smarty.net/docs/en/api.get.registered.object.tpl
+ *
+ * @api Smarty::getRegisteredObject()
+ */
+ public function getRegisteredObject($object_name): object {
+ if (!isset($this->registered_objects[$object_name])) {
+ throw new Exception("'$object_name' is not a registered object");
+ }
+ if (!is_object($this->registered_objects[$object_name][0])) {
+ throw new Exception("registered '$object_name' is not an object");
+ }
+ return $this->registered_objects[$object_name][0];
+ }
+
+ /**
+ * Get literals
+ *
+ * @return array list of literals
+ * @api Smarty::getLiterals()
+ *
+ */
+ public function getLiterals(): array {
+ return $this->literals;
+ }
+
+ /**
+ * Add literals
+ *
+ * @param array|string $literals literal or list of literals
+ * to addto add
+ *
+ * @return Smarty
+ * @throws Exception
+ * @api Smarty::addLiterals()
+ *
+ */
+ public function addLiterals($literals = null): Smarty {
+ if (isset($literals)) {
+ $this->_setLiterals((array)$literals);
+ }
+ return $this;
+ }
+
+ /**
+ * Set literals
+ *
+ * @param array|string $literals literal or list of literals to set
+ *
+ * @return Smarty
+ * @throws Exception
+ * @api Smarty::setLiterals()
+ */
+ public function setLiterals($literals = null): Smarty {
+ $this->literals = [];
+ if (!empty($literals)) {
+ $this->_setLiterals((array)$literals);
+ }
+ return $this;
+ }
+
+ /**
+ * common setter for literals for easier handling of duplicates the
+ * Smarty::$literals array gets filled with identical key values
+ *
+ * @param array $literals
+ *
+ * @throws Exception
+ */
+ private function _setLiterals(array $literals) {
+ $literals = array_combine($literals, $literals);
+ $error = isset($literals[$this->getLeftDelimiter()]) ? [$this->getLeftDelimiter()] : [];
+ $error = isset($literals[$this->getRightDelimiter()]) ? $error[] = $this->getRightDelimiter() : $error;
+ if (!empty($error)) {
+ throw new Exception(
+ 'User defined literal(s) "' . $error .
+ '" may not be identical with left or right delimiter'
+ );
+ }
+ $this->literals = array_merge($this->literals, (array)$literals);
+ }
+
+ /**
+ * Registers static classes to be used in templates
+ *
+ * @param string $class_name
+ * @param string $class_impl the referenced PHP class to
+ * register
+ *
+ * @return Smarty
+ * @throws Exception
+ * @api Smarty::registerClass()
+ * @link https://www.smarty.net/docs/en/api.register.class.tpl
+ *
+ */
+ public function registerClass($class_name, $class_impl): Smarty {
+ // test if exists
+ if (!class_exists($class_impl)) {
+ throw new Exception("Undefined class '$class_impl' in register template class");
+ }
+ // register the class
+ $this->registered_classes[$class_name] = $class_impl;
+ return $this;
+ }
+
+ /**
+ * Register config default handler
+ *
+ * @param callable $callback class/method name
+ *
+ * @return Smarty
+ * @throws Exception if $callback is not callable
+ * @api Smarty::registerDefaultConfigHandler()
+ *
+ */
+ public function registerDefaultConfigHandler($callback): Smarty {
+ if (is_callable($callback)) {
+ $this->default_config_handler_func = $callback;
+ } else {
+ throw new Exception('Default config handler not callable');
+ }
+ return $this;
+ }
+
+ /**
+ * Register template default handler
+ *
+ * @param callable $callback class/method name
+ *
+ * @return Smarty
+ * @throws Exception if $callback is not callable
+ * @api Smarty::registerDefaultTemplateHandler()
+ *
+ */
+ public function registerDefaultTemplateHandler($callback): Smarty {
+ if (is_callable($callback)) {
+ $this->default_template_handler_func = $callback;
+ } else {
+ throw new Exception('Default template handler not callable');
+ }
+ return $this;
+ }
+
+ /**
+ * Registers a resource to fetch a template
+ *
+ * @param string $name name of resource type
+ * @param BasePlugin $resource_handler instance of Smarty\Resource\Base
+ *
+ * @return Smarty
+ * @link https://www.smarty.net/docs/en/api.register.resource.tpl
+ *
+ * @api Smarty::registerResource()
+ */
+ public function registerResource($name, \Smarty\Resource\BasePlugin $resource_handler): Smarty {
+ $this->registered_resources[$name] = $resource_handler;
+ return $this;
+ }
+
+ /**
+ * Unregisters a resource to fetch a template
+ *
+ * @param string $type name of resource type
+ *
+ * @return Smarty
+ * @api Smarty::unregisterResource()
+ * @link https://www.smarty.net/docs/en/api.unregister.resource.tpl
+ *
+ */
+ public function unregisterResource($type): Smarty {
+ if (isset($this->registered_resources[$type])) {
+ unset($this->registered_resources[$type]);
+ }
+ return $this;
+ }
+
+ /**
+ * set the debug template
+ *
+ * @param string $tpl_name
+ *
+ * @return Smarty
+ * @throws Exception if file is not readable
+ * @api Smarty::setDebugTemplate()
+ *
+ */
+ public function setDebugTemplate($tpl_name): Smarty {
+ if (!is_readable($tpl_name)) {
+ throw new Exception("Unknown file '{$tpl_name}'");
+ }
+ $this->debug_tpl = $tpl_name;
+ return $this;
+ }
+
}
diff --git a/src/Template.php b/src/Template.php
index 43006db5..31717112 100644
--- a/src/Template.php
+++ b/src/Template.php
@@ -126,7 +126,6 @@ class Template extends TemplateBase {
$this->compile_id = $_compile_id === null ? $this->smarty->compile_id : $_compile_id;
$this->caching = (int)($_caching === null ? $this->smarty->caching : $_caching);
$this->cache_lifetime = $this->smarty->cache_lifetime;
- $this->compile_check = (int)$smarty->compile_check;
$this->parent = $_parent;
// Template resource
$this->template_resource = $template_resource;
@@ -138,20 +137,18 @@ class Template extends TemplateBase {
}
/**
- * render template
- *
- * @param bool $no_output_filter if true do not run output filter
- * @param null|bool $display true: display, false: fetch null: sub-template
+ * render template and return result
*
* @return string
* @throws \Exception
* @throws \Smarty\Exception
*/
- private function render($no_output_filter = true, $display = null) {
+ private function getResult(): string {
+
if ($this->smarty->debugging) {
- $this->smarty->getDebug()->start_template($this, $display);
+ $this->smarty->getDebug()->start_template($this, 0);
}
- // checks if template exists
+
if (!$this->getSource()->exists) {
throw new Exception(
"Unable to load template '{$this->getSource()->type}:{$this->getSource()->name}'" .
@@ -168,68 +165,28 @@ class Template extends TemplateBase {
call_user_func($callback, $this);
}
- try {
-
- // read from cache or render
- if ($this->caching === \Smarty\Smarty::CACHING_LIFETIME_CURRENT || $this->caching === \Smarty\Smarty::CACHING_LIFETIME_SAVED) {
- if ($this->getCached()->cache_id !== $this->cache_id || $this->getCached()->compile_id !== $this->compile_id) {
- $this->getCached(true);
- }
- $this->getCached()->render($this, $no_output_filter);
- } else {
- $compiled = $this->getCompiled();
- if ($compiled->compile_id !== $this->compile_id) {
- $compiled = $this->getCompiled(true);
- }
- $compiled->render($this);
+ // read from cache or render
+ if ($this->isCachingEnabled()) {
+ // @TODO why would cache_id or compile_id differ. Is this because of re-using of Cached objects over subtemplates?
+ // If so, it seems error-prone to just throw that away here.
+ if ($this->getCached()->cache_id !== $this->cache_id || $this->getCached()->compile_id !== $this->compile_id) {
+ $this->getCached(true);
}
-
- } finally {
- foreach ($this->endRenderCallbacks as $callback) {
- call_user_func($callback, $this);
- }
- }
-
- // display or fetch
- if ($display) {
- if ($this->caching && $this->smarty->cache_modified_check) {
- $this->smarty->cacheModifiedCheck(
- $this->getCached(),
- $this,
- isset($content) ? $content : ob_get_clean()
- );
- } else {
- if ((!$this->caching || $this->getCached()->getNocacheCode() || $this->getSource()->handler->recompiled)
- && !$no_output_filter && isset($this->smarty->registered_filters['output'])
- ) {
- echo $this->smarty->runOutputFilters(ob_get_clean(), $this);
- } else {
- echo ob_get_clean();
- }
- }
- if ($this->smarty->debugging) {
- $this->smarty->getDebug()->end_template($this);
- // debug output
- $this->smarty->getDebug()->display_debug($this, true);
- }
- return '';
+ $result = $this->getCached()->fetch($this);
} else {
- if ($this->smarty->debugging) {
- $this->smarty->getDebug()->end_template($this);
- if ($this->smarty->debugging === 2 && $display === false) {
- $this->smarty->getDebug()->display_debug($this, true);
- }
- }
- if (
- !$no_output_filter
- && (!$this->caching || $this->getCached()->getNocacheCode() || $this->getSource()->handler->recompiled)
- ) {
-
- return $this->smarty->runOutputFilters(ob_get_clean(), $this);
- }
- // return cache content
- return null;
+ $compiled = $this->getCompiled();
+ $result = $compiled->fetch();
}
+
+ foreach ($this->endRenderCallbacks as $callback) {
+ call_user_func($callback, $this);
+ }
+
+ if ($this->smarty->debugging) {
+ $this->smarty->getDebug()->end_template($this);
+ }
+
+ return $result;
}
/**
@@ -286,7 +243,7 @@ class Template extends TemplateBase {
}
}
- $tpl->render();
+ $tpl->getResult();
}
/**
@@ -311,36 +268,7 @@ class Template extends TemplateBase {
* @throws \Exception
*/
public function compileTemplateSource() {
- return $this->getCompiled()->compileAndWrite($this);
- }
-
- /**
- * Return cached content
- *
- * @return null|string
- * @throws Exception
- */
- public function getCachedContent() {
- return $this->getCached()->getContent($this);
- }
-
- /**
- * Writes the content to cache resource
- *
- * @param string $content
- *
- * @return bool
- *
- * @TODO this method is only used in unit tests that (mostly) try to test CacheResources.
- */
- public function writeCachedContent($content) {
- if ($this->getSource()->handler->recompiled || !$this->caching
- ) {
- // don't write cache file
- return false;
- }
- $codeframe = $this->createCodeFrame($content, '', true);
- return $this->getCached()->writeCache($this, $codeframe);
+ return $this->getCompiled()->compileAndWrite();
}
/**
@@ -369,7 +297,7 @@ class Template extends TemplateBase {
*/
public function getCompiled($forceNew = false) {
if ($forceNew || !isset($this->compiled)) {
- $this->compiled = Compiled::load($this);
+ $this->compiled = new Compiled($this);
}
return $this->compiled;
}
@@ -378,8 +306,7 @@ class Template extends TemplateBase {
* Return Cached object
*
* @param bool $forceNew force new cached object
- *
- * @throws Exception
+ * @return Cached
*/
public function getCached($forceNew = false): Cached {
if ($forceNew || !isset($this->cached)) {
@@ -391,9 +318,6 @@ class Template extends TemplateBase {
$this->cache_id
);
$cacheResource->populate($this->cached, $this);
- if (!$this->isCachingEnabled()) {
- $this->cached->setValid(false);
- }
}
return $this->cached;
}
@@ -442,13 +366,12 @@ class Template extends TemplateBase {
* @param string $content optional template content
* @param string $functions compiled template function and block code
* @param bool $cache flag for cache file
- * @param Compiler\Template|null $compiler
*
* @return string
* @throws Exception
*/
- public function createCodeFrame($content = '', $functions = '', $cache = false, \Smarty\Compiler\Template $compiler = null) {
- return $this->getCodeFrameCompiler()->create($content, $functions, $cache, $compiler);
+ public function createCodeFrame($content = '', $functions = '', $cache = false) {
+ return $this->getCodeFrameCompiler()->create($content, $functions, $cache);
}
/**
@@ -482,7 +405,7 @@ class Template extends TemplateBase {
return $this->smarty->force_compile
|| $this->getSource()->handler->recompiled
|| !$this->getCompiled()->exists
- || ($this->compile_check && $this->getCompiled()->getTimeStamp() < $this->getSource()->getTimeStamp());
+ || ($this->getSmarty()->getCompileCheck() && $this->getCompiled()->getTimeStamp() < $this->getSource()->getTimeStamp());
}
private function getCodeFrameCompiler(): Compiler\CodeFrame {
@@ -567,50 +490,12 @@ class Template extends TemplateBase {
return $confObj;
}
- public function fetch() {
- $result = $this->_execute(0);
- return $result === null ? ob_get_clean() : $result;
- }
-
- public function display() {
- $this->_execute(1);
- }
-
- /**
- * test if cache is valid
- *
- * @param mixed $cache_id cache id to be used with this template
- * @param mixed $compile_id compile id to be used with this template
- * @param object $parent next higher level of Smarty variables
- *
- * @return bool cache status
- * @throws \Exception
- * @throws \Smarty\Exception
- * @link https://www.smarty.net/docs/en/api.is.cached.tpl
- *
- * @api Smarty::isCached()
- */
- public function isCached(): bool {
- return (bool) $this->_execute(2);
- }
-
- /**
- * fetches a rendered Smarty template
- *
- * @param string $function function type 0 = fetch, 1 = display, 2 = isCache
- *
- * @return mixed
- * @throws Exception
- * @throws \Throwable
- */
- private function _execute($function) {
-
+ public function fetch(): ?string {
$smarty = $this->getSmarty();
// make sure we have integer values
$this->caching = (int)$this->caching;
- // fetch template content
- $level = ob_get_level();
+
try {
$_smarty_old_error_level =
isset($smarty->error_reporting) ? error_reporting($smarty->error_reporting) : null;
@@ -620,28 +505,15 @@ class Template extends TemplateBase {
$errorHandler->activate();
}
- if ($function === 2) {
- if ($this->caching) {
- // return cache status of template
- $result = $this->getCached()->isCached($this);
- } else {
- return false;
- }
- } else {
+ // After rendering a template, the tpl/config variables are reset, so the template can be re-used.
+ $savedTplVars = $this->tpl_vars;
+ $savedConfigVars = $this->config_vars;
- // After rendering a template, the tpl/config variables are reset, so the template can be re-used.
- $savedTplVars = $this->tpl_vars;
- $savedConfigVars = $this->config_vars;
+ $result = $this->getResult();
- // Start output-buffering.
- ob_start();
-
- $result = $this->render(false, $function);
-
- // Restore the template to its previous state
- $this->tpl_vars = $savedTplVars;
- $this->config_vars = $savedConfigVars;
- }
+ // Restore the template to its previous state
+ $this->tpl_vars = $savedTplVars;
+ $this->config_vars = $savedConfigVars;
if (isset($errorHandler)) {
$errorHandler->deactivate();
@@ -650,11 +522,9 @@ class Template extends TemplateBase {
if (isset($_smarty_old_error_level)) {
error_reporting($_smarty_old_error_level);
}
- return $result;
+
} catch (\Throwable $e) {
- while (ob_get_level() > $level) {
- ob_end_clean();
- }
+
if (isset($errorHandler)) {
$errorHandler->deactivate();
}
@@ -664,6 +534,25 @@ class Template extends TemplateBase {
}
throw $e;
}
+
+ return $result;
+ }
+
+ public function display() {
+ echo $this->fetch();
+ }
+
+ /**
+ * test if caching is enabled, a cache exists and it is still fresh
+ *
+ * @return bool cache status
+ * @throws Exception
+ * @link https://www.smarty.net/docs/en/api.is.cached.tpl
+ *
+ * @api Smarty::isCached()
+ */
+ public function isCached(): bool {
+ return $this->isCachingEnabled() && $this->getCached()->isCached($this);
}
/**
diff --git a/src/Template/Cached.php b/src/Template/Cached.php
index 888858a2..8f0ffe8d 100644
--- a/src/Template/Cached.php
+++ b/src/Template/Cached.php
@@ -11,7 +11,14 @@ use Smarty\Template\Compiler\CodeFrame;
* Represents a cached version of a template or config file.
* @author Rodney Rehm
*/
-class Cached extends GeneratedPhpFile {
+class Cached {
+
+ /**
+ * Filepath of cached file
+ *
+ * @var string
+ */
+ public $filepath = null;
/**
* Cache Is Valid
@@ -21,11 +28,25 @@ class Cached extends GeneratedPhpFile {
private $valid = null;
/**
- * @param bool|null $valid
+ * Indicates existence of cache in cacheresource
+ *
+ * @var boolean
*/
- public function setValid(?bool $valid): void {
- $this->valid = $valid;
- }
+ public $exists = false;
+
+ /**
+ * Template Compile Id (\Smarty\Template::$compile_id)
+ *
+ * @var string
+ */
+ public $compile_id = null;
+
+ /**
+ * Compiled Timestamp
+ *
+ * @var int|bool
+ */
+ public $timestamp = false;
/**
* CacheResource Handler
@@ -67,7 +88,7 @@ class Cached extends GeneratedPhpFile {
*
* @var Source
*/
- public $source = null;
+ private $source = null;
/**
* Nocache hash codes of processed compiled templates
@@ -83,6 +104,13 @@ class Cached extends GeneratedPhpFile {
*/
public $content = null;
+ /**
+ * resource file dependency
+ *
+ * @var array
+ */
+ public $file_dependency = [];
+
/**
* create Cached Object container
*
@@ -96,36 +124,69 @@ class Cached extends GeneratedPhpFile {
$this->cache_id = $cache_id;
$this->source = $source;
$this->handler = $handler;
+ //@TODO we need $template here too.
}
/**
- * Render cache template
+ * Return cached template contents
*
- * @param \Smarty\Template $_template
- * @param bool $no_output_filter
+ * @param Template $_template
*
- * @throws \Exception
+ * @return string
+ * @throws Exception
*/
- public function render(Template $_template, $no_output_filter = true) {
+ public function fetch(Template $_template): string {
if (!$this->isCached($_template)) {
- $this->updateCache($_template, $no_output_filter);
- } else {
- if (!$this->processed) {
- $this->process($_template);
- }
+ $this->saveCache($_template);
}
if ($_template->getSmarty()->debugging) {
$_template->getSmarty()->getDebug()->start_cache($_template);
}
- $this->getRenderedTemplateCode($_template, $this->unifunc);
+ $codeFrameClassname = $this->getCodeFrameClassname();
+ if ($this->getCodeFrameClassname() && !class_exists($codeFrameClassname)) {
+ $this->runCodeFrame($_template, $this->readCache($_template));
+ }
+
+ /** @var \Smarty\CodeFrame\Cached $codeFrameClassname */
+ $cachedTemplate = new $codeFrameClassname();
+ ob_start();
+
+ $cachedTemplate->renderContent($_template);
+
+ $result = ob_get_clean();
if ($_template->getSmarty()->debugging) {
$_template->getSmarty()->getDebug()->end_cache($_template);
}
+ return $result;
+ }
+
+ /**
+ * Render cache template
+ *
+ * @param \Smarty\Template $_template
+ *
+ * @throws \Exception
+ */
+ public function render(Template $_template): void {
+ echo $this->fetch($_template);
+ }
+
+ /**
+ * Returns the codeframe for this cache, but only if it is loaded already. Will not load any data from
+ * the cacheresource.
+ *
+ * @return \Smarty\CodeFrame\Cached|null
+ */
+ private function getCodeFrame(): ?\Smarty\CodeFrame\Cached {
+ if (!$this->getCodeFrameClassname() || !class_exists($classname = $this->getCodeFrameClassname())) {
+ return null;
+ }
+ return new $classname;
}
/**
@@ -137,86 +198,7 @@ class Cached extends GeneratedPhpFile {
* @throws Exception
*/
public function isCached(Template $_template) {
- if ($this->valid !== null) {
- return $this->valid;
- }
- while (true) {
- while (true) {
- if ($this->exists === false || $_template->getSmarty()->force_compile || $_template->getSmarty()->force_cache) {
- $this->valid = false;
- } else {
- $this->valid = true;
- }
- if ($this->valid && $_template->caching === \Smarty\Smarty::CACHING_LIFETIME_CURRENT
- && $_template->cache_lifetime >= 0 && time() > ($this->timestamp + $_template->cache_lifetime)
- ) {
- // lifetime expired
- $this->valid = false;
- }
- if ($this->valid && $_template->compile_check === \Smarty\Smarty::COMPILECHECK_ON
- && $_template->getSource()->getTimeStamp() > $this->timestamp
- ) {
- $this->valid = false;
- }
- if ($this->valid || !$_template->getSmarty()->cache_locking) {
- break;
- }
- if (!$this->handler->locked($_template->getSmarty(), $this)) {
- $this->handler->acquireLock($_template->getSmarty(), $this);
- break 2;
- }
- $this->handler->populate($this, $_template);
- }
- if ($this->valid) {
- if (!$_template->getSmarty()->cache_locking || $this->handler->locked($_template->getSmarty(), $this) === null) {
- // load cache file for the following checks
- if ($_template->getSmarty()->debugging) {
- $_template->getSmarty()->getDebug()->start_cache($_template);
- }
- if ($this->handler->process($_template, $this) === false) {
- $this->valid = false;
- } else {
- $this->processed = true;
- }
- if ($_template->getSmarty()->debugging) {
- $_template->getSmarty()->getDebug()->end_cache($_template);
- }
- } else {
- $this->is_locked = true;
- continue;
- }
- } else {
- return $this->valid;
- }
- if ($this->valid && $_template->caching === \Smarty\Smarty::CACHING_LIFETIME_SAVED
- && $_template->getCached()->cache_lifetime >= 0
- && (time() > ($_template->getCached()->timestamp + $_template->getCached()->cache_lifetime))
- ) {
- $this->valid = false;
- }
- if ($_template->getSmarty()->cache_locking) {
- if (!$this->valid) {
- $this->handler->acquireLock($_template->getSmarty(), $this);
- } elseif ($this->is_locked) {
- $this->handler->releaseLock($_template->getSmarty(), $this);
- }
- }
- return $this->valid;
- }
- return $this->valid;
- }
-
- /**
- * Process cached template
- *
- * @param Template $_template template object
- * @param bool $update flag if called because cache update
- */
- private function process(Template $_template, $update = false) {
- if ($this->handler->process($_template, $this, $update) === false) {
- $this->valid = false;
- }
- $this->processed = $this->valid;
+ return $this->getCodeFrame() && $this->getCodeFrame()->isFresh($_template);
}
/**
@@ -224,13 +206,13 @@ class Cached extends GeneratedPhpFile {
*
* @param Template $_template template object
*
- * @return string|false content
- */
- public function readCache(Template $_template) {
+ * @return string content
+s */
+ private function readCache(Template $_template) {
if (!$_template->getSource()->handler->recompiled) {
return $this->handler->retrieveCachedContent($_template);
}
- return false;
+ return '';
}
/**
@@ -240,7 +222,7 @@ class Cached extends GeneratedPhpFile {
*
* @return bool success
*/
- public function writeCache(Template $_template, $content) {
+ private function writeCache(Template $_template, $content) {
if (!$_template->getSource()->handler->recompiled) {
if ($this->handler->storeCachedContent($_template, $content)) {
$this->content = null;
@@ -248,7 +230,6 @@ class Cached extends GeneratedPhpFile {
$this->exists = true;
$this->valid = true;
$this->cache_lifetime = $_template->cache_lifetime;
- $this->processed = false;
if ($_template->getSmarty()->cache_locking) {
$this->handler->releaseLock($_template->getSmarty(), $this);
}
@@ -258,7 +239,6 @@ class Cached extends GeneratedPhpFile {
$this->timestamp = false;
$this->exists = false;
$this->valid = false;
- $this->processed = false;
}
return false;
}
@@ -267,29 +247,29 @@ class Cached extends GeneratedPhpFile {
* Cache was invalid , so render from compiled and write to cache
*
* @param Template $_template
- * @param bool $no_output_filter
*
* @throws \Smarty\Exception
*/
- private function updateCache(Template $_template, $no_output_filter) {
+ private function saveCache(Template $_template) {
- ob_start();
-
- $_template->getCompiled()->render($_template);
+ $content = $_template->getCompiled()->fetch();
if ($_template->getSmarty()->debugging) {
$_template->getSmarty()->getDebug()->start_cache($_template);
}
- $this->removeNoCacheHash($_template, $no_output_filter);
+ $content = $this->removeNoCacheHash($content, $_template);
+
+ $codeframe = (new \Smarty\Compiler\CodeFrame($_template))->create(
+ $content,
+ '',
+ true
+ );
+ $this->writeCache($_template, $codeframe);
if ($_template->_isSubTpl()) {
- // @TODO why is this needed?
- $_template->getCompiled()->unifunc = $_template->parent->getCompiled()->unifunc;
- }
-
- if (!$this->processed) {
- $this->process($_template, true);
+ // @TODO why was this needed? unifunc is no longer used, so this won't work anymore.
+// $_template->getCompiled()->unifunc = $_template->parent->getCompiled()->unifunc;
}
if ($_template->getSmarty()->debugging) {
@@ -297,22 +277,41 @@ class Cached extends GeneratedPhpFile {
}
}
+ private function getCompiledUid(): string {
+ return hash(
+ \PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128',
+ join('_', [
+ $this->getSource()->uid,
+// $this->template->compile_id, //@TODO
+ $this->getSource()->getSmarty()->config_overwrite ? '1' : '0',
+ $this->getSource()->getSmarty()->config_booleanize ? '1' : '0',
+ $this->getSource()->getSmarty()->config_read_hidden ? '1' : '0',
+ $this->getSource()->getSmarty()->caching ? '1' : '0',
+ ])
+ );
+ }
+
+ private function getCodeFrameClassname() {
+ return '__Cache_' . $this->getCompiledUid(); //@TODO use value from properties
+ }
+
/**
* Sanitize content and write it to cache resource
*
+ * @param string $content
* @param Template $_template
- * @param bool $no_output_filter
*
- * @throws \Smarty\Exception
+ * @return string
*/
- private function removeNoCacheHash(Template $_template, $no_output_filter) {
+ private function removeNoCacheHash(string $content, Template $_template): string {
+
$php_pattern = '/(<%|%>|<\?php|<\?|\?>|