Re-organized rendering (read source / compile / cache) process to avoid circular dependencies.

Deactivated merge_compiled_includes and the {include inline} attribute. They don't seem to do much in terms of performance, but we'll
have to check back.
This commit is contained in:
Simon Wisselink
2023-01-25 16:59:10 +01:00
parent 46dfed3837
commit 169cd924df
18 changed files with 213 additions and 374 deletions

View File

@@ -7,18 +7,8 @@ use Smarty\Smarty;
use Smarty\Template; use Smarty\Template;
use Smarty\Template\Cached; use Smarty\Template\Cached;
/**
* Smarty Internal Plugin
*
*/
/** /**
* Cache Handler API * Cache Handler API
*
* @author Rodney Rehm * @author Rodney Rehm
*/ */
abstract class Base abstract class Base

View File

@@ -62,7 +62,7 @@ class File extends Base
$_filepath[ 4 ] . $_filepath[ 5 ] . DIRECTORY_SEPARATOR; $_filepath[ 4 ] . $_filepath[ 5 ] . DIRECTORY_SEPARATOR;
} }
$cached->filepath .= $_filepath; $cached->filepath .= $_filepath;
$basename = $source->handler->getBasename($source); $basename = $source->getBasename();
if (!empty($basename)) { if (!empty($basename)) {
$cached->filepath .= '.' . $basename; $cached->filepath .= '.' . $basename;
} }
@@ -105,7 +105,7 @@ class File extends Base
Cached $cached = null, Cached $cached = null,
$update = false $update = false
) { ) {
$_smarty_tpl->getCached()->valid = false; $_smarty_tpl->getCached()->setValid(false);
if ($update && defined('HHVM_VERSION')) { if ($update && defined('HHVM_VERSION')) {
eval('?>' . file_get_contents($_smarty_tpl->getCached()->filepath)); eval('?>' . file_get_contents($_smarty_tpl->getCached()->filepath));
return true; return true;

View File

@@ -100,21 +100,10 @@ class ExtendsTag extends Inheritance {
* @throws \Smarty\Exception * @throws \Smarty\Exception
*/ */
private function compileEndChild(\Smarty\Compiler\Template $compiler, $template = null) { private function compileEndChild(\Smarty\Compiler\Template $compiler, $template = null) {
$inlineUids = '';
if (isset($template) && $compiler->getSmarty()->merge_compiled_includes) {
$code = $compiler->compileTag('include', [$template, ['scope' => 'parent']]);
// @TODO this relies on the generated code to have a certain format and is sure to break someday
if (preg_match('/(,\s*\'[a-z0-9]+\',\s*\'content.*\')/', $code, $match)) {
$inlineUids = $match[1];
}
}
$compiler->getParser()->template_postfix[] = new \Smarty\ParseTree\Tag( $compiler->getParser()->template_postfix[] = new \Smarty\ParseTree\Tag(
$compiler->getParser(), $compiler->getParser(),
'<?php $_smarty_tpl->getInheritance()->endChild($_smarty_tpl' . '<?php $_smarty_tpl->getInheritance()->endChild($_smarty_tpl' .
(isset($template) ? (isset($template) ? ", {$template}" : '') . ");\n?>"
", {$template}{$inlineUids}" :
'') . ");\n?>"
); );
} }

View File

@@ -100,11 +100,6 @@ class IncludeTag extends Base {
$fullResourceName = $match[1] . $fullResourceName . $match[1]; $fullResourceName = $match[1] . $fullResourceName . $match[1];
} }
} }
if (empty($match[5])) {
$variable_template = true;
}
} else {
$variable_template = true;
} }
// scope setup // scope setup
$_scope = isset($_attr['scope']) ? $this->convertScope($_attr['scope']) : 0; $_scope = isset($_attr['scope']) ? $this->convertScope($_attr['scope']) : 0;
@@ -118,16 +113,6 @@ class IncludeTag extends Base {
$_caching = \Smarty\Template::CACHING_NOCACHE_CODE; $_caching = \Smarty\Template::CACHING_NOCACHE_CODE;
} }
// flag if included template code should be merged into caller
$merge_compiled_includes = ($compiler->getSmarty()->merge_compiled_includes || $_attr['inline'] === true) &&
!$compiler->getTemplate()->getSource()->handler->recompiled;
if ($merge_compiled_includes) {
// variable template name ?
if ($variable_template) {
$merge_compiled_includes = false;
}
}
/* /*
* if the {include} tag provides individual parameter for caching or compile_id * if the {include} tag provides individual parameter for caching or compile_id
* the subtemplate must not be included into the common cache file and is treated like * the subtemplate must not be included into the common cache file and is treated like
@@ -157,10 +142,6 @@ class IncludeTag extends Base {
$_cache_id = '$_smarty_tpl->cache_id'; $_cache_id = '$_smarty_tpl->cache_id';
} }
// if subtemplate will be called in nocache mode do not merge
if ($compiler->getTemplate()->caching && $call_nocache) {
$merge_compiled_includes = false;
}
// assign attribute // assign attribute
if (isset($_attr['assign'])) { if (isset($_attr['assign'])) {
// output will be stored in a smarty variable instead of being displayed // output will be stored in a smarty variable instead of being displayed
@@ -175,30 +156,7 @@ class IncludeTag extends Base {
} }
} }
$has_compiled_template = false; $has_compiled_template = false;
if ($merge_compiled_includes) {
$c_id = $compiler->getTemplate()->compile_id;
// we must observe different compile_id and caching
$t_hash = sha1($c_id . ($_caching ? '--caching' : '--nocaching'));
$compiler->getSmarty()->setAllowAmbiguousResources(true);
$tpl = $compiler->getSmarty()->createTemplate(
trim($fullResourceName, '"\''),
$compiler->getTemplate()->cache_id,
$c_id,
$compiler->getTemplate(),
$_caching
);
$uid = $tpl->getSource()->type . $tpl->getSource()->uid;
if (!isset($compiler->getParentCompiler()->mergedSubTemplatesData[$uid][$t_hash])) {
$has_compiled_template = $this->compileInlineTemplate($compiler, $tpl, $t_hash);
} else {
$has_compiled_template = true;
}
$compiler->getSmarty()->setAllowAmbiguousResources(false);
}
// delete {include} standard attributes // delete {include} standard attributes
unset($_attr['file'], $_attr['assign'], $_attr['cache_id'], $_attr['cache_lifetime'], $_attr['nocache'], $_attr['caching'], $_attr['scope'], $_attr['inline']); unset($_attr['file'], $_attr['assign'], $_attr['cache_id'], $_attr['cache_lifetime'], $_attr['nocache'], $_attr['caching'], $_attr['scope'], $_attr['inline']);
// remaining attributes must be assigned as smarty variable // remaining attributes must be assigned as smarty variable
@@ -211,27 +169,6 @@ class IncludeTag extends Base {
} }
$_vars = 'array(' . join(',', $_pairs) . ')'; $_vars = 'array(' . join(',', $_pairs) . ')';
} }
if ($has_compiled_template && !$call_nocache) {
$_output = "<?php\n";
if (!empty($_attr) && $_caching === \Smarty\Template::CACHING_NOCACHE_CODE && $compiler->getTemplate()->caching) {
$_vars_nc = "foreach ($_vars as \$ik => \$iv) {\n";
$_vars_nc .= "\$_smarty_tpl->assign(\$ik, \$iv);\n";
$_vars_nc .= "}\n";
$_output .= substr($compiler->processNocacheCode('<?php ' . $_vars_nc . "?>\n"), 6, -3);
}
if (isset($_assign)) {
$_output .= "ob_start();\n";
}
$_output .= "\$_smarty_tpl->renderSubTemplate({$fullResourceName}, {$_cache_id}, \$_smarty_tpl->compile_id, " .
"{$_caching}, {$_cache_lifetime}, {$_vars}, " .
"'{$compiler->getParentCompiler()->mergedSubTemplatesData[$uid][$t_hash]['uid']}', " .
"'{$compiler->getParentCompiler()->mergedSubTemplatesData[$uid][$t_hash]['func']}', (int) {$_scope});\n";
if (isset($_assign)) {
$_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean(), false, {$_scope});\n";
}
$_output .= "?>";
return $_output;
}
if ($call_nocache) { if ($call_nocache) {
$compiler->tag_nocache = true; $compiler->tag_nocache = true;
} }
@@ -241,7 +178,7 @@ class IncludeTag extends Base {
$_output .= "ob_start();\n"; $_output .= "ob_start();\n";
} }
$_output .= "\$_smarty_tpl->renderSubTemplate({$fullResourceName}, $_cache_id, \$_smarty_tpl->compile_id, " . $_output .= "\$_smarty_tpl->renderSubTemplate({$fullResourceName}, $_cache_id, \$_smarty_tpl->compile_id, " .
"$_caching, $_cache_lifetime, $_vars, null, null, (int) {$_scope});\n"; "$_caching, $_cache_lifetime, $_vars, (int) {$_scope});\n";
if (isset($_assign)) { if (isset($_assign)) {
$_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean(), false, {$_scope});\n"; $_output .= "\$_smarty_tpl->assign({$_assign}, ob_get_clean(), false, {$_scope});\n";
} }
@@ -280,7 +217,7 @@ class IncludeTag extends Base {
if ($tplSource->type === 'file') { if ($tplSource->type === 'file') {
$sourceInfo = $tplSource->filepath; $sourceInfo = $tplSource->filepath;
} else { } else {
$basename = $tplSource->handler->getBasename($tplSource); $basename = $tplSource->getBasename();
$sourceInfo = $tplSource->type . ':' . $sourceInfo = $tplSource->type . ':' .
($basename ? $basename : $tplSource->name); ($basename ? $basename : $tplSource->name);
} }

View File

@@ -400,7 +400,7 @@ class Template extends BaseCompiler {
$this->has_variable_string = false; $this->has_variable_string = false;
$this->prefix_code = []; $this->prefix_code = [];
// add file dependency // add file dependency
if ($this->smarty->merge_compiled_includes || $this->template->getSource()->handler->checkTimestamps()) { if ($this->template->getSource()->handler->checkTimestamps()) {
$this->parent_compiler->getTemplate()->getCompiled()->file_dependency[$this->template->getSource()->uid] = $this->parent_compiler->getTemplate()->getCompiled()->file_dependency[$this->template->getSource()->uid] =
[ [
$this->template->getSource()->filepath, $this->template->getSource()->filepath,

View File

@@ -2,12 +2,14 @@
namespace Smarty\Resource; namespace Smarty\Resource;
use Smarty\Exception;
use Smarty\Smarty; use Smarty\Smarty;
use Smarty\Template;
use Smarty\Template\Source;
/** /**
* Smarty Resource Plugin * Smarty Resource Plugin
* Base implementation for resource plugins * Base implementation for resource plugins
* @method process(\Smarty\Template $_smarty_tpl)
* @author Rodney Rehm * @author Rodney Rehm
*/ */
abstract class BasePlugin abstract class BasePlugin
@@ -41,15 +43,25 @@ abstract class BasePlugin
return true; return true;
} }
/** /**
* Load Resource Handler * Check if resource must check time stamps when loading compiled or cached templates.
* * Resources like 'extends' which use source components my disable timestamp checks on own resource.
* @param \Smarty $smarty smarty object * @return bool
* @param string $type name of the resource */
* public function checkTimestamps()
* @return BasePlugin Resource Handler {
*@throws \Smarty\Exception return true;
*/ }
/**
* Load Resource Handler
*
* @param Smarty $smarty smarty object
* @param string $type name of the resource
*
* @return BasePlugin Resource Handler
* @throws Exception
*/
public static function load(Smarty $smarty, $type) public static function load(Smarty $smarty, $type)
{ {
// try smarty's cache // try smarty's cache
@@ -108,77 +120,50 @@ abstract class BasePlugin
return array($name, $type); return array($name, $type);
} }
/**
* initialize Source Object for given resource
* wrapper for backward compatibility to versions < 3.1.22
* Either [$_template] or [$smarty, $template_resource] must be specified
*
* @param \Smarty\Template $_template template object
* @param \Smarty $smarty smarty object
* @param string $template_resource resource identifier
*
* @return \Smarty\Template\Source Source Object
* @throws \Smarty\Exception
*/
public static function source(
\Smarty\Template $_template = null,
\Smarty\Smarty $smarty = null,
$template_resource = null
) {
return \Smarty\Template\Source::load($_template, $smarty, $template_resource);
}
/** /**
* Load template's source into current template object * Load template's source into current template object
* *
* @param \Smarty\Template\Source $source source object * @param Source $source source object
* *
* @return string template source * @return string template source
* @throws \Smarty\Exception if source cannot be loaded * @throws \Smarty\Exception if source cannot be loaded
*/ */
abstract public function getContent(\Smarty\Template\Source $source); abstract public function getContent(Source $source);
/** /**
* populate Source Object with meta data from Resource * populate Source Object with metadata from Resource
* *
* @param \Smarty\Template\Source $source source object * @param Source $source source object
* @param \Smarty\Template $_template template object * @param Template|null $_template template object
*/ */
abstract public function populate(\Smarty\Template\Source $source, \Smarty\Template $_template = null); abstract public function populate(Source $source, \Smarty\Template $_template = null);
/** /**
* populate Source Object with timestamp and exists from Resource * populate Source Object with timestamp and exists from Resource
* *
* @param \Smarty\Template\Source $source source object * @param Source $source source object
*/ */
public function populateTimestamp(\Smarty\Template\Source $source) public function populateTimestamp(Source $source)
{ {
// intentionally left blank // intentionally left blank
} }
/* /*
* Check if resource must check time stamps when when loading complied or cached templates. * Check if resource must check time stamps when when loading complied or cached templates.
* Resources like 'extends' which use source components my disable timestamp checks on own resource. * Resources like 'extends' which use source components my disable timestamp checks on own resource.
* *
* @return bool * @return bool
*/ */
/** /**
* Determine basename for compiled filename * Determine basename for compiled filename
* *
* @param \Smarty\Template\Source $source source object * @param \Smarty\Template\Source $source source object
* *
* @return string resource's basename * @return string resource's basename
*/ */
public function getBasename(\Smarty\Template\Source $source) public function getBasename(\Smarty\Template\Source $source)
{ {
return basename(preg_replace('![^\w]+!', '_', $source->name)); return basename(preg_replace('![^\w]+!', '_', $source->name));
} }
/**
* @return bool
*/
public function checkTimestamps()
{
return true;
}
} }

View File

@@ -8,19 +8,9 @@ use Smarty\Template\Source;
/** /**
* Smarty Internal Plugin Resource Extends * Smarty Internal Plugin Resource Extends
* * Implements the file system as resource for Smarty which {extend}s a chain of template files templates
* @author Uwe Tews * @author Uwe Tews
* @author Rodney Rehm * @author Rodney Rehm
*/
/**
* Smarty Internal Plugin Resource Extends
* Implements the file system as resource for Smarty which {extend}s a chain of template files templates
*
*/ */
class ExtendsPlugin extends BasePlugin class ExtendsPlugin extends BasePlugin
{ {

View File

@@ -9,9 +9,7 @@
namespace Smarty\Resource; namespace Smarty\Resource;
use Smarty\Smarty;
use Smarty\Template; use Smarty\Template;
use Smarty\Template\Compiled;
/** /**
* Smarty Resource Plugin * Smarty Resource Plugin
@@ -45,7 +43,7 @@ abstract class RecompiledPlugin extends BasePlugin {
* *
* @throws Exception * @throws Exception
*/ */
public function process(Template $_smarty_tpl) { public function recompile(Template $_smarty_tpl) {
$compiled = $_smarty_tpl->getCompiled(); $compiled = $_smarty_tpl->getCompiled();
$compiled->file_dependency = []; $compiled->file_dependency = [];
$compiled->includes = []; $compiled->includes = [];

View File

@@ -40,13 +40,6 @@ class CaptureRuntime {
*/ */
private $namedBuffer = []; private $namedBuffer = [];
/**
* Flag if callbacks are registered
*
* @var bool
*/
private $isRegistered = false;
/** /**
* Open capture section * Open capture section
* *
@@ -56,9 +49,9 @@ class CaptureRuntime {
* @param string $append variable name * @param string $append variable name
*/ */
public function open(Template $_template, $buffer, $assign, $append) { public function open(Template $_template, $buffer, $assign, $append) {
if (!$this->isRegistered) {
$this->register($_template); $this->registerCallbacks($_template);
}
$this->captureStack[] = [ $this->captureStack[] = [
$buffer, $buffer,
$assign, $assign,
@@ -73,7 +66,15 @@ class CaptureRuntime {
* *
* @param \Smarty\Template $_template * @param \Smarty\Template $_template
*/ */
private function register(Template $_template) { private function registerCallbacks(Template $_template) {
foreach ($_template->startRenderCallbacks as $callback) {
if (is_array($callback) && get_class($callback[0]) == self::class) {
// already registered
return;
}
}
$_template->startRenderCallbacks[] = [ $_template->startRenderCallbacks[] = [
$this, $this,
'startRender', 'startRender',
@@ -83,7 +84,6 @@ class CaptureRuntime {
'endRender', 'endRender',
]; ];
$this->startRender($_template); $this->startRender($_template);
$this->isRegistered = true;
} }
/** /**

View File

@@ -85,8 +85,6 @@ class InheritanceRuntime {
ob_start(); ob_start();
} }
++$this->inheritanceLevel; ++$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 state was waiting for parent change state to parent
if ($this->state === 2) { if ($this->state === 2) {
@@ -100,13 +98,11 @@ class InheritanceRuntime {
* *
* @param \Smarty\Template $tpl * @param \Smarty\Template $tpl
* @param null|string $template optional name of inheritance parent template * @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 \Exception
* @throws \Smarty\Exception * @throws \Smarty\Exception
*/ */
public function endChild(Template $tpl, $template = null, $uid = null, $func = null) { public function endChild(Template $tpl, $template = null) {
--$this->inheritanceLevel; --$this->inheritanceLevel;
if (!$this->inheritanceLevel) { if (!$this->inheritanceLevel) {
ob_end_clean(); ob_end_clean();
@@ -118,10 +114,7 @@ class InheritanceRuntime {
$tpl->cache_id, $tpl->cache_id,
$tpl->compile_id, $tpl->compile_id,
$tpl->caching ? \Smarty\Template::CACHING_NOCACHE_CODE : 0, $tpl->caching ? \Smarty\Template::CACHING_NOCACHE_CODE : 0,
$tpl->cache_lifetime, $tpl->cache_lifetime
[],
$uid,
$func
); );
} }
} }
@@ -151,7 +144,7 @@ class InheritanceRuntime {
while ($block->child && $block->child->child && $block->tplIndex <= $block->child->tplIndex) { while ($block->child && $block->child->child && $block->tplIndex <= $block->child->tplIndex) {
$block->child = $block->child->child; $block->child = $block->child->child;
} }
$this->process($tpl, $block); $this->processBlock($tpl, $block);
} }
/** /**
@@ -163,7 +156,7 @@ class InheritanceRuntime {
* *
* @throws Exception * @throws Exception
*/ */
private function process( private function processBlock(
Template $tpl, Template $tpl,
\Smarty\Runtime\Block $block, \Smarty\Runtime\Block $block,
\Smarty\Runtime\Block $parent = null \Smarty\Runtime\Block $parent = null
@@ -181,7 +174,7 @@ class InheritanceRuntime {
if ($block->callsChild || !isset($block->child) || ($block->child->hide && !isset($block->child->child))) { if ($block->callsChild || !isset($block->child) || ($block->child->hide && !isset($block->child->child))) {
$this->callBlock($block, $tpl); $this->callBlock($block, $tpl);
} else { } else {
$this->process($tpl, $block->child, $block); $this->processBlock($tpl, $block->child, $block);
} }
if ($block->prepend && isset($parent)) { if ($block->prepend && isset($parent)) {
$this->callParent($tpl, $block, '{block prepend}'); $this->callParent($tpl, $block, '{block prepend}');
@@ -191,7 +184,7 @@ class InheritanceRuntime {
) { ) {
$this->callBlock($block, $tpl); $this->callBlock($block, $tpl);
} else { } else {
$this->process($tpl, $block->child, $block); $this->processBlock($tpl, $block->child, $block);
} }
} }
} }
@@ -209,7 +202,7 @@ class InheritanceRuntime {
*/ */
public function callChild(Template $tpl, \Smarty\Runtime\Block $block) { public function callChild(Template $tpl, \Smarty\Runtime\Block $block) {
if (isset($block->child)) { if (isset($block->child)) {
$this->process($tpl, $block->child, $block); $this->processBlock($tpl, $block->child, $block);
} }
} }

View File

@@ -70,13 +70,6 @@ class Template extends TemplateBase {
*/ */
public $templateId = null; public $templateId = null;
/**
* Flag which is set while rending a cache file
*
* @var bool
*/
public $isRenderingCache = false;
/** /**
* Callbacks called before rendering template * Callbacks called before rendering template
* *
@@ -161,7 +154,7 @@ class Template extends TemplateBase {
* @throws \Exception * @throws \Exception
* @throws \Smarty\Exception * @throws \Smarty\Exception
*/ */
public function render($no_output_filter = true, $display = null) { private function render($no_output_filter = true, $display = null) {
if ($this->smarty->debugging) { if ($this->smarty->debugging) {
$this->smarty->getDebug()->start_template($this, $display); $this->smarty->getDebug()->start_template($this, $display);
} }
@@ -172,23 +165,38 @@ class Template extends TemplateBase {
($this->_isSubTpl() ? " in '{$this->parent->template_resource}'" : '') ($this->_isSubTpl() ? " in '{$this->parent->template_resource}'" : '')
); );
} }
// disable caching for evaluated code // disable caching for evaluated code
if ($this->getSource()->handler->recompiled) { if ($this->getSource()->handler->recompiled) {
$this->caching = \Smarty\Smarty::CACHING_OFF; $this->caching = \Smarty\Smarty::CACHING_OFF;
} }
// read from cache or render
if ($this->caching === \Smarty\Smarty::CACHING_LIFETIME_CURRENT || $this->caching === \Smarty\Smarty::CACHING_LIFETIME_SAVED) { foreach ($this->startRenderCallbacks as $callback) {
if ($this->getCached()->cache_id !== $this->cache_id || $this->getCached()->compile_id !== $this->compile_id) { call_user_func($callback, $this);
$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);
} }
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);
}
} finally {
foreach ($this->endRenderCallbacks as $callback) {
call_user_func($callback, $this);
}
}
// display or fetch // display or fetch
if ($display) { if ($display) {
if ($this->caching && $this->smarty->cache_modified_check) { if ($this->caching && $this->smarty->cache_modified_check) {
@@ -240,8 +248,6 @@ class Template extends TemplateBase {
* @param integer $caching cache mode * @param integer $caching cache mode
* @param integer $cache_lifetime lifetime of cache data * @param integer $cache_lifetime lifetime of cache data
* @param array $extra_vars passed parameter template variables * @param array $extra_vars passed parameter template variables
* @param null $uid file dependency uid
* @param null $content_func function name
* @param int|null $scope * @param int|null $scope
* *
* @throws Exception * @throws Exception
@@ -252,9 +258,7 @@ class Template extends TemplateBase {
$compile_id, $compile_id,
$caching, $caching,
$cache_lifetime, $cache_lifetime,
array $extra_vars, array $extra_vars = [],
$uid = null,
$content_func = null,
int $scope = null int $scope = null
) { ) {
@@ -270,22 +274,10 @@ class Template extends TemplateBase {
// recursive call ? // recursive call ?
if ($tpl->getTemplateId() !== $this->getTemplateId()) { if ($tpl->getTemplateId() !== $this->getTemplateId()) {
$tpl->setSource(Source::load($tpl));
if (isset($uid) && $this->getCompiled()->file_dependency) { $tpl->getCompiled(true);
// for inline templates we can get all resource information from file dependency
[$filepath, $timestamp, $type] = $this->getCompiled()->file_dependency[$uid];
$source = new Source($this->getSmarty(), $filepath, $type, $filepath);
$source->filepath = $filepath;
$source->timestamp = $timestamp;
$source->exists = true;
$source->uid = $uid;
$tpl->setSource($source);
} else {
$tpl->setSource(Source::load($tpl));
$tpl->getCompiled(true); // @TODO this unset($tpl->compiled), there might be a bug here
}
if ($caching !== \Smarty\Template::CACHING_NOCACHE_CODE) { if ($caching !== \Smarty\Template::CACHING_NOCACHE_CODE) {
$tpl->getCached(true); // @TODO this unset($tpl->cached), there might be a bug here $tpl->getCached(true);
} }
} }
@@ -300,20 +292,8 @@ class Template extends TemplateBase {
$this->getCached()->hashes[$tpl->getCompiled()->nocache_hash] = true; $this->getCached()->hashes[$tpl->getCompiled()->nocache_hash] = true;
} }
} }
if (isset($uid)) {
$smarty = $this->getSmarty(); $tpl->render();
if ($smarty->debugging) {
$smarty->getDebug()->start_template($tpl);
$smarty->getDebug()->start_render($tpl);
}
$tpl->getRenderedTemplateCode($content_func);
if ($smarty->debugging) {
$smarty->getDebug()->end_template($tpl);
$smarty->getDebug()->end_render($tpl);
}
} else {
$tpl->render();
}
} }
/** /**
@@ -326,7 +306,7 @@ class Template extends TemplateBase {
} }
public function assign($tpl_var, $value = null, $nocache = false, $scope = null) { public function assign($tpl_var, $value = null, $nocache = false, $scope = null) {
return parent::assign($tpl_var, $value, $nocache || $this->isRenderingCache, $scope); return parent::assign($tpl_var, $value, $nocache, $scope);
} }
/** /**
@@ -388,7 +368,7 @@ class Template extends TemplateBase {
$is_valid = false; $is_valid = false;
} }
$this->getCached()->cache_lifetime = $properties['cache_lifetime']; $this->getCached()->cache_lifetime = $properties['cache_lifetime'];
$this->getCached()->valid = $is_valid; $this->getCached()->setValid($is_valid);
$generatedFile = $this->getCached(); $generatedFile = $this->getCached();
} else { } else {
$this->mustCompile = !$is_valid; $this->mustCompile = !$is_valid;
@@ -429,6 +409,8 @@ class Template extends TemplateBase {
* @param string $content * @param string $content
* *
* @return bool * @return bool
*
* @TODO this method is only used in unit tests that (mostly) try to test CacheResources.
*/ */
public function writeCachedContent($content) { public function writeCachedContent($content) {
if ($this->getSource()->handler->recompiled || !$this->caching if ($this->getSource()->handler->recompiled || !$this->caching
@@ -495,7 +477,7 @@ class Template extends TemplateBase {
return $this->cached; return $this->cached;
} }
public function isCachingEnabled(): bool { private function isCachingEnabled(): bool {
return $this->caching && !$this->getSource()->handler->recompiled; return $this->caching && !$this->getSource()->handler->recompiled;
} }
@@ -523,14 +505,6 @@ class Template extends TemplateBase {
$this->inheritance = $inheritanceRuntime; $this->inheritance = $inheritanceRuntime;
} }
/**
* Unload event callbacks
*/
private function _cleanUp() {
$this->startRenderCallbacks = [];
$this->endRenderCallbacks = [];
}
/** /**
* Return Compiler object * Return Compiler object
*/ */
@@ -745,7 +719,6 @@ class Template extends TemplateBase {
$result = $this->render(false, $function); $result = $this->render(false, $function);
// Restore the template to its previous state // Restore the template to its previous state
$this->_cleanUp();
$this->tpl_vars = $savedTplVars; $this->tpl_vars = $savedTplVars;
$this->config_vars = $savedConfigVars; $this->config_vars = $savedConfigVars;
} }
@@ -773,41 +746,6 @@ class Template extends TemplateBase {
} }
} }
/**
* get rendered template content by calling compiled or cached template code
*
* @param string $unifunc function with template code
*
* @throws \Exception
*/
public function getRenderedTemplateCode($unifunc) {
$level = ob_get_level();
try {
if (empty($unifunc) || !function_exists($unifunc)) {
throw new \Smarty\Exception("Invalid compiled template for '{$this->template_resource}'");
}
if ($this->startRenderCallbacks) {
foreach ($this->startRenderCallbacks as $callback) {
call_user_func($callback, $this);
}
}
$unifunc($this);
foreach ($this->endRenderCallbacks as $callback) {
call_user_func($callback, $this);
}
$this->isRenderingCache = false;
} catch (\Exception $e) {
$this->isRenderingCache = false;
while (ob_get_level() > $level) {
ob_end_clean();
}
if (isset($this->getSmarty()->security_policy)) {
$this->getSmarty()->security_policy->endTemplate();
}
throw $e;
}
}
/** /**
* @return Config|Source|null * @return Config|Source|null
*/ */

View File

@@ -5,6 +5,7 @@ namespace Smarty\Template;
use Smarty\Exception; use Smarty\Exception;
use Smarty\Template; use Smarty\Template;
use Smarty\Template\Cacheresource\Base; use Smarty\Template\Cacheresource\Base;
use Smarty\Template\Compiler\CodeFrame;
/** /**
* Represents a cached version of a template or config file. * Represents a cached version of a template or config file.
@@ -17,14 +18,7 @@ class Cached extends GeneratedPhpFile {
* *
* @var boolean * @var boolean
*/ */
public $valid = null; private $valid = null;
/**
* @return bool|null
*/
public function getValid(): ?bool {
return $this->valid;
}
/** /**
* @param bool|null $valid * @param bool|null $valid
@@ -89,11 +83,6 @@ class Cached extends GeneratedPhpFile {
*/ */
public $content = null; public $content = null;
private function renderTemplateCode(Template $_template) {
$_template->isRenderingCache = true;
$_template->getRenderedTemplateCode($this->unifunc);
}
/** /**
* create Cached Object container * create Cached Object container
* *
@@ -118,21 +107,25 @@ class Cached extends GeneratedPhpFile {
* @throws \Exception * @throws \Exception
*/ */
public function render(Template $_template, $no_output_filter = true) { public function render(Template $_template, $no_output_filter = true) {
if ($this->isCached($_template)) {
if ($_template->getSmarty()->debugging) { if (!$this->isCached($_template)) {
$_template->getSmarty()->getDebug()->start_cache($_template); $this->updateCache($_template, $no_output_filter);
} } else {
if (!$this->processed) { if (!$this->processed) {
$this->process($_template); $this->process($_template);
} }
$this->renderTemplateCode($_template);
if ($_template->getSmarty()->debugging) {
$_template->getSmarty()->getDebug()->end_cache($_template);
}
return;
} else {
$this->updateCache($_template, $no_output_filter);
} }
if ($_template->getSmarty()->debugging) {
$_template->getSmarty()->getDebug()->start_cache($_template);
}
$this->getRenderedTemplateCode($_template, $this->unifunc);
if ($_template->getSmarty()->debugging) {
$_template->getSmarty()->getDebug()->end_cache($_template);
}
} }
/** /**
@@ -283,23 +276,30 @@ class Cached extends GeneratedPhpFile {
* @throws \Smarty\Exception * @throws \Smarty\Exception
*/ */
private function updateCache(Template $_template, $no_output_filter) { private function updateCache(Template $_template, $no_output_filter) {
ob_start(); ob_start();
$_template->getCompiled()->render($_template); $_template->getCompiled()->render($_template);
if ($_template->getSmarty()->debugging) { if ($_template->getSmarty()->debugging) {
$_template->getSmarty()->getDebug()->start_cache($_template); $_template->getSmarty()->getDebug()->start_cache($_template);
} }
$this->removeNoCacheHash($_template, $no_output_filter); $this->removeNoCacheHash($_template, $no_output_filter);
$compile_check = (int)$_template->compile_check; $compile_check = (int)$_template->compile_check;
$_template->compile_check = \Smarty\Smarty::COMPILECHECK_OFF; $_template->compile_check = \Smarty\Smarty::COMPILECHECK_OFF;
if ($_template->_isSubTpl()) { if ($_template->_isSubTpl()) {
$_template->getCompiled()->unifunc = $_template->parent->getCompiled()->unifunc; $_template->getCompiled()->unifunc = $_template->parent->getCompiled()->unifunc;
} }
if (!$_template->getCached()->processed) {
$_template->getCached()->process($_template, true); if (!$this->processed) {
$this->process($_template, true);
} }
$_template->compile_check = $compile_check; $_template->compile_check = $compile_check;
$this->renderTemplateCode($_template);
if ($_template->getSmarty()->debugging) { if ($_template->getSmarty()->debugging) {
$_template->getSmarty()->getDebug()->end_cache($_template); $_template->getSmarty()->getDebug()->end_cache($_template);
} }
@@ -367,8 +367,9 @@ class Cached extends GeneratedPhpFile {
) { ) {
$content = $_template->getSmarty()->runOutputFilters($content, $_template); $content = $_template->getSmarty()->runOutputFilters($content, $_template);
} }
// write cache file content
$_template->writeCachedContent($content); $codeframe = (new \Smarty\Compiler\CodeFrame($_template))->create($content, '', true);
$this->writeCache($_template, $codeframe);
} }
/** /**

View File

@@ -65,11 +65,10 @@ class Compiled extends GeneratedPhpFile {
$this->filepath .= (int)$smarty->config_read_hidden + (int)$smarty->config_booleanize * 2 + $this->filepath .= (int)$smarty->config_read_hidden + (int)$smarty->config_booleanize * 2 +
(int)$smarty->config_overwrite * 4; (int)$smarty->config_overwrite * 4;
} else { } else {
$this->filepath .= (int)$smarty->merge_compiled_includes + (int)$smarty->escape_html * 2 + $this->filepath .= (int)$smarty->escape_html * 2;
(($smarty->merge_compiled_includes && $source->type === 'extends') ? 4 : 0);
} }
$this->filepath .= '.' . $source->type; $this->filepath .= '.' . $source->type;
$basename = $source->handler->getBasename($source); $basename = $source->getBasename();
if (!empty($basename)) { if (!empty($basename)) {
$this->filepath .= '.' . $basename; $this->filepath .= '.' . $basename;
} }
@@ -101,13 +100,13 @@ class Compiled extends GeneratedPhpFile {
$_template->getSmarty()->getDebug()->start_render($_template); $_template->getSmarty()->getDebug()->start_render($_template);
} }
if (!$this->processed) { if (!$this->processed) {
$this->process($_template); $this->compileAndLoad($_template);
} }
$_template->getCached()->file_dependency = $_template->getCached()->file_dependency =
array_merge($_template->getCached()->file_dependency, $this->file_dependency); array_merge($_template->getCached()->file_dependency, $this->file_dependency);
$_template->getRenderedTemplateCode($this->unifunc); $this->getRenderedTemplateCode($_template, $this->unifunc);
if ($_template->caching && $this->getNocacheCode()) { if ($_template->caching && $this->getNocacheCode()) {
$_template->getCached()->hashes[$this->nocache_hash] = true; $_template->getCached()->hashes[$this->nocache_hash] = true;
@@ -124,29 +123,23 @@ class Compiled extends GeneratedPhpFile {
* *
* @throws Exception * @throws Exception
*/ */
private function process(Template $_smarty_tpl) { private function compileAndLoad(Template $_smarty_tpl) {
$source = $_smarty_tpl->getSource(); $source = $_smarty_tpl->getSource();
$smarty = $_smarty_tpl->getSmarty(); $smarty = $_smarty_tpl->getSmarty();
if ($source->handler->recompiled) { if ($source->handler->recompiled) {
$source->handler->process($_smarty_tpl); $source->handler->recompile($_smarty_tpl); // @TODO who is compiling here?
} else { } else {
if (!$this->exists || $smarty->force_compile if (!$this->exists || $smarty->force_compile
|| ($_smarty_tpl->compile_check && $source->getTimeStamp() > $this->getTimeStamp()) || ($_smarty_tpl->compile_check && $source->getTimeStamp() > $this->getTimeStamp())
) { ) {
$this->compileTemplateSource($_smarty_tpl); $this->compileTemplateSource($_smarty_tpl);
$compileCheck = $_smarty_tpl->compile_check;
$_smarty_tpl->compile_check = \Smarty\Smarty::COMPILECHECK_OFF;
$this->loadCompiledTemplate($_smarty_tpl); $this->loadCompiledTemplate($_smarty_tpl);
$_smarty_tpl->compile_check = $compileCheck;
} else { } else {
$_smarty_tpl->mustCompile = true; $_smarty_tpl->mustCompile = true;
@include $this->filepath; @include $this->filepath;
if ($_smarty_tpl->mustCompile) { if ($_smarty_tpl->mustCompile) {
$this->compileTemplateSource($_smarty_tpl); $this->compileTemplateSource($_smarty_tpl);
$compileCheck = $_smarty_tpl->compile_check;
$_smarty_tpl->compile_check = \Smarty\Smarty::COMPILECHECK_OFF;
$this->loadCompiledTemplate($_smarty_tpl); $this->loadCompiledTemplate($_smarty_tpl);
$_smarty_tpl->compile_check = $compileCheck;
} }
} }
$this->processed = true; $this->processed = true;
@@ -192,7 +185,7 @@ class Compiled extends GeneratedPhpFile {
* @return bool success * @return bool success
* @throws \Smarty\Exception * @throws \Smarty\Exception
*/ */
public function write(Template $_template, $code) { private function write(Template $_template, $code) {
if (!$_template->getSource()->handler->recompiled) { if (!$_template->getSource()->handler->recompiled) {
if ($_template->getSmarty()->writeFile($this->filepath, $code) === true) { if ($_template->getSmarty()->writeFile($this->filepath, $code) === true) {
$this->timestamp = $this->exists = is_file($this->filepath); $this->timestamp = $this->exists = is_file($this->filepath);
@@ -206,20 +199,6 @@ class Compiled extends GeneratedPhpFile {
return true; return true;
} }
/**
* Read compiled content from handler
*
* @param Template $_template template object
*
* @return string content
*/
public function read(Template $_template) {
if (!$_template->getSource()->handler->recompiled) {
return file_get_contents($this->filepath);
}
return false;
}
/** /**
* Load fresh compiled template by including the PHP file * Load fresh compiled template by including the PHP file
* HHVM requires a workaround because of a PHP incompatibility * HHVM requires a workaround because of a PHP incompatibility
@@ -227,6 +206,8 @@ class Compiled extends GeneratedPhpFile {
* @param \Smarty\Template $_smarty_tpl do not change variable name, is used by compiled template * @param \Smarty\Template $_smarty_tpl do not change variable name, is used by compiled template
*/ */
private function loadCompiledTemplate(Template $_smarty_tpl) { private function loadCompiledTemplate(Template $_smarty_tpl) {
$compileCheck = $_smarty_tpl->compile_check;
$_smarty_tpl->compile_check = \Smarty\Smarty::COMPILECHECK_OFF;
if (function_exists('opcache_invalidate') if (function_exists('opcache_invalidate')
&& (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1) && (!function_exists('ini_get') || strlen(ini_get("opcache.restrict_api")) < 1)
) { ) {
@@ -239,6 +220,7 @@ class Compiled extends GeneratedPhpFile {
} else { } else {
include $this->filepath; include $this->filepath;
} }
$_smarty_tpl->compile_check = $compileCheck;
} }
} }

View File

@@ -41,7 +41,7 @@ abstract class GeneratedPhpFile {
* *
* @var boolean * @var boolean
*/ */
public $processed = false; protected $processed = false;
/** /**
* unique function name for compiled template code * unique function name for compiled template code
@@ -90,4 +90,27 @@ abstract class GeneratedPhpFile {
$this->has_nocache_code = $has_nocache_code; $this->has_nocache_code = $has_nocache_code;
} }
/**
* get rendered template content by calling compiled or cached template code
*
* @param string $unifunc function with template code
*
* @throws \Exception
*/
protected function getRenderedTemplateCode(\Smarty\Template $_template, $unifunc) {
$level = ob_get_level();
try {
if (empty($unifunc) || !function_exists($unifunc)) {
throw new \Smarty\Exception("Invalid compiled template for '{$this->filepath}'");
}
$unifunc($_template);
} catch (\Exception $e) {
while (ob_get_level() > $level) {
ob_end_clean();
}
throw $e;
}
}
} }

View File

@@ -234,4 +234,14 @@ class Source {
return $this->smarty; return $this->smarty;
} }
/**
* Determine basename for compiled filename
*
* @return string resource's basename
*/
public function getBasename()
{
return $this->handler->getBasename($this);
}
} }

View File

@@ -7,7 +7,7 @@ use Smarty\Template\Source;
class Smarty_Resource_FiletestPlugin extends FilePlugin class Smarty_Resource_FiletestPlugin extends FilePlugin
{ {
/** /**
* populate Source Object with meta data from Resource * populate Source Object with metadata from Resource
* *
* @param Source $source source object * @param Source $source source object
* @param Template $_template template object * @param Template $_template template object

View File

@@ -17,20 +17,22 @@ class Smarty_CacheResource_Memcache extends \Smarty\Cacheresource\KeyValueStore
* *
* @var Memcache * @var Memcache
*/ */
protected $memcache = null; private $memcache = null;
/** /**
* Smarty_CacheResource_Memcache constructor. * @return Memcache|Memcached
*/ */
public function __construct() public function getMemcache() {
{ if ($this->memcache === null) {
if (class_exists('Memcached')) { if (class_exists('Memcached')) {
$this->memcache = new Memcached(); $this->memcache = new Memcached();
} else { } else {
$this->memcache = new Memcache(); $this->memcache = new Memcache();
} }
$this->memcache->addServer('127.0.0.1', 11211); $this->memcache->addServer('127.0.0.1', 11211);
} }
return $this->memcache;
}
/** /**
* Read values for a set of keys from cache * Read values for a set of keys from cache
@@ -45,7 +47,7 @@ class Smarty_CacheResource_Memcache extends \Smarty\Cacheresource\KeyValueStore
$res = array(); $res = array();
foreach ($keys as $key) { foreach ($keys as $key) {
$k = sha1($key); $k = sha1($key);
$res[$key] = $this->memcache->get($k); $res[$key] = $this->getMemcache()->get($k);
} }
return $res; return $res;
} }
@@ -63,9 +65,9 @@ class Smarty_CacheResource_Memcache extends \Smarty\Cacheresource\KeyValueStore
foreach ($keys as $k => $v) { foreach ($keys as $k => $v) {
$k = sha1($k); $k = sha1($k);
if (class_exists('Memcached')) { if (class_exists('Memcached')) {
$this->memcache->set($k, $v, $expire); $this->getMemcache()->set($k, $v, $expire);
} else { } else {
$this->memcache->set($k, $v, 0, $expire); $this->getMemcache()->set($k, $v, 0, $expire);
} }
} }
return true; return true;
@@ -82,7 +84,7 @@ class Smarty_CacheResource_Memcache extends \Smarty\Cacheresource\KeyValueStore
{ {
foreach ($keys as $k) { foreach ($keys as $k) {
$k = sha1($k); $k = sha1($k);
$this->memcache->delete($k); $this->getMemcache()->delete($k);
} }
return true; return true;
} }
@@ -94,6 +96,6 @@ class Smarty_CacheResource_Memcache extends \Smarty\Cacheresource\KeyValueStore
*/ */
protected function purge() protected function purge()
{ {
return $this->memcache->flush(); return $this->getMemcache()->flush();
} }
} }

View File

@@ -29,7 +29,8 @@ class My_Resource_Extendsall extends \Smarty\Resource\ExtendsPlugin
$timestamp = 0; $timestamp = 0;
foreach ($source->getSmarty()->getTemplateDir() as $key => $directory) { foreach ($source->getSmarty()->getTemplateDir() as $key => $directory) {
try { try {
$s = Smarty\Resource\BasePlugin::source(null, $source->getSmarty(), 'file:' . '[' . $key . ']' . $source->name); $s = \Smarty\Template\Source::load(null, $source->getSmarty(),
'file:' . '[' . $key . ']' . $source->name);
if (!$s->exists) { if (!$s->exists) {
continue; continue;
} }