diff --git a/change_log.txt b/change_log.txt index f4bc48c0..bbc45eae 100644 --- a/change_log.txt +++ b/change_log.txt @@ -1,4 +1,7 @@  ===== 3.1.30-dev ===== (xx.xx.xx) + 25.12.2015 + - compile {block} tag code and its processing into classes + 24.12.2015 - new feature Compiler does now observe the template_dir setting and will create separate compiled files if required - bugfix post filter did fail on template inheritance https://github.com/smarty-php/smarty/issues/144 diff --git a/libs/Smarty.class.php b/libs/Smarty.class.php index 602ff5a4..ec88a6e0 100644 --- a/libs/Smarty.class.php +++ b/libs/Smarty.class.php @@ -118,7 +118,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * smarty version */ - const SMARTY_VERSION = '3.1.30-dev/8'; + const SMARTY_VERSION = '3.1.30-dev/9'; /** * define variable scopes diff --git a/libs/sysplugins/smarty_internal_block.php b/libs/sysplugins/smarty_internal_block.php new file mode 100644 index 00000000..375ce34a --- /dev/null +++ b/libs/sysplugins/smarty_internal_block.php @@ -0,0 +1,155 @@ +tplIndex = $_smarty_tpl->ext->_inheritance->tplIndex; + if (isset($_smarty_tpl->ext->_inheritance->blockParameter[ $this->name ])) { + $this->child = $_smarty_tpl->ext->_inheritance->blockParameter[ $this->name ]; + } + if ($_smarty_tpl->ext->_inheritance->state == 1) { + $_smarty_tpl->ext->_inheritance->blockParameter[ $this->name ] = $this; + return; + } + $this->process($_smarty_tpl); + } + + /** + * Goto child block or render this + * + * @param \Smarty_Internal_Template $_smarty_tpl + * @param \Smarty_Internal_Block|null $parent + */ + public function process(Smarty_Internal_Template $_smarty_tpl, Smarty_Internal_Block $parent = null) { + if ($this->hide && !isset($this->child)) { + return; + } + if (isset($this->child) && $this->child->hide && !isset($this->child->child)) { + $this->child = null; + } + $this->parent = $parent; + if ($this->append && !$this->prepend && isset($parent)) { + $this->callParent($_smarty_tpl); + } + if ($this->callsChild || !isset($this->child) || ($this->child->hide && !isset($this->child->child))) { + $this->callBlock($_smarty_tpl); + } else { + $this->child->process($_smarty_tpl, $this); + } + if ($this->prepend && isset($parent)) { + $this->callParent($_smarty_tpl); + if ($this->append) { + if ($this->callsChild || !isset($this->child) || ($this->child->hide && !isset($this->child->child))) { + $this->callBlock($_smarty_tpl); + } else { + $this->child->process($_smarty_tpl, $this); + } + } + } + $this->parent = null; + } + + /** + * Compiled block code overloaded by {block} class + * + * @param \Smarty_Internal_Template $_smarty_tpl + */ + public function callBlock(Smarty_Internal_Template $_smarty_tpl) { + } + + /** + * Render child on {$smarty.block.child} + * + * @param \Smarty_Internal_Template $_smarty_tpl + */ + public function callChild (Smarty_Internal_Template $_smarty_tpl) { + if (isset($this->child)) { + $this->child->process($_smarty_tpl, $this); + } + } + + /** + * Render parent on {$smarty.block.parent} or {block append/prepend} * + * + * @param \Smarty_Internal_Template $_smarty_tpl + * + * @throws \SmartyException + */ + public function callParent (Smarty_Internal_Template $_smarty_tpl) { + if (isset($this->parent)) { + $this->parent->callBlock($_smarty_tpl, $this->parent->parent); + } else { + throw new SmartyException("inheritance: illegal {\$smarty.block.parent} or {block append/prepend} used in parent template '{$_smarty_tpl->ext->_inheritance->compiledFilePath[$this->tplIndex]}' block '{$this->name}'"); + } + } +} \ No newline at end of file diff --git a/libs/sysplugins/smarty_internal_compile_block.php b/libs/sysplugins/smarty_internal_compile_block.php index ab1fc6e0..d2e63ee2 100644 --- a/libs/sysplugins/smarty_internal_compile_block.php +++ b/libs/sysplugins/smarty_internal_compile_block.php @@ -72,10 +72,10 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_Compile_Shared_Inher */ public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter) { - if (!isset($compiler->_cache['blockNesting'])) { - $compiler->_cache['blockNesting'] = 0; + if (!isset($compiler->_cache[ 'blockNesting' ])) { + $compiler->_cache[ 'blockNesting' ] = 0; } - if ($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'); @@ -84,10 +84,9 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_Compile_Shared_Inher } // check and get attributes $_attr = $this->getAttributes($compiler, $args); - $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; + $compiler->_cache[ 'blockNesting' ] ++; + $compiler->_cache[ 'blockName' ][ $compiler->_cache[ 'blockNesting' ] ] = $_attr[ 'name' ]; + $compiler->_cache[ 'blockParams' ][ $compiler->_cache[ 'blockNesting' ] ][ 'name' ] = "{$_attr['name']}"; $this->openTag($compiler, 'block', array($_attr, $compiler->nocache, $compiler->parser->current_buffer, $compiler->template->compiled->has_nocache_code, $compiler->template->caching)); @@ -97,7 +96,7 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_Compile_Shared_Inher } $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; // $compiler->suppressNocacheProcessing = true; - if ($_attr['nocache'] === true) { + if ($_attr[ 'nocache' ] === true) { //$compiler->trigger_template_error('nocache option not allowed', $compiler->parser->lex->taglineno); } $compiler->parser->current_buffer = new Smarty_Internal_ParseTree_Template(); @@ -115,14 +114,14 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_Compile_Shared_Inher */ static function compileChildBlock(Smarty_Internal_TemplateCompilerBase $compiler, $_name = null) { - if (!isset($compiler->_cache['blockNesting'])) { + 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->_cache['blockParams'][$compiler->_cache['blockNesting']][1] = true; - $output = "ext->_inheritance->processBlock(\$_smarty_tpl, 2, {$compiler->_cache['blockName'][$compiler->_cache['blockNesting']]}, null, \$_blockParentStack);\n?>\n"; + $compiler->_cache[ 'blockParams' ][ $compiler->_cache[ 'blockNesting' ] ][ 'callsChild' ] = 'true'; + $output = "callChild(\$_smarty_tpl);\n?>\n"; return $output; } @@ -136,13 +135,13 @@ class Smarty_Internal_Compile_Block extends Smarty_Internal_Compile_Shared_Inher */ static function compileParentBlock(Smarty_Internal_TemplateCompilerBase $compiler, $_name = null) { - if (!isset($compiler->_cache['blockNesting'])) { + 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 = "ext->_inheritance->processBlock(\$_smarty_tpl, 4, {$compiler->_cache['blockName'][$compiler->_cache['blockNesting']]}, null, \$_blockParentStack);\n?>\n"; + $output = "callParent(\$_smarty_tpl);\n?>\n"; return $output; } } @@ -166,62 +165,35 @@ class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_Compile_Shared_ { list($_attr, $_nocache, $_buffer, $_has_nocache_code, $_caching) = $this->closeTag($compiler, array('block')); // 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']); + $_block = $compiler->_cache[ 'blockParams' ][ $compiler->_cache[ 'blockNesting' ] ]; + unset($compiler->_cache[ 'blockParams' ][ $compiler->_cache[ 'blockNesting' ] ]); + $_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; + $_block[ $name ] = 'true'; } } - $_funcName = $_block[0]; + $_className = 'Block_' . preg_replace('#[^\w\|]+#S', '_', $_name) . '_' . + preg_replace('![^\w]+!', '_', uniqid(rand(), true)); // get compiled block code $_functionCode = $compiler->parser->current_buffer; // setup buffer for template function code $compiler->parser->current_buffer = new Smarty_Internal_ParseTree_Template(); - if ($compiler->template->compiled->has_nocache_code) { - // $compiler->parent_compiler->template->tpl_function[$_name]['call_name_caching'] = $_funcNameCaching; - $_block[6] = $_funcNameCaching = $_funcName . '_nocache'; - $output = "template->source->type}:{$compiler->template->source->name} */\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"; - 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_clean());\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->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, \$_blockParentStack) {\n"; + $output .= "/* {block '{$_name}'} {$compiler->template->source->type}:{$compiler->template->source->name} */\n"; + $output .= "class {$_className} extends Smarty_Internal_Block\n"; + $output .= "{\n"; + foreach ($_block as $property => $value) { + $output .= "public \${$property} = {$value};\n"; + } + $output .= "public function callBlock(Smarty_Internal_Template \$_smarty_tpl) {\n"; + //$output .= "/*/%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\n"; + if ($compiler->template->compiled->has_nocache_code) { + $output .= "\$_smarty_tpl->cached->hashes['{$compiler->template->compiled->nocache_hash}'] = true;\n"; + } if (isset($_assign)) { $output .= "ob_start();\n"; } @@ -234,59 +206,44 @@ class Smarty_Internal_Compile_Blockclose extends Smarty_Internal_Compile_Shared_ if (isset($_assign)) { $output .= "\$_smarty_tpl->tpl_vars[{$_assign}] = new Smarty_Variable(ob_get_clean());\n"; } + //$output .= "/*%%SmartyNocache:{$compiler->template->compiled->nocache_hash}%%*/\n"; + $output .= "}\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 .= $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(); // nocache plugins must be copied - if (!empty($compiler->template->compiled->required_plugins['nocache'])) { - foreach ($compiler->template->compiled->required_plugins['nocache'] as $plugin => $tmp) { + if (!empty($compiler->template->compiled->required_plugins[ 'nocache' ])) { + foreach ($compiler->template->compiled->required_plugins[ 'nocache' ] as $plugin => $tmp) { foreach ($tmp as $type => $data) { - $compiler->parent_compiler->template->compiled->required_plugins['compiled'][$plugin][$type] = + $compiler->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin ][ $type ] = $data; } } } - // 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; $output = "_cache['blockNesting'] == 1) { - $output .= "\$_smarty_tpl->ext->_inheritance->processBlock(\$_smarty_tpl, 0, {$compiler->_cache['blockName'][$compiler->_cache['blockNesting']]}, " . - var_export($_block, true) . ");\n"; + if ($compiler->_cache[ 'blockNesting' ] == 1) { + $output .= "new {$_className}(\$_smarty_tpl);\n"; } else { - $output .= "\$_smarty_tpl->ext->_inheritance->processBlock(\$_smarty_tpl, 0, {$compiler->_cache['blockName'][$compiler->_cache['blockNesting']]}, " . - var_export($_block, true) . ", \$_blockParentStack);\n"; - + $output .= "new {$_className}(\$_smarty_tpl);\n"; } $output .= "?>\n"; - $compiler->_cache['blockNesting'] --; - if ($compiler->_cache['blockNesting'] == 0) { - unset($compiler->_cache['blockNesting']); + $compiler->_cache[ 'blockNesting' ] --; + if ($compiler->_cache[ 'blockNesting' ] == 0) { + unset($compiler->_cache[ 'blockNesting' ]); } $compiler->has_code = true; $compiler->suppressNocacheProcessing = true; return $output; } - - /** - * @param $match - * - * @return mixed - */ - function removeNocache($match) - { - $code = - preg_replace("/((<\?php )?echo '\/\*%%SmartyNocache:{$this->compiler->template->compiled->nocache_hash}%%\*\/)|(\/\*\/%%SmartyNocache:{$this->compiler->template->compiled->nocache_hash}%%\*\/';(\?>\n)?)/", - '', $match[0]); - $code = str_replace(array('\\\'', '\\\\\''), array('\'', '\\\''), $code); - return $code; - } } diff --git a/libs/sysplugins/smarty_internal_runtime_inheritance.php b/libs/sysplugins/smarty_internal_runtime_inheritance.php index 08a56060..64ae5db7 100644 --- a/libs/sysplugins/smarty_internal_runtime_inheritance.php +++ b/libs/sysplugins/smarty_internal_runtime_inheritance.php @@ -88,6 +88,7 @@ class Smarty_Internal_Runtime_Inheritance // in parent state {include} will not increment template index if ($this->state != 3) { $this->tplIndex ++; + $this->compiledFilePath[ $this->tplIndex ] = $tpl->template_resource; } // if state was waiting for parent change state to parent if ($this->state == 2) { @@ -109,125 +110,4 @@ class Smarty_Internal_Runtime_Inheritance $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 = {block}: - * - nested {block} - * - search in inheritance template hierarchy for child blocks - * if found call it, otherwise call current block - * - * $type 2 = {$smarty.block.child}: - * - search in inheritance template hierarchy for child blocks - * if found call it, otherwise ignore - * - * $type 3 = {block append} {block prepend}: - * - call parent block - * - * $type 4 = {$smarty.block.parent}: - * - 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)) { - $block = array_shift($callStack); - } else { - return; - } - } elseif ($type == 4) { - 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 { - $index = 0; - $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 ]; - for ($i = 0; $i < count($blockParameter); $i ++) { - if ($blockParameter[ $i ][ 3 ] <= $block[ 3 ]) { - $index = $blockParameter[ $i ][ 2 ]; - } - } - $block[ 2 ] = $index; - $callStack = array(&$block); - } elseif ($type == 2) { - $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 ++; - // {block append} ? - if (isset($block[ 'append' ])) { - $appendStack = $callStack; - if ($type == 0) { - array_shift($appendStack); - } - $this->processBlock($tpl, 3, $name, null, $appendStack); - } - // call block of current stack level - if (isset($block[6])) { - $block[6]($tpl, $callStack); - } else { - $block[0]($tpl, $callStack); - } - // {block prepend} ? - if (isset($block[ 'prepend' ])) { - $prependStack = $callStack; - if ($type == 0) { - array_shift($prependStack); - } - $this->processBlock($tpl, 3, $name, null, $prependStack); - } - $this->blockNesting --; - } } diff --git a/libs/sysplugins/smarty_internal_testinstall.php b/libs/sysplugins/smarty_internal_testinstall.php index 45ca4b06..3f98cda8 100644 --- a/libs/sysplugins/smarty_internal_testinstall.php +++ b/libs/sysplugins/smarty_internal_testinstall.php @@ -364,6 +364,7 @@ class Smarty_Internal_TestInstall 'smarty_cacheresource_custom.php' => true, 'smarty_cacheresource_keyvaluestore.php' => true, 'smarty_data.php' => true, + 'smarty_internal_block.php' => true, 'smarty_internal_cacheresource_file.php' => true, 'smarty_internal_compilebase.php' => true, 'smarty_internal_compile_append.php' => true,