From 9ac1dd44e280f6be934433cc4d6ea30b69816c14 Mon Sep 17 00:00:00 2001 From: Uwe Tews Date: Wed, 1 Jul 2015 03:30:08 +0200 Subject: [PATCH] - update {foreach} compiler --- change_log.txt | 3 +- libs/Smarty.class.php | 2 +- .../smarty_internal_compile_foreach.php | 298 +++++++++++------- 3 files changed, 192 insertions(+), 111 deletions(-) diff --git a/change_log.txt b/change_log.txt index f530dc97..384f6215 100644 --- a/change_log.txt +++ b/change_log.txt @@ -1,7 +1,8 @@  ===== 3.1.28-dev===== (xx.xx.2015) 01.07.2015 - optimize compile check handling - + - update {foreach} compiler + 28.06.2015 - move $smarty->enableSecurity() into Smarty_Security class - optimize security isTrustedResourceDir() diff --git a/libs/Smarty.class.php b/libs/Smarty.class.php index debcc3a0..c2a71bd1 100644 --- a/libs/Smarty.class.php +++ b/libs/Smarty.class.php @@ -111,7 +111,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * smarty version */ - const SMARTY_VERSION = '3.1.28-dev/18'; + const SMARTY_VERSION = '3.1.28-dev/20'; /** * define variable scopes diff --git a/libs/sysplugins/smarty_internal_compile_foreach.php b/libs/sysplugins/smarty_internal_compile_foreach.php index 82ea4963..9f62bc84 100644 --- a/libs/sysplugins/smarty_internal_compile_foreach.php +++ b/libs/sysplugins/smarty_internal_compile_foreach.php @@ -23,6 +23,7 @@ class Smarty_Internal_Compile_Foreach extends Smarty_Internal_CompileBase * @see Smarty_Internal_CompileBase */ public $required_attributes = array('from', 'item'); + /** * Attribute definition: Overwrites base class. * @@ -30,6 +31,7 @@ class Smarty_Internal_Compile_Foreach extends Smarty_Internal_CompileBase * @see Smarty_Internal_CompileBase */ public $optional_attributes = array('name', 'key'); + /** * Attribute definition: Overwrites base class. * @@ -38,178 +40,244 @@ class Smarty_Internal_Compile_Foreach extends Smarty_Internal_CompileBase */ public $shorttag_order = array('from', 'item', 'key', 'name'); + /** + * Foreach counter + * + * @var int + */ + public $foreach_number = 0; + /** * Compiles code for the {foreach} tag * - * @param array $args array with attributes from parser - * @param object $compiler compiler object - * @param array $parameter array with compilation parameter + * @param array $args array with attributes from parser + * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object + * @param array $parameter array with compilation parameter * * @return string compiled code + * @throws \SmartyCompilerException */ - public function compile($args, $compiler, $parameter) + public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter) { // check and get attributes $_attr = $this->getAttributes($compiler, $args); - $from = $_attr['from']; - $item = $_attr['item']; - if (!strncmp("\$_smarty_tpl->tpl_vars[$item]", $from, strlen($item) + 24)) { - $compiler->trigger_template_error("item variable {$item} may not be the same variable as at 'from'", $compiler->lex->taglineno); + $item = $compiler->getId($_attr['item']); + if ($item === false) { + $item = $compiler->getVariableName($_attr['item']); } - + $attributes = array('item' => $item); if (isset($_attr['key'])) { - $key = $_attr['key']; - } else { - $key = null; + $key = $compiler->getId($_attr['key']); + if ($key === false) { + $key = $compiler->getVariableName($_attr['key']); + } + $attributes['key'] = $key; } - - $this->openTag($compiler, 'foreach', array('foreach', $compiler->nocache, $item, $key, true)); + if (isset($_attr['name'])) { + $attributes['name'] = $compiler->getId($_attr['name']); + } + foreach ($attributes as $a => $v) { + if ($v === false) { + $compiler->trigger_template_error("'{$a}' attribute/variable has illegal value", $compiler->lex->taglineno); + } + } + $fromName = $compiler->getVariableName($_attr['from']); + if ($fromName) { + foreach (array('item', 'key') as $a) { + if (isset($attributes[$a]) && $attributes[$a] == $fromName) { + $compiler->trigger_template_error("'{$a}' and 'from' may not have same variable name '{$fromName}'", $compiler->lex->taglineno); + } + } + } + $attributes['no'] = $this->foreach_number ++ . '_' . (isset($attributes['name']) ? $attributes['name'] : $attributes['item']); + $this->openTag($compiler, 'foreach', array('foreach', $compiler->nocache, $attributes, true)); // maybe nocache because of nocache variables $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; - if (isset($_attr['name'])) { - $name = trim($_attr['name'], '\'"'); - $has_name = true; - $SmartyVarName = "\$smarty.foreach.{$name}."; + // prepare preg + if ($has_name = isset($attributes['name'])) { + $smartyPreg = "|([\$]smarty[.]foreach[.]{$attributes['name']}[.]((first)|(last)|(index)|(iteration)|(show)|(total)))"; } else { - $has_name = false; + $smartyPreg = ''; } - $ItemVarName = '$' . trim($item, '\'"') . '@'; - // evaluates which Smarty variables and properties have to be computed - - if ($has_name) { - $useSmartyForeach = $usesSmartyFirst = strpos($compiler->lex->data, $SmartyVarName . 'first') !== false; - $useSmartyForeach = ($usesSmartyLast = strpos($compiler->lex->data, $SmartyVarName . 'last') !== false) || $useSmartyForeach; - $useSmartyForeach = ($usesSmartyIndex = strpos($compiler->lex->data, $SmartyVarName . 'index') !== false) || $useSmartyForeach; - $useSmartyForeach = ($usesSmartyIteration = (!$usesSmartyIndex && ($usesSmartyFirst || $usesSmartyLast)) || strpos($compiler->lex->data, $SmartyVarName . 'iteration') !== false) || $useSmartyForeach; - $useSmartyForeach = ($usesSmartyShow = strpos($compiler->lex->data, $SmartyVarName . 'show') !== false) || $useSmartyForeach; - $useSmartyForeach = ($usesSmartyTotal = $usesSmartyLast ||strpos($compiler->lex->data, $SmartyVarName . 'total') !== false) || $useSmartyForeach; - } else { - $usesSmartyFirst = false; - $usesSmartyLast = false; - $usesSmartyTotal = false; - $usesSmartyShow = false; - $useSmartyForeach = false; + $itemPreg = "([\$]{$attributes['item']}[@]((first)|(last)|(index)|(iteration)|(show)|(total)|(key)))"; + $preg = '~(' . $itemPreg . $smartyPreg . ')\W~i'; + $itemAttr = array(); + $smartyAttr = array(); + + // search template source + preg_match_all($preg, $compiler->lex->data, $match, PREG_SET_ORDER); + foreach ($match as $m) { + if (isset($m[3]) && !empty($m[3])) { + $itemAttr[strtolower($m[3])] = true; + } + if ($has_name && isset($m[12]) && !empty($m[12])) { + $smartyAttr[strtolower($m[12])] = true; + } } - $usesPropKey = strpos($compiler->lex->data, $ItemVarName . 'key') !== false; - $usesPropFirst = strpos($compiler->lex->data, $ItemVarName . 'first') !== false; - $usesPropLast = strpos($compiler->lex->data, $ItemVarName . 'last') !== false; - $usesPropIndex = strpos($compiler->lex->data, $ItemVarName . 'index') !== false; - $usesPropIteration = (!$usesPropIndex && ($usesPropFirst || $usesPropLast)) || strpos($compiler->lex->data, $ItemVarName . 'iteration') !== false; - $usesPropShow = strpos($compiler->lex->data, $ItemVarName . 'show') !== false; - $usesPropTotal = $usesPropLast || strpos($compiler->lex->data, $ItemVarName . 'total') !== false; + // search {block} sources + foreach ($compiler->template->block_data as $b) { + if (isset($b['source'])) { + preg_match_all($preg, $b['source'], $match, PREG_SET_ORDER); + foreach ($match as $m) { + if (isset($m[3]) && !empty($m[3])) { + $itemAttr[strtolower($m[3])] = true; + } + if ($has_name && isset($m[12]) && !empty($m[12])) { + $smartyAttr[strtolower($m[12])] = true; + } + } + } + } + if (class_exists('Smarty_Internal_Compile_Block', false)) { + foreach (Smarty_Internal_Compile_Block::$block_data as $b) { + if (isset($b['source'])) { + preg_match_all($preg, $b['source'], $match, PREG_SET_ORDER); + foreach ($match as $m) { + if (isset($m[3]) && !empty($m[3])) { + $itemAttr[strtolower($m[3])] = true; + } + if ($has_name && isset($m[12]) && !empty($m[12])) { + $smartyAttr[strtolower($m[12])] = true; + } + } + } + } + } + + if (!isset($itemAttr['index']) && (isset($itemAttr['first']) || isset($itemAttr['last']))) { + $itemAttr['iteration'] = true; + } + if (isset($itemAttr['last'])) { + $itemAttr['total'] = true; + } + if ($has_name) { + if (!isset($smartyAttr['index']) && (isset($smartyAttr['first']) || isset($smartyAttr['last']))) { + $smartyAttr['iteration'] = true; + } + if (isset($smartyAttr['last'])) { + $smartyAttr['total'] = true; + } + } $keyTerm = ''; - if ($usesPropKey) { - $keyTerm = "\$_smarty_tpl->tpl_vars[$item]->key => "; - } elseif ($key != null) { - $keyTerm = "\$_smarty_tpl->tpl_vars[$key]->value => "; + if (isset($itemAttr['key'])) { + $keyTerm = "\$_smarty_tpl->tpl_vars['{$item}']->key => "; + } elseif (isset($attributes['key'])) { + $keyTerm = "\$_smarty_tpl->tpl_vars['{$key}']->value => "; } // generate output code $output = "tpl_vars['{$attributes[$a]}']) ? \$_smarty_tpl->tpl_vars['{$attributes[$a]}'] : false;\n"; + } + } + if (isset($attributes['name'])) { + $output .= "\$foreach_{$attributes['no']}_sav['s_name'] = isset(\$_smarty_tpl->tpl_vars['__foreach_{$attributes['name']}']) ? \$_smarty_tpl->tpl_vars['__foreach_{$attributes['name']}'] : false;\n"; + } $output .= "\$_from = $from;\n"; $output .= "if (!is_array(\$_from) && !is_object(\$_from)) {\n"; $output .= "settype(\$_from, 'array');\n"; $output .= "}\n"; - $output .= "\$_smarty_tpl->tpl_vars[$item] = new Smarty_Variable;\n"; - $output .= "\$_smarty_tpl->tpl_vars[$item]->_loop = false;\n"; - if ($key != null) { - $output .= "\$_smarty_tpl->tpl_vars[$key] = new Smarty_Variable;\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$item}'] = new Smarty_Variable;\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->_loop = false;\n"; + if (isset($attributes['key'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$key}'] = new Smarty_Variable;\n"; } - if ($usesPropTotal) { - $output .= "\$_smarty_tpl->tpl_vars[$item]->total= \$_smarty_tpl->_count(\$_from);\n"; + if (isset($itemAttr['total'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->total= \$_smarty_tpl->_count(\$_from);\n"; } - if ($usesPropIteration) { - $output .= "\$_smarty_tpl->tpl_vars[$item]->iteration=0;\n"; + if (isset($itemAttr['iteration'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->iteration=0;\n"; } - if ($usesPropIndex) { - $output .= "\$_smarty_tpl->tpl_vars[$item]->index=-1;\n"; + if (isset($itemAttr['index'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->index=-1;\n"; } - if ($usesPropShow) { - if ($usesPropTotal) { - $output .= "\$_smarty_tpl->tpl_vars[$item]->show = (\$_smarty_tpl->tpl_vars[$item]->total > 0);\n"; + if (isset($itemAttr['show'])) { + if (isset($itemAttr['total'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->show = (\$_smarty_tpl->tpl_vars['{$item}']->total > 0);\n"; } else { - $output .= "\$_smarty_tpl->tpl_vars[$item]->show = (\$_smarty_tpl->_count(\$_from) > 0);\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->show = (\$_smarty_tpl->_count(\$_from) > 0);\n"; } } if ($has_name) { $prop = array(); - if ($usesSmartyTotal) { + if (isset($smartyAttr['total'])) { $prop['total'] = "'total' => "; - $prop['total'] .= $usesSmartyShow ? '$total = ' : ''; + $prop['total'] .= isset($smartyAttr['show']) ? '$total = ' : ''; $prop['total'] .= '$_smarty_tpl->_count($_from)'; } - if ($usesSmartyIteration) { + if (isset($smartyAttr['iteration'])) { $prop['iteration'] = "'iteration' => 0"; } - if ($usesSmartyIndex) { + if (isset($smartyAttr['index'])) { $prop['index'] = "'index' => -1"; } - if ($usesSmartyShow) { + if (isset($smartyAttr['show'])) { $prop['show'] = "'show' => "; - if ($usesSmartyTotal) { + if (isset($smartyAttr['total'])) { $prop['show'] .= "(\$total > 0)"; } else { $prop['show'] .= "(\$_smarty_tpl->_count(\$_from) > 0)"; } } - if ($useSmartyForeach) { + if (!empty($smartyAttr)) { $_vars = 'array(' . join(', ', $prop) . ')'; - $foreachVar = "'__foreach_{$name}'"; + $foreachVar = "'__foreach_{$attributes['name']}'"; $output .= "\$_smarty_tpl->tpl_vars[$foreachVar] = new Smarty_Variable({$_vars});\n"; } } - $output .= "foreach (\$_from as {$keyTerm}\$_smarty_tpl->tpl_vars[$item]->value) {\n"; - $output .= "\$_smarty_tpl->tpl_vars[$item]->_loop = true;\n"; - if ($key != null && $usesPropKey) { - $output .= "\$_smarty_tpl->tpl_vars[$key]->value = \$_smarty_tpl->tpl_vars[$item]->key;\n"; + $output .= "foreach (\$_from as {$keyTerm}\$_smarty_tpl->tpl_vars['{$item}']->value) {\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->_loop = true;\n"; + if (isset($attributes['key']) && isset($itemAttr['key'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$key}']->value = \$_smarty_tpl->tpl_vars['{$item}']->key;\n"; } - if ($usesPropIteration) { - $output .= "\$_smarty_tpl->tpl_vars[$item]->iteration++;\n"; + if (isset($itemAttr['iteration'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->iteration++;\n"; } - if ($usesPropIndex) { - $output .= "\$_smarty_tpl->tpl_vars[$item]->index++;\n"; + if (isset($itemAttr['index'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->index++;\n"; } - if ($usesPropFirst) { - if ($usesPropIndex) { - $output .= "\$_smarty_tpl->tpl_vars[$item]->first = \$_smarty_tpl->tpl_vars[$item]->index == 0;\n"; + if (isset($itemAttr['first'])) { + if (isset($itemAttr['index'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->first = \$_smarty_tpl->tpl_vars['{$item}']->index == 0;\n"; } else { - $output .= "\$_smarty_tpl->tpl_vars[$item]->first = \$_smarty_tpl->tpl_vars[$item]->iteration == 1;\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->first = \$_smarty_tpl->tpl_vars['{$item}']->iteration == 1;\n"; } } - if ($usesPropLast) { - if ($usesPropIndex) { - $output .= "\$_smarty_tpl->tpl_vars[$item]->last = \$_smarty_tpl->tpl_vars[$item]->index + 1 == \$_smarty_tpl->tpl_vars[$item]->total;\n"; + if (isset($itemAttr['last'])) { + if (isset($itemAttr['index'])) { + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->last = \$_smarty_tpl->tpl_vars['{$item}']->index + 1 == \$_smarty_tpl->tpl_vars['{$item}']->total;\n"; } else { - $output .= "\$_smarty_tpl->tpl_vars[$item]->last = \$_smarty_tpl->tpl_vars[$item]->iteration == \$_smarty_tpl->tpl_vars[$item]->total;\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$item}']->last = \$_smarty_tpl->tpl_vars['{$item}']->iteration == \$_smarty_tpl->tpl_vars['{$item}']->total;\n"; } } if ($has_name) { - if ($usesSmartyIteration) { + if (isset($smartyAttr['iteration'])) { $output .= "\$_smarty_tpl->tpl_vars[$foreachVar]->value['iteration']++;\n"; } - if ($usesSmartyIndex) { + if (isset($smartyAttr['index'])) { $output .= "\$_smarty_tpl->tpl_vars[$foreachVar]->value['index']++;\n"; } - if ($usesSmartyFirst) { - if ($usesSmartyIndex) { + if (isset($smartyAttr['first'])) { + if (isset($smartyAttr['index'])) { $output .= "\$_smarty_tpl->tpl_vars[$foreachVar]->value['first'] = \$_smarty_tpl->tpl_vars[$foreachVar]->value['index'] == 0;\n"; } else { $output .= "\$_smarty_tpl->tpl_vars[$foreachVar]->value['first'] = \$_smarty_tpl->tpl_vars[$foreachVar]->value['iteration'] == 1;\n"; } } - if ($usesSmartyLast) { - if ($usesSmartyIndex) { + if (isset($smartyAttr['last'])) { + if (isset($smartyAttr['index'])) { $output .= "\$_smarty_tpl->tpl_vars[$foreachVar]->value['last'] = \$_smarty_tpl->tpl_vars[$foreachVar]->value['index'] + 1 == \$_smarty_tpl->tpl_vars[$foreachVar]->value['total'];\n"; } else { $output .= "\$_smarty_tpl->tpl_vars[$foreachVar]->value['last'] = \$_smarty_tpl->tpl_vars[$foreachVar]->value['iteration'] == \$_smarty_tpl->tpl_vars[$foreachVar]->value['total'];\n"; } } } - $itemName = trim($item,"'\""); - $output .= "\$foreach_{$itemName}_Sav = \$_smarty_tpl->tpl_vars[$item];\n"; + $output .= "\$foreach_{$attributes['no']}_sav['item'] = \$_smarty_tpl->tpl_vars['{$item}'];\n"; $output .= "?>"; return $output; @@ -227,24 +295,23 @@ class Smarty_Internal_Compile_Foreachelse extends Smarty_Internal_CompileBase /** * Compiles code for the {foreachelse} tag * - * @param array $args array with attributes from parser - * @param object $compiler compiler object - * @param array $parameter array with compilation parameter + * @param array $args array with attributes from parser + * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object + * @param array $parameter array with compilation parameter * * @return string compiled code */ - public function compile($args, $compiler, $parameter) + public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter) { // check and get attributes $_attr = $this->getAttributes($compiler, $args); - list($openTag, $nocache, $item, $key, $foo) = $this->closeTag($compiler, array('foreach')); - $this->openTag($compiler, 'foreachelse', array('foreachelse', $nocache, $item, $key, false)); - $itemName = trim($item,"'\""); + list($openTag, $nocache, $attributes, $foo) = $this->closeTag($compiler, array('foreach')); + $this->openTag($compiler, 'foreachelse', array('foreachelse', $nocache, $attributes, false)); $output = "tpl_vars[$item] = \$foreach_{$itemName}_Sav;\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$attributes['item']}'] = \$foreach_{$attributes['no']}_sav['item'];\n"; $output .= "}\n"; - $output .= "if (!\$_smarty_tpl->tpl_vars[$item]->_loop) {\n?>"; + $output .= "if (!\$_smarty_tpl->tpl_vars['{$attributes['item']}']->_loop) {\n?>"; return $output; } } @@ -260,13 +327,13 @@ class Smarty_Internal_Compile_Foreachclose extends Smarty_Internal_CompileBase /** * Compiles code for the {/foreach} tag * - * @param array $args array with attributes from parser - * @param object $compiler compiler object - * @param array $parameter array with compilation parameter + * @param array $args array with attributes from parser + * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object + * @param array $parameter array with compilation parameter * * @return string compiled code */ - public function compile($args, $compiler, $parameter) + public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter) { // check and get attributes $_attr = $this->getAttributes($compiler, $args); @@ -275,13 +342,26 @@ class Smarty_Internal_Compile_Foreachclose extends Smarty_Internal_CompileBase $compiler->tag_nocache = true; } - list($openTag, $compiler->nocache, $item, $key, $restore) = $this->closeTag($compiler, array('foreach', 'foreachelse')); - $itemName = trim($item,"'\""); + list($openTag, $compiler->nocache, $attributes, $restore) = $this->closeTag($compiler, array('foreach', + 'foreachelse')); $output = "tpl_vars[$item] = \$foreach_{$itemName}_Sav;\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$attributes['item']}'] = \$foreach_{$attributes['no']}_sav['item'];\n"; } - $output .= "}\n?>"; + $output .= "}\n"; + foreach (array('item', 'key') as $a) { + if (isset($attributes[$a])) { + $output .= "if (\$foreach_{$attributes['no']}_sav['s_{$a}']) {\n"; + $output .= "\$_smarty_tpl->tpl_vars['{$attributes[$a]}'] = \$foreach_{$attributes['no']}_sav['s_{$a}'];\n"; + $output .= "}\n"; + } + } + if (isset($attributes['name'])) { + $output .= "if (\$foreach_{$attributes['no']}_sav['s_name']) {\n"; + $output .= "\$_smarty_tpl->tpl_vars['__foreach_{$attributes['name']}'] = \$foreach_{$attributes['no']}_sav['s_name'];\n"; + $output .= "}\n"; + } + $output .= "?>"; return $output; }