- optimization of capture and security handling

This commit is contained in:
uwetews
2016-03-11 02:55:55 +01:00
parent 62bf9eeddc
commit af71fda639
8 changed files with 262 additions and 82 deletions

View File

@@ -1,4 +1,7 @@
 ===== 3.1.30-dev ===== (xx.xx.xx)
11.03.2014
- optimization of capture and security handling
10.03.2014
- optimization of resource processing

View File

@@ -121,7 +121,7 @@ class Smarty extends Smarty_Internal_TemplateBase
/**
* smarty version
*/
const SMARTY_VERSION = '3.1.30-dev/55';
const SMARTY_VERSION = '3.1.30-dev/56';
/**
* define variable scopes

View File

@@ -32,32 +32,6 @@ class Smarty_Internal_Compile_Capture extends Smarty_Internal_CompileBase
*/
public $optional_attributes = array('name', 'assign', 'append');
/**
* Compiles code for the {capture} tag
*
* @param array $args array with attributes from parser
* @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
*
* @return string compiled code
*/
public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
{
// check and get attributes
$_attr = $this->getAttributes($compiler, $args);
$buffer = isset($_attr[ 'name' ]) ? $_attr[ 'name' ] : "'default'";
$assign = isset($_attr[ 'assign' ]) ? $_attr[ 'assign' ] : 'null';
$append = isset($_attr[ 'append' ]) ? $_attr[ 'append' ] : 'null';
$this->openTag($compiler, 'capture', array($buffer, $assign, $append, $compiler->nocache));
// maybe nocache because of nocache variables
$compiler->nocache = $compiler->nocache | $compiler->tag_nocache;
$_output =
"<?php \$_smarty_tpl->smarty->_cache['capture_stack'][] = array($buffer, $assign, $append); ob_start(); ?>";
return $_output;
}
/**
* Compiles code for the {$smarty.capture.xxx}
*
@@ -68,14 +42,42 @@ class Smarty_Internal_Compile_Capture extends Smarty_Internal_CompileBase
* @return string compiled code
* @throws \SmartyCompilerException
*/
public static function compileSpecialVariable($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
public static function compileSpecialVariable($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter = null)
{
$tag = strtolower(trim($parameter[ 0 ], '"\''));
// make all lower case
$parameter = array_map('strtolower', $parameter);
$tag = trim($parameter[ 0 ], '"\'');
$name = isset($parameter[ 1 ]) ? $compiler->getId($parameter[ 1 ]) : false;
if (!$name) {
$compiler->trigger_template_error("missing or illegal \$smarty.{$tag} name attribute", null, true);
}
return "(isset(\$_smarty_tpl->smarty->_cache['__smarty_capture']['{$name}']) ? \$_smarty_tpl->smarty->_cache['__smarty_capture']['{$name}'] : null)";
return "\$_smarty_tpl->ext->_capture->getBuffer(\$_smarty_tpl, '{$name}')";
}
/**
* Compiles code for the {capture} tag
*
* @param array $args array with attributes from parser
* @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
* @param null $parameter
*
* @return string compiled code
*/
public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter = null)
{
// check and get attributes
$_attr = $this->getAttributes($compiler, $args, $parameter, 'capture');
$buffer = isset($_attr[ 'name' ]) ? $_attr[ 'name' ] : "'default'";
$assign = isset($_attr[ 'assign' ]) ? $_attr[ 'assign' ] : 'null';
$append = isset($_attr[ 'append' ]) ? $_attr[ 'append' ] : 'null';
$compiler->_cache[ 'capture_stack' ][] = array($compiler->nocache);
// maybe nocache because of nocache variables
$compiler->nocache = $compiler->nocache | $compiler->tag_nocache;
$_output = "<?php \$_smarty_tpl->ext->_capture->open(\$_smarty_tpl, $buffer, $assign, $append);\n?>\n";
return $_output;
}
}
@@ -92,27 +94,21 @@ class Smarty_Internal_Compile_CaptureClose extends Smarty_Internal_CompileBase
*
* @param array $args array with attributes from parser
* @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
* @param null $parameter
*
* @return string compiled code
*/
public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
{
// check and get attributes
$_attr = $this->getAttributes($compiler, $args);
$_attr = $this->getAttributes($compiler, $args, $parameter, '/capture');
// must endblock be nocache?
if ($compiler->nocache) {
$compiler->tag_nocache = true;
}
list($buffer, $assign, $append, $compiler->nocache) = $this->closeTag($compiler, array('capture'));
$_output =
"<?php list(\$_capture_buffer, \$_capture_assign, \$_capture_append) = array_pop(\$_smarty_tpl->smarty->_cache['capture_stack']);\n";
$_output .= "if (!empty(\$_capture_buffer)) {\n";
$_output .= " if (isset(\$_capture_assign)) \$_smarty_tpl->assign(\$_capture_assign, ob_get_contents());\n";
$_output .= " if (isset( \$_capture_append)) \$_smarty_tpl->append( \$_capture_append, ob_get_contents());\n";
$_output .= "\$_smarty_tpl->smarty->_cache['__smarty_capture'][\$_capture_buffer]=ob_get_clean();\n";
$_output .= "} else \$_smarty_tpl->capture_error();?>";
list($compiler->nocache) = array_pop($compiler->_cache[ 'capture_stack' ]);
return $_output;
return "<?php \$_smarty_tpl->ext->_capture->close(\$_smarty_tpl);\n?>\n";
}
}

View File

@@ -0,0 +1,155 @@
<?php
/**
* Runtime Extension Capture
*
* @package Smarty
* @subpackage PluginsInternal
* @author Uwe Tews
*/
class Smarty_Internal_Runtime_Capture
{
/**
* Flag that this instance will not be cached
*
* @var bool
*/
public $isPrivateExtension = true;
/**
* Stack of capture parameter
*
* @var array
*/
private $captureStack = array();
/**
* Current open capture sections
*
* @var int
*/
private $captureCount = 0;
/**
* Count stack
*
* @var int[]
*/
private $countStack = array();
/**
* Flag if callbacks are registered
*
* @var bool
*/
private $isRegistered = false;
/**
* Open capture section
*
* @param \Smarty_Internal_Template $_template
* @param string $buffer capture name
* @param string $assign variable name
* @param string $append variable name
*/
public function open(Smarty_Internal_Template $_template, $buffer, $assign, $append)
{
if (!$this->isRegistered) {
$this->register($_template);
}
$this->captureStack[] = array($buffer, $assign, $append);
$this->captureCount ++;
ob_start();
}
/**
* Register callbacks in template class
*
* @param \Smarty_Internal_Template $_template
*/
private function register(Smarty_Internal_Template $_template)
{
$_template->startRenderCallbacks[] = array($this, 'startRender');
$_template->endRenderCallbacks[] = array($this, 'endRender');
$this->startRender($_template);
$this->isRegistered = true;
}
/**
* Start render callback
*
* @param \Smarty_Internal_Template $_template
*/
public function startRender(Smarty_Internal_Template $_template)
{
$this->countStack[] = $this->captureCount;
$this->captureCount = 0;
$_template->_cache[ 'capture' ] = array();
}
/**
* Close capture section
*
* @param \Smarty_Internal_Template $_template
*
* @throws \SmartyException
*/
public function close(Smarty_Internal_Template $_template)
{
if ($this->captureCount) {
list($buffer, $assign, $append) = array_pop($this->captureStack);
$this->captureCount --;
if (isset($assign)) {
$_template->assign($assign, ob_get_contents());
}
if (isset($append)) {
$_template->append($append, ob_get_contents());
}
$_template->_cache[ 'capture' ][ $buffer ] = ob_get_clean();
} else {
$this->error($_template);
}
}
/**
* Error exception on not matching {capture}{/capture}
*
* @param \Smarty_Internal_Template $_template
*
* @throws \SmartyException
*/
public function error(Smarty_Internal_Template $_template)
{
throw new SmartyException("Not matching {capture}{/capture} in \"{$_template->template_resource}\"");
}
/**
* Return content of named capture buffer
*
* @param \Smarty_Internal_Template $_template
* @param $name
*
* @return null
*/
public function getBuffer(Smarty_Internal_Template $_template, $name)
{
return isset($_template->_cache[ 'capture' ][ $name ]) ? $_template->_cache[ 'capture' ][ $name ] : null;
}
/**
* End render callback
*
* @param \Smarty_Internal_Template $_template
*
* @throws \SmartyException
*/
public function endRender(Smarty_Internal_Template $_template)
{
if ($this->captureCount) {
$this->error($_template);
} else {
$this->captureCount = array_pop($this->countStack);
}
}
}

View File

@@ -87,6 +87,21 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
*/
public $isRenderingCache = false;
/**
* Callbacks called before rendering template
*
* @var callback[]
*/
public $startRenderCallbacks = array();
/**
* Callbacks called after rendering template
*
* @var callback[]
*/
public $endRenderCallbacks = array();
/**
* Create template data object
* Some of the global Smarty settings copied to template scope
@@ -120,6 +135,9 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
$this->template_resource = $template_resource;
$this->source = Smarty_Template_Source::load($this);
parent::__construct();
if ($smarty->security_policy && method_exists($smarty->security_policy, 'registerCallBacks')) {
$smarty->security_policy->registerCallBacks($this);
}
}
/**

View File

@@ -387,15 +387,21 @@ class Smarty_Internal_TestInstall
'smarty_internal_compile_make_nocache.php' => true,
'smarty_internal_compile_nocache.php' => true,
'smarty_internal_compile_private_block_plugin.php' => true,
'smarty_internal_compile_private_compiler_plugin.php' => true,
'smarty_internal_compile_private_foreachsection.php' => true,
'smarty_internal_compile_private_function_plugin.php' => true,
'smarty_internal_compile_private_modifier.php' => true,
'smarty_internal_compile_private_modifiercompiler_plugin.php' => true,
'smarty_internal_compile_private_modifier_plugin.php' => true,
'smarty_internal_compile_private_object.php' => true,
'smarty_internal_compile_private_object_block_function.php' => true,
'smarty_internal_compile_private_object_function.php' => true,
'smarty_internal_compile_private_php.php' => true,
'smarty_internal_compile_private_print_expression.php' => true,
'smarty_internal_compile_private_registered_block.php' => true,
'smarty_internal_compile_private_registered_compiler.php' => true,
'smarty_internal_compile_private_registered_function.php' => true,
'smarty_internal_compile_private_registered_modifier.php' => true,
'smarty_internal_compile_private_special_variable.php' => true,
'smarty_internal_compile_rdelim.php' => true,
'smarty_internal_compile_section.php' => true,
@@ -469,7 +475,9 @@ class Smarty_Internal_TestInstall
'smarty_internal_resource_registered.php' => true,
'smarty_internal_resource_stream.php' => true,
'smarty_internal_resource_string.php' => true,
'smarty_internal_runtime_assignindexed.php' => true,
'smarty_internal_runtime_cachemodify.php' => true,
'smarty_internal_runtime_capture.php' => true,
'smarty_internal_runtime_codeframe.php' => true,
'smarty_internal_runtime_filterhandler.php' => true,
'smarty_internal_runtime_foreach.php' => true,

View File

@@ -626,32 +626,6 @@ class Smarty_Security
return true;
}
/**
* Start template processing
*
* @param $template
*
* @throws SmartyException
*/
public function startTemplate($template)
{
if ($this->max_template_nesting > 0 && $this->_current_template_nesting ++ >= $this->max_template_nesting) {
throw new SmartyException("maximum template nesting level of '{$this->max_template_nesting}' exceeded when calling '{$template->template_resource}'");
}
}
/**
* Exit template processing
*
* @internal param $template
*/
public function exitTemplate()
{
if ($this->max_template_nesting > 0) {
$this->_current_template_nesting --;
}
}
/**
* Check if file is inside a valid directory
*
@@ -716,4 +690,39 @@ class Smarty_Security
}
return;
}
/**
* Start template processing
*
* @param $template
*
* @throws SmartyException
*/
public function startTemplate($template)
{
if ($this->max_template_nesting > 0 && $this->_current_template_nesting ++ >= $this->max_template_nesting) {
throw new SmartyException("maximum template nesting level of '{$this->max_template_nesting}' exceeded when calling '{$template->template_resource}'");
}
}
/**
* Exit template processing
*
*/
public function endTemplate()
{
if ($this->max_template_nesting > 0) {
$this->_current_template_nesting --;
}
}
/**
* Register callback functions call at start/end of template rendering
*
* @param \Smarty_Internal_Template $template
*/
public function registerCallBacks(Smarty_Internal_Template $template)
{
$template->startRenderCallbacks[] = array($this, 'startTemplate');
$template->endRenderCallbacks[] = array($this, 'endTemplate');
}
}

View File

@@ -121,23 +121,14 @@ abstract class Smarty_Template_Resource_Base
if (empty($unifunc) || !function_exists($unifunc)) {
throw new SmartyException("Invalid compiled template for '{$_template->template_resource}'");
}
if (isset($smarty->security_policy)) {
$smarty->security_policy->startTemplate($_template);
if ($_template->startRenderCallbacks) {
foreach ($_template->startRenderCallbacks as $callback) {
call_user_func($callback, $_template);
}
//
// render compiled or saved template code
//
if (!isset($_template->_cache[ 'capture_stack' ])) {
$_template->_cache[ 'capture_stack' ] = array();
}
$_saved_capture_level = count($_template->_cache[ 'capture_stack' ]);
$unifunc($_template);
// any unclosed {capture} tags ?
if ($_saved_capture_level != count($_template->_cache[ 'capture_stack' ])) {
$_template->capture_error();
}
if (isset($smarty->security_policy)) {
$smarty->security_policy->exitTemplate();
foreach ($_template->endRenderCallbacks as $callback) {
call_user_func($callback, $_template);
}
$_template->isRenderingCache = false;
return null;
@@ -148,7 +139,7 @@ abstract class Smarty_Template_Resource_Base
ob_end_clean();
}
if (isset($smarty->security_policy)) {
$smarty->security_policy->exitTemplate();
$smarty->security_policy->endTemplate();
}
throw $e;
}