diff --git a/change_log.txt b/change_log.txt index 87406797..3b1bdd07 100644 --- a/change_log.txt +++ b/change_log.txt @@ -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 diff --git a/libs/sysplugins/smarty_internal_compile_block.php b/libs/sysplugins/smarty_internal_compile_block.php index 6a3e3a30..3a19f1bd 100644 --- a/libs/sysplugins/smarty_internal_compile_block.php +++ b/libs/sysplugins/smarty_internal_compile_block.php @@ -13,7 +13,7 @@ * * @author Uwe Tews */ -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 = "_Block->callChildBlock(\$_smarty_tpl, \$block);?>"; - return $_output; + $compiler->_cache['blockParams'][$compiler->_cache['blockNesting']][1] = true; + $output = "_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 = "_Block->callParentBlock(\$_smarty_tpl, \$block);?>"; - return $_output; + $output = "_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 = "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 = "template->compiled->nocache_hash}%%*/\n"; - $output .= "\n}\n"; + $output = "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 = "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 = "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 = "_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 = - "_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; diff --git a/libs/sysplugins/smarty_internal_compile_extends.php b/libs/sysplugins/smarty_internal_compile_extends.php index 0b8a1836..d1b26a86 100644 --- a/libs/sysplugins/smarty_internal_compile_extends.php +++ b/libs/sysplugins/smarty_internal_compile_extends.php @@ -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, + "_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}'; + } } diff --git a/libs/sysplugins/smarty_internal_compile_include.php b/libs/sysplugins/smarty_internal_compile_include.php index a78673a9..64aed4ce 100644 --- a/libs/sysplugins/smarty_internal_compile_include.php +++ b/libs/sysplugins/smarty_internal_compile_include.php @@ -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 .= "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; diff --git a/libs/sysplugins/smarty_internal_compile_shared_inheritance.php b/libs/sysplugins/smarty_internal_compile_shared_inheritance.php new file mode 100644 index 00000000..445ad7ae --- /dev/null +++ b/libs/sysplugins/smarty_internal_compile_shared_inheritance.php @@ -0,0 +1,46 @@ +_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 .= "_inheritance->init(\$_smarty_tpl, " . + var_export($initChildSequence, true) . ");\n?>\n"; + } +} \ No newline at end of file diff --git a/libs/sysplugins/smarty_internal_runtime_block.php b/libs/sysplugins/smarty_internal_runtime_block.php deleted file mode 100644 index 36694588..00000000 --- a/libs/sysplugins/smarty_internal_runtime_block.php +++ /dev/null @@ -1,98 +0,0 @@ -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); - } - } -} \ No newline at end of file diff --git a/libs/sysplugins/smarty_internal_runtime_inheritance.php b/libs/sysplugins/smarty_internal_runtime_inheritance.php new file mode 100644 index 00000000..54a55565 --- /dev/null +++ b/libs/sysplugins/smarty_internal_runtime_inheritance.php @@ -0,0 +1,208 @@ +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 --; + } +} diff --git a/libs/sysplugins/smarty_internal_template.php b/libs/sysplugins/smarty_internal_template.php index 88e711a9..16fca678 100644 --- a/libs/sysplugins/smarty_internal_template.php +++ b/libs/sysplugins/smarty_internal_template.php @@ -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(); } diff --git a/libs/sysplugins/smarty_internal_templatecompilerbase.php b/libs/sysplugins/smarty_internal_templatecompilerbase.php index 0fbf8851..9125b43f 100644 --- a/libs/sysplugins/smarty_internal_templatecompilerbase.php +++ b/libs/sysplugins/smarty_internal_templatecompilerbase.php @@ -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, - "_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, - "\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, - "_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, - "\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