- rework of template inheritance

This commit is contained in:
uwetews
2015-10-18 04:46:05 +02:00
parent ca9ccfc919
commit 8dc2a0af7c
9 changed files with 451 additions and 303 deletions

View File

@@ -1,6 +1,7 @@
 ===== 3.1.28-dev===== (xx.xx.2015)
18.10.2015
- optimize filepath normalization
- rework of template inheritance
18.09.2015
- bugfix {if $foo instanceof $bar} failed to compile if 2nd value is a variable https://github.com/smarty-php/smarty/issues/92

View File

@@ -13,7 +13,7 @@
*
* @author Uwe Tews <uwe.tews@googlemail.com>
*/
class Smarty_Internal_Compile_Block extends Smarty_Internal_CompileBase
class Smarty_Internal_Compile_Block extends Smarty_Internal_Compile_Shared_Inheritance
{
/**
* Attribute definition: Overwrites base class.
@@ -45,7 +45,7 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_CompileBase
* @var array
* @see Smarty_Internal_CompileBase
*/
public $optional_attributes = array();
public $optional_attributes = array('assign');
/**
* nesting level of block tags
@@ -54,6 +54,13 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_CompileBase
*/
public static $blockTagNestingLevel = 0;
/**
* Saved compiler object
*
* @var Smarty_Internal_TemplateCompilerBase
*/
public $compiler = null;
/**
* Compiles code for the {block} tag
*
@@ -65,14 +72,22 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_CompileBase
*/
public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
{
if ($compiler->blockTagNestingLevel == 0 && $compiler->inheritanceChild) {
if (!isset($compiler->_cache['blockNesting'])) {
$compiler->_cache['blockNesting'] = 0;
}
if ($compiler->_cache['blockNesting'] == 0) {
// make sure that inheritance gets initialized in template code
$this->registerInit($compiler);
$this->option_flags = array('hide', 'nocache', 'append', 'prepend');
} else {
$this->option_flags = array('hide', 'nocache');
}
// check and get attributes
$_attr = $this->getAttributes($compiler, $args);
$compiler->blockTagNestingLevel ++;
$compiler->_cache['blockNesting'] ++;
$compiler->_cache['blockName'][$compiler->_cache['blockNesting']] = $_attr['name'];
$compiler->_cache['blockParams'][$compiler->_cache['blockNesting']][0] = 'block_' . preg_replace('![^\w]+!', '_', uniqid(rand(), true));
$compiler->_cache['blockParams'][$compiler->_cache['blockNesting']][1] = false;
$this->openTag($compiler, 'block', array($_attr, $compiler->nocache, $compiler->parser->current_buffer,
$compiler->template->compiled->has_nocache_code,
$compiler->template->caching));
@@ -100,15 +115,15 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_CompileBase
*/
static function compileChildBlock(Smarty_Internal_TemplateCompilerBase $compiler, $_name = null)
{
if (!$compiler->blockTagNestingLevel) {
if (!isset($compiler->_cache['blockNesting'])) {
$compiler->trigger_template_error(' tag {$smarty.block.child} used outside {block} tags ',
$compiler->parser->lex->taglineno);
}
$compiler->has_code = true;
$compiler->suppressNocacheProcessing = true;
$compiler->callChildBlock[$compiler->blockTagNestingLevel] = true;
$_output = "<?php \n\$_smarty_tpl->_Block->callChildBlock(\$_smarty_tpl, \$block);?>";
return $_output;
$compiler->_cache['blockParams'][$compiler->_cache['blockNesting']][1] = true;
$output = "<?php \n\$_smarty_tpl->_inheritance->processBlock(\$_smarty_tpl, 2, {$compiler->_cache['blockName'][$compiler->_cache['blockNesting']]}, null, \$_blockParentStack);\n?>\n";
return $output;
}
/**
@@ -121,18 +136,14 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_CompileBase
*/
static function compileParentBlock(Smarty_Internal_TemplateCompilerBase $compiler, $_name = null)
{
if (!$compiler->inheritanceChild) {
$compiler->trigger_template_error(' tag {$smarty.block.parent} used in parent template ',
$compiler->parser->lex->taglineno);
}
if (!$compiler->blockTagNestingLevel) {
if (!isset($compiler->_cache['blockNesting'])) {
$compiler->trigger_template_error(' tag {$smarty.block.parent} used outside {block} tags ',
$compiler->parser->lex->taglineno);
}
$compiler->suppressNocacheProcessing = true;
$compiler->has_code = true;
$_output = "<?php \n\$_smarty_tpl->_Block->callParentBlock(\$_smarty_tpl, \$block);?>";
return $_output;
$output = "<?php \n\$_smarty_tpl->_inheritance->processBlock(\$_smarty_tpl, 3, {$compiler->_cache['blockName'][$compiler->_cache['blockNesting']]}, null, \$_blockParentStack);\n?>\n";
return $output;
}
}
@@ -140,7 +151,7 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_CompileBase
* Smarty Internal Plugin Compile BlockClose Class
*
*/
class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_CompileBase
class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_Compile_Shared_Inheritance
{
/**
* Compiles code for the {/block} tag
@@ -153,57 +164,83 @@ class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_CompileBase
*/
public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
{
$this->compiler = $compiler;
list($_attr, $_nocache, $_buffer, $_has_nocache_code, $_caching) = $this->closeTag($compiler, array('block'));
$_name = trim($_attr['name'], "'\"");
// init block parameter
$_block = $compiler->_cache['blockParams'][$compiler->_cache['blockNesting']];
unset($compiler->_cache['blockParams'][$compiler->_cache['blockNesting']]);
$_block[2] = $_block[3] = 0;
$_name = trim($_attr['name'], "'\"");
$_assign = isset($_attr['assign']) ? $_attr['assign'] : null;
unset($_attr['assign'], $_attr['name']);
foreach ($_attr as $name => $stat) {
if ((is_bool($stat) && $stat !== false) || (!is_bool($stat) && $stat != 'false')) {
$_block[$name] = is_string($stat) ? trim($stat, "'\"") : $stat;
}
}
$_funcName = $_block[0];
// get compiled block code
$_functionCode = $compiler->parser->current_buffer;
// setup buffer for template function code
$compiler->parser->current_buffer = new Smarty_Internal_ParseTree_Template();
$_funcNameCaching =
$_funcName = preg_replace('![^\w]+!', '_', "block_function_{$_name}_" . uniqid(rand(), true));
if ($compiler->template->compiled->has_nocache_code) {
// $compiler->parent_compiler->template->tpl_function[$_name]['call_name_caching'] = $_funcNameCaching;
$_funcNameCaching .= '_nocache';
$output = "<?php\n\n";
$_block[6] = $_funcNameCaching = $_funcName . '_nocache';
$output = "<?php\n";
$output .= "/* {block '{$_name}'} {$compiler->template->source->type}:{$compiler->template->source->name} */\n";
$output .= "function {$_funcNameCaching} (\$_smarty_tpl, \$block) {\n";
$output .= "function {$_funcNameCaching} (\$_smarty_tpl, \$_blockParentStack) {\n";
$output .= "/*/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\n";
$output .= "\$_smarty_tpl->cached->hashes['{$compiler->template->compiled->nocache_hash}'] = true;\n?>\n";
$output .= "\$_smarty_tpl->cached->hashes['{$compiler->template->compiled->nocache_hash}'] = true;\n";
if (isset($_assign)) {
$output .= "ob_start();\n";
}
$output .= "?>\n";
$compiler->parser->current_buffer->append_subtree($compiler->parser,
new Smarty_Internal_ParseTree_Tag($compiler->parser,
$output));
$compiler->parser->current_buffer->append_subtree($compiler->parser, $_functionCode);
$output = "<?php /*%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\n";
$output .= "\n}\n";
$output = "<?php\n";
if (isset($_assign)) {
$output .= "\$_smarty_tpl->tpl_vars[{$_assign}] = new Smarty_Variable(ob_get_contents());\n";
}
$output .= "/*%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\n";
$output .= "}\n";
$output .= "/* {/block '{$_name}'} */\n\n";
$output .= "?>\n";
$compiler->parser->current_buffer->append_subtree($compiler->parser,
new Smarty_Internal_ParseTree_Tag($compiler->parser,
$output));
$compiler->blockOrFunctionCode .= $f =
$compiler->parser->current_buffer->to_smarty_php($compiler->parser);
$compiler->blockOrFunctionCode .= $f = $compiler->parser->current_buffer->to_smarty_php($compiler->parser);
$compiler->parser->current_buffer = new Smarty_Internal_ParseTree_Template();
$this->compiler = $compiler;
$_functionCode = new Smarty_Internal_ParseTree_Tag($compiler->parser,
preg_replace_callback("/((<\?php )?echo '\/\*%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%\*\/([\S\s]*?)\/\*\/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%\*\/';(\?>\n)?)/",
array($this, 'removeNocache'),
$_functionCode->to_smarty_php($compiler->parser)));
$this->compiler = null;
}
$output = "<?php\n\n";
$output = "<?php\n";
$output .= "/* {block '{$_name}'} {$compiler->template->source->type}:{$compiler->template->source->name} */\n";
$output .= "function {$_funcName}(\$_smarty_tpl, \$block) {?>";
$output .= "function {$_funcName}(\$_smarty_tpl, \$_blockParentStack) {\n";
if (isset($_assign)) {
$output .= "ob_start();\n";
}
$output .= "?>\n";
$compiler->parser->current_buffer->append_subtree($compiler->parser,
new Smarty_Internal_ParseTree_Tag($compiler->parser,
$output));
$compiler->parser->current_buffer->append_subtree($compiler->parser, $_functionCode);
$output = "<?php\n}\n";
$output = "<?php\n";
if (isset($_assign)) {
$output .= "\$_smarty_tpl->tpl_vars[{$_assign}] = new Smarty_Variable(ob_get_contents());\n";
}
$output .= "}\n";
$output .= "/* {/block '{$_name}'} */\n\n";
$output .= "?>\n";
$compiler->parser->current_buffer->append_subtree($compiler->parser,
new Smarty_Internal_ParseTree_Tag($compiler->parser,
$output));
$compiler->blockOrFunctionCode .= $f =
$compiler->parser->current_buffer->to_smarty_php($compiler->parser);
$compiler->blockOrFunctionCode .= $compiler->parser->current_buffer->to_smarty_php($compiler->parser);
// nocache plugins must be copied
if (!empty($compiler->template->compiled->required_plugins['nocache'])) {
foreach ($compiler->template->compiled->required_plugins['nocache'] as $plugin => $tmp) {
@@ -213,38 +250,27 @@ class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_CompileBase
}
}
}
// restore old status
$compiler->template->compiled->has_nocache_code = $_has_nocache_code;
$compiler->tag_nocache = $compiler->nocache;
$compiler->nocache = $_nocache;
$compiler->parser->current_buffer = $_buffer;
$_parameter = $_attr;
foreach ($_parameter as $name => $stat) {
if ($stat === false) {
unset($_parameter[$name]);
}
}
if (isset($compiler->callChildBlock[$compiler->blockTagNestingLevel])) {
$_parameter['callChildBlock'] = 'true';
unset($compiler->callChildBlock[$compiler->blockTagNestingLevel]);
}
$compiler->blockTagNestingLevel --;
// inner {block} or child template {block} must register block
if ($compiler->blockTagNestingLevel == 0 && $compiler->inheritanceChild) {
$_function = 'register';
$output = "<?php \n";
if ($compiler->_cache['blockNesting'] == 1) {
$output .= "\$_smarty_tpl->_inheritance->processBlock(\$_smarty_tpl, 0, {$compiler->_cache['blockName'][$compiler->_cache['blockNesting']]}, " .
var_export($_block, true) . ");\n";
} else {
$_function = 'call';
$output .= "\$_smarty_tpl->_inheritance->processBlock(\$_smarty_tpl, 0, {$compiler->_cache['blockName'][$compiler->_cache['blockNesting']]}, " .
var_export($_block, true) . ", \$_blockParentStack);\n";
}
$cm = $compiler->template->caching ? 'true' : 'false';
$output =
"<?php \n\$_smarty_tpl->_Block->{$_function}Block(\$_smarty_tpl, array('caching' => {$cm}, 'function' => '{$_funcNameCaching}'";
foreach ($_parameter as $name => $stat) {
if ($stat !== false) {
$output .= ", '{$name}' => {$stat}";
}
$output .= "?>\n";
$compiler->_cache['blockNesting'] --;
if ($compiler->_cache['blockNesting'] == 0) {
unset($compiler->_cache['blockNesting']);
}
$output .= "));\n?>\n";
$compiler->has_code = true;
$compiler->suppressNocacheProcessing = true;
return $output;

View File

@@ -15,7 +15,7 @@
* @package Smarty
* @subpackage Compiler
*/
class Smarty_Internal_Compile_Extends extends Smarty_Internal_CompileBase
class Smarty_Internal_Compile_Extends extends Smarty_Internal_Compile_Shared_Inheritance
{
/**
* Attribute definition: Overwrites base class.
@@ -25,6 +25,14 @@ class Smarty_Internal_Compile_Extends extends Smarty_Internal_CompileBase
*/
public $required_attributes = array('file');
/**
* Array of names of optional attribute required by tag
* use array('_any') if there is no restriction of attributes names
*
* @var array
*/
public $optional_attributes = array('extends_resource');
/**
* Attribute definition: Overwrites base class.
*
@@ -34,7 +42,7 @@ class Smarty_Internal_Compile_Extends extends Smarty_Internal_CompileBase
public $shorttag_order = array('file');
/**
* Compiles code for the {extends} tag
* Compiles code for the {extends} tag extends: resource
*
* @param array $args array with attributes from parser
* @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
@@ -53,11 +61,74 @@ class Smarty_Internal_Compile_Extends extends Smarty_Internal_CompileBase
if (strpos($_attr['file'], '$_tmp') !== false) {
$compiler->trigger_template_error('illegal value for file attribute', $compiler->parser->lex->line - 1);
}
// save template name
$compiler->extendsFileName = $_attr['file'];
// process {block} in child template mode
$compiler->inheritanceChild = true;
// add code to initialize inheritance
$this->registerInit($compiler, true);
$file = trim($_attr['file'], '\'"');
if (strlen($file) > 8 && substr($file, 0, 8) == 'extends:') {
// generate code for each template
$files = array_reverse(explode('|', substr($file, 8)));
$i = 0;
foreach ($files as $file) {
if ($file[0] == '"') {
$file = trim($file, '".');
} else {
$file = "'{$file}'";
}
$i ++;
if ($i == count($files) && isset($_attr['extends_resource'])) {
$this->compileEndChild($compiler);
}
$this->compileInclude($compiler, $file);
}
if (!isset($_attr['extends_resource'])) {
$this->compileEndChild($compiler);
}
} else {
$this->compileEndChild($compiler);
$this->compileInclude($compiler, $_attr['file']);
}
$compiler->has_code = false;
return '';
}
/**
* Add code for inheritance endChild() method to end of template
*
* @param \Smarty_Internal_TemplateCompilerBase $compiler
*/
private function compileEndChild(Smarty_Internal_TemplateCompilerBase $compiler)
{
$compiler->parser->template_postfix[] = new Smarty_Internal_ParseTree_Tag($compiler->parser,
"<?php \$_smarty_tpl->_inheritance->endChild(\$_smarty_tpl);\n?>\n");
}
/**
* Add code for including subtemplate to end of template
*
* @param \Smarty_Internal_TemplateCompilerBase $compiler
* @param string $file subtemplate name
*/
private function compileInclude(Smarty_Internal_TemplateCompilerBase $compiler, $file)
{
$compiler->parser->template_postfix[] = new Smarty_Internal_ParseTree_Tag($compiler->parser,
$compiler->compileTag('include',
array($file,
array('scope' => 'parent'))));
}
/**
* Create source code for {extends} from source components array
*
* @param []\Smarty_Internal_Template_Source $components
*
* @return string
*/
public static function extendsSourceArrayCode($components)
{
$resources = array();
foreach ($components as $source) {
$resources[] = $source->resource;
}
return '{extends file=\'extends:' . join('|', $resources) . '\' extends_resource=true}';
}
}

View File

@@ -98,9 +98,6 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase
} else {
$variable_template = true;
}
if ($compiler->inheritanceForceChild) {
$hashResourceName .= '-child';
}
if (isset($_attr['assign'])) {
// output will be stored in a smarty variable instead of being displayed
@@ -297,19 +294,22 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase
*
* @return bool
*/
public function compileInlineTemplate(Smarty_Internal_SmartyTemplateCompiler $compiler, $fullResourceName, $_caching, $hashResourceName, $t_hash, $c_id)
public function compileInlineTemplate(Smarty_Internal_SmartyTemplateCompiler $compiler, $fullResourceName,
$_caching, $hashResourceName, $t_hash, $c_id)
{
$compiler->smarty->allow_ambiguous_resources = true;
/* @var Smarty_Internal_Template $tpl */
$tpl = new $compiler->smarty->template_class (trim($fullResourceName, "'"), $compiler->smarty, $compiler->template,
$compiler->template->cache_id, $c_id, $_caching);
$compiler->parent_compiler->mergedSubTemplatesData[$hashResourceName][$t_hash]['uid'] = $tpl->source->uid;
$tpl =
new $compiler->smarty->template_class (trim($fullResourceName, "'"), $compiler->smarty, $compiler->template,
$compiler->template->cache_id, $c_id, $_caching);
if (!($tpl->source->handler->uncompiled) && $tpl->source->exists) {
$compiler->parent_compiler->mergedSubTemplatesData[$hashResourceName][$t_hash]['uid'] = $tpl->source->uid;
if (isset($compiler->template->_inheritance)) {
$tpl->_inheritance = clone $compiler->template->_inheritance;
}
$tpl->compiled = new Smarty_Template_Compiled();
$tpl->compiled->nocache_hash = $compiler->parent_compiler->template->compiled->nocache_hash;
$tpl->loadCompiler();
$tpl->compiler->inheritanceChild = $tpl->compiler->inheritanceForceChild = $compiler->inheritanceForceChild;
$tpl->compiler->inheritance = $compiler->inheritance;
// save unique function name
$compiler->parent_compiler->mergedSubTemplatesData[$hashResourceName][$t_hash]['func'] =
$tpl->compiled->unifunc = 'content_' . str_replace(array('.', ','), '_', uniqid('', true));
@@ -328,13 +328,12 @@ class Smarty_Internal_Compile_Include extends Smarty_Internal_CompileBase
$compiled_code .= "<?php\n\n";
$compiled_code .= "/* End inline template \"{$tpl->source->type}:{$tpl->source->name}\" =============================*/\n";
$compiled_code .= "?>";
$compiler->inheritanceParentIsChild = $tpl->compiler->inheritanceChild;
$compiler->inheritance = $tpl->compiler->inheritance;
unset($tpl->compiler);
if ($tpl->compiled->has_nocache_code) {
// replace nocache_hash
$compiled_code = str_replace("{$tpl->compiled->nocache_hash}", $compiler->template->compiled->nocache_hash,
$compiled_code);
$compiled_code =
str_replace("{$tpl->compiled->nocache_hash}", $compiler->template->compiled->nocache_hash,
$compiled_code);
$compiler->template->compiled->has_nocache_code = true;
}
$compiler->parent_compiler->mergedSubTemplatesCode[$tpl->compiled->unifunc] = $compiled_code;

View File

@@ -0,0 +1,46 @@
<?php
/**
* Smarty Internal Plugin Compile Shared Inheritance
* Shared methods for {extends} and {block} tags
*
* @package Smarty
* @subpackage Compiler
* @author Uwe Tews
*/
/**
* Smarty Internal Plugin Compile Shared Inheritance Class
*
* @package Smarty
* @subpackage Compiler
*/
class Smarty_Internal_Compile_Shared_Inheritance extends Smarty_Internal_CompileBase
{
/**
* Register post compile callback to compile inheritance initialization code
*
* @param \Smarty_Internal_TemplateCompilerBase $compiler
* @param bool|false $initChildSequence if true force child template
*/
public function registerInit(Smarty_Internal_TemplateCompilerBase $compiler, $initChildSequence = false)
{
if ($initChildSequence || !isset($compiler->_cache['inheritanceInit'])) {
$compiler->registerPostCompileCallback(array('Smarty_Internal_Compile_Shared_Inheritance', 'postCompile'),
array($initChildSequence), 'inheritanceInit', $initChildSequence);
$compiler->_cache['inheritanceInit'] = true;
}
}
/**
* Compile inheritance initialization code as prefix
*
* @param \Smarty_Internal_TemplateCompilerBase $compiler
* @param bool|false $initChildSequence if true force child template
*/
static function postCompile(Smarty_Internal_TemplateCompilerBase $compiler, $initChildSequence = false)
{
$compiler->prefixCompiledCode .= "<?php \$_smarty_tpl->_inheritance->init(\$_smarty_tpl, " .
var_export($initChildSequence, true) . ");\n?>\n";
}
}

View File

@@ -1,98 +0,0 @@
<?php
/**
* Runtime Method _callBlock, _callParentBlock, _callChildBlock, _registerBlock
*
* @package Smarty
* @subpackage PluginsInternal
* @author Uwe Tews
*
**/
class Smarty_Internal_Runtime_Block
{
public $inheritanceBlocks = array();
/**
* Call inheritance {block} tag
*
* @param \Smarty_Internal_Template $callerTpl template object of caller
* @param array $block block parameter
*/
public function callBlock(Smarty_Internal_Template $callerTpl, $block)
{
$function = $block['function'];
$level = isset($block['level']) ? $block['level'] : 0;
// find to level child block
while (!isset($block['callChildBlock']) &&
isset($this->inheritanceBlocks[$block['name']][$level])) {
$block = $this->inheritanceBlocks[$block['name']][$level];
$block['level'] = $level;
$level ++;
}
// ignore hidden block
if (isset($block['hide'])) {
return;
}
// root block function for possible parent block call
$block['root'] = $function;
if (isset($block['append'])) {
$this->callParentBlock($callerTpl, $block);
}
$block['function']($callerTpl, $block);
if (isset($block['prepend'])) {
$this->callParentBlock($callerTpl, $block);
}
}
/**
* Call inheritance parent {block} tag
*
* @param \Smarty_Internal_Template $callerTpl template object of caller
* @param array $block block parameter
*/
public function callParentBlock(Smarty_Internal_Template $callerTpl, $block)
{
$level = isset($block['level']) ? $block['level'] : 0;
if (isset($this->inheritanceBlocks[$block['name']][$level - 1])) {
// call registered parent
$parent = $this->inheritanceBlocks[$block['name']][$level - 1];
$parent['root'] = $block['root'];
$parent['function']($callerTpl, $parent);
} else {
// default to root block
$block['root']($callerTpl, $block);
}
}
/**
* Call inheritance child {block} tag
*
* @param \Smarty_Internal_Template $callerTpl template object of caller
* @param array $block block parameter
*/
public function callChildBlock(Smarty_Internal_Template $callerTpl, $block)
{
$level = isset($block['level']) ? $block['level'] : - 1;
if (isset($this->inheritanceBlocks[$block['name']][$level + 1])) {
$child = $this->inheritanceBlocks[$block['name']][$level + 1];
$child['level'] = $level + 1;
$child['function']($callerTpl, $child);
}
}
/**
* Register inheritance {block} tag
*
* @param \Smarty_Internal_Template $callerTpl template object of caller
* @param array $block block parameter
*/
public function registerBlock(Smarty_Internal_Template $callerTpl, $block)
{
if (!isset($this->inheritanceBlocks[$block['name']])) {
$this->inheritanceBlocks[$block['name']][0] = $block;
} else {
array_unshift($this->inheritanceBlocks[$block['name']], $block);
}
}
}

View File

@@ -0,0 +1,208 @@
<?php
/**
* Runtime Method callBlock,callParentBlock, callChildBlock, registerBlock
*
* @package Smarty
* @subpackage PluginsInternal
* @author Uwe Tews
*
**/
class Smarty_Internal_Runtime_Inheritance
{
/**
* State machine
* - 0 idle next extends will create a new inheritance tree
* - 1 processing child template
* - 2 wait for next inheritance template
* - 3 assume parent template, if child will loaded goto state 1
* a call to a sub template resets the state to 0
*
* @var int
*/
public $state = 0;
/**
* Array of block parameter of known {block} tags
*
* @var array
*/
public $blockParameter = array();
/**
* inheritance template nesting level
*
* @var int
*/
public $inheritanceLevel = 0;
/**
* inheritance template index
*
* @var int
*/
public $tplIndex = - 1;
/**
* Array of compiled template file path
* - key template index
* only used when caching is enabled
*
* @var []string
*/
public $compiledFilePath = array();
/**
* Current {block} nesting level
*
* @var int
*/
public $blockNesting = 0;
/**
* Initialize inheritance
*
* @param \Smarty_Internal_Template $tpl template object of caller
* @param bool $initChild if true init for child template
* @param array $blockNames outer level block name
*
*/
public function init(Smarty_Internal_Template $tpl, $initChild, $blockNames = array())
{
// if template was from an inner block or template is a parent template create new inheritance root
if ($initChild && ($this->blockNesting || $this->state == 3)) {
$tpl->_inheritance = new Smarty_Internal_Runtime_Inheritance();
$tpl->_inheritance->init($tpl, $initChild, $blockNames);
return;
}
// start of child sub template(s)
if ($initChild) {
$this->state = 1;
if (!$this->inheritanceLevel) {
//grab any output of child templates
ob_start();
}
$this->inheritanceLevel ++;
}
// in parent state {include} will not increment template index
if ($this->state != 3) {
$this->tplIndex ++;
}
// if state was waiting for parent change state to parent
if ($this->state == 2) {
$this->state = 3;
}
}
/**
* End of child template(s)
* - if outer level is reached flush output buffer and switch to wait for parent template state
*
* @param \Smarty_Internal_Template $tpl template object of caller
*/
public function endChild(Smarty_Internal_Template $tpl)
{
$this->inheritanceLevel --;
if (!$this->inheritanceLevel) {
ob_end_clean();
$this->state = 2;
}
}
/**
* Process inheritance {block} tag
*
* $type 0 = {block}:
* - search in inheritance template hierarchy for child blocks
* if found call it, otherwise call current block
* - ignored for outer level blocks in child templates
*
* $type 1 = {$smarty.block.child}:
* - search in inheritance template hierarchy for child blocks
* if found call it, otherwise ignore
*
* $type 2 = {$smarty.block.parent}:
* - get block id from parent stack and call parent block
*
* @param \Smarty_Internal_Template $tpl template object of caller
* @param int $type call type see above
* @param string $name block name
* @param array $block block parameter
* @param array $callStack call stack with block parameters
*
* @throws \SmartyException
*/
public function processBlock(Smarty_Internal_Template $tpl, $type = 0, $name, $block, $callStack = array())
{
if (!isset($this->blockParameter[$name])) {
$this->blockParameter[$name] = array();
}
if ($this->state == 1) {
$block[2] = count($this->blockParameter[$name]);
$block[3] = $this->tplIndex;
$this->blockParameter[$name][] = $block;
return;
}
if ($type == 3) {
if (!empty($callStack)) {
array_shift($callStack);
if (empty($callStack)) {
throw new SmartyException("inheritance: tag {\$smarty.block.parent} used in parent template block '{$name}'");
}
$block = array_shift($callStack);
} else {
return;
}
} else {
$blockParameter = &$this->blockParameter[$name];
if ($type == 0) {
$index = $block[2] = count($blockParameter);
$block[3] = $this->tplIndex;
$callStack = array(&$block);
} elseif ($type == 1) {
$block[3] = $callStack[0][3];
$index = 0;
for ($i = 0; $i < count($blockParameter); $i ++) {
if ($blockParameter[$i][3] <= $block[3]) {
$index = $blockParameter[$i][2];
}
}
$block[2] = $index;
$callStack = array(&$block);
} else {
$index = $callStack[0][2];
if ($index == 0) {
return;
}
$callStack = $block = array(1 => false);
}
$index --;
// find lowest level child block
while ($index >= 0 && ($type || !$block[1])) {
$block = &$blockParameter[$index];
array_unshift($callStack, $block);
if ($block[1]) {
break;
}
$index --;
}
if (isset($block['hide']) && $index <= 0) {
return;
}
}
$this->blockNesting ++;
if (isset($block['append'])) {
$this->processBlock($tpl, 3, $name, null, $callStack);
}
if (isset($block[6])) {
$block[6]($tpl, $callStack);
} else {
$block[0]($tpl, $callStack);
}
if (isset($block['prepend'])) {
$this->processBlock($tpl, 3, $name, null, $callStack);
}
$this->blockNesting --;
}
}

View File

@@ -17,6 +17,7 @@
* @property Smarty_Template_Source|Smarty_Template_Config $source
* @property Smarty_Template_Compiled $compiled
* @property Smarty_Template_Cached $cached
* @property Smarty_Internal_Runtime_Inheritance $_inheritance
* @method bool mustCompile()
*/
class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
@@ -314,10 +315,10 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
}
$tpl->tpl_function = $this->tpl_function;
// copy inheritance object?
if (isset($this->_Block)) {
$tpl->_Block = $this->_Block;
if (isset($this->_inheritance)) {
$tpl->_inheritance = $this->_inheritance;
} else {
unset($tpl->_Block);
unset($tpl->_inheritance);
}
} else {
$tpl = clone $this;
@@ -692,8 +693,8 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
{
// object properties of runtime template extensions will start with '_'
if ($property_name[0] == '_') {
$property_name[1] = chr(ord($property_name[1]) & 0xDF);
$class = 'Smarty_Internal_Runtime' . $property_name;
$class[24] = chr(ord($class[24]) & 0xDF);
if (class_exists($class)) {
return $this->$property_name = new $class();
}

View File

@@ -14,6 +14,11 @@
*
* @package Smarty
* @subpackage Compiler
*
* @property Smarty_Internal_SmartyTemplateCompiler $prefixCompiledCode = ''
* @property Smarty_Internal_SmartyTemplateCompiler $postfixCompiledCode = ''
* @method Smarty_Internal_SmartyTemplateCompiler registerPostCompileCallback($callback, $parameter = array(), $key = null, $replace = false)
* @method Smarty_Internal_SmartyTemplateCompiler unregisterPostCompileCallback($key)
*/
abstract class Smarty_Internal_TemplateCompilerBase
{
@@ -88,55 +93,6 @@ abstract class Smarty_Internal_TemplateCompilerBase
*/
public $templateProperties = array();
/**
* sources which must be compiled
*
* @var array
*/
public $sources = array();
/**
* flag when compiling inheritance template
*
* @var bool
*/
public $inheritance = false;
/**
* flag when compiling inheritance child template
*
* @var bool
*/
public $inheritanceChild = false;
/**
* Force all subtemplate calls to be inheritance childs
*
* @var bool
*/
public $inheritanceForceChild = false;
/**
* Flag if compiled parent template is child of other parent
*
* @var bool
*/
public $inheritanceParentIsChild = false;
/**
* uid of templates called by {extends} for recursion check
*
* @var array
*/
public $extends_uid = array();
/**
* Template name of {extends} tag
*
* @var null|string
*/
public $extendsFileName = null;
/**
* source line offset for error messages
*
@@ -305,20 +261,6 @@ abstract class Smarty_Internal_TemplateCompilerBase
*/
public $loopNesting = 0;
/**
* nesting level of block tags
*
* @var int
*/
public $blockTagNestingLevel = 0;
/**
* Flag if {$smarty.block.child} was called at $blockTagNestingLevel
*
* @var array
*/
public $callChildBlock = array();
/**
* Strip preg pattern
*
@@ -333,6 +275,13 @@ abstract class Smarty_Internal_TemplateCompilerBase
*/
public $plugin_search_order = array('function', 'block', 'compiler', 'class');
/**
* General storage area for tag compiler plugins
*
* @var array
*/
public $_cache = array();
/**
* method to compile a Smarty template
*
@@ -345,9 +294,12 @@ abstract class Smarty_Internal_TemplateCompilerBase
/**
* Initialize compiler
*
* @param Smarty $smarty global instance
*/
public function __construct()
public function __construct(Smarty $smarty)
{
$this->smarty = $smarty;
$this->nocache_hash = str_replace(array('.', ','), '_', uniqid(rand(), true));
}
@@ -424,13 +376,13 @@ abstract class Smarty_Internal_TemplateCompilerBase
// get template source
if (!empty($this->template->source->components)) {
// we have array of inheritance templates by extends: resource
$this->sources = array_reverse($this->template->source->components);
$_content = '';
// generate corresponding source code sequence
$_content =
Smarty_Internal_Compile_Extends::extendsSourceArrayCode($this->template->source->components);
} else {
// get template source
$_content = $this->template->source->getContent();
}
$_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
}
catch (Exception $e) {
@@ -494,64 +446,6 @@ abstract class Smarty_Internal_TemplateCompilerBase
}
}
/**
* Add code to call parent template on inheritance child templates
*
* @param $parser
*
* @throws \SmartyException
*/
public function processInheritance($parser)
{
if (isset($this->extendsFileName)) {
// child did use {extends}
$name = $this->extendsFileName;
$this->extendsFileName = null;
if (!$this->inheritanceForceChild && !$this->inheritance) {
// drop any output of child templates
array_unshift($parser->current_buffer->subtrees, new Smarty_Internal_ParseTree_Tag($parser,
"<?php ob_start();\n\$_smarty_tpl->_Block = new Smarty_Internal_Runtime_Block();?>\n"));
$this->inheritance = true;
}
$include = new Smarty_Internal_ParseTree_Tag($parser, $this->compileTag('include', array($name,
array('scope' => 'parent'),
array('inline' => true))));
if (!$this->inheritanceForceChild && !$this->inheritanceParentIsChild) {
$parser->current_buffer->append_subtree($parser, new Smarty_Internal_ParseTree_Tag($parser,
"<?php ob_end_clean();?>\n"));
$this->inheritance = false;
}
$this->inheritanceParentIsChild = false;
$parser->current_buffer->append_subtree($parser, $include);
return;
}
// template list of extends: resource ?
if (!empty($this->sources)) {
if (!$this->inheritanceForceChild) {
// drop any output of child templates
$parser->current_buffer->append_subtree($parser, new Smarty_Internal_ParseTree_Tag($parser,
"<?php ob_start();\n\$_smarty_tpl->_Block = new Smarty_Internal_Runtime_Block();?>\n"));
}
while (!empty($this->sources)) {
$source = array_shift($this->sources);
if (!$this->inheritanceForceChild && empty($this->sources)) {
// drop any output of child templates
$parser->current_buffer->append_subtree($parser, new Smarty_Internal_ParseTree_Tag($parser,
"<?php ob_end_clean();?>\n"));
}
$forceChild = $this->inheritanceForceChild;
$this->inheritanceForceChild = $this->inheritanceForceChild || !empty($this->sources);
$parser->current_buffer->append_subtree($parser, new Smarty_Internal_ParseTree_Tag($parser,
$this->compileTag('include',
array("'{$source->resource}'",
array('scope' => 'parent'),
array('inline' => true)))));
// restore value
$this->inheritanceForceChild = $forceChild;
}
}
}
/**
* Compile Tag
* This is a call back from the lexer/parser