fix misc syntax issues, add ability to pass modifiers to functions

This commit is contained in:
mohrt
2002-12-19 17:02:11 +00:00
parent d768237b44
commit d587059b28
3 changed files with 292 additions and 235 deletions

3
NEWS
View File

@@ -1,4 +1,5 @@
- can use constants now, with security restrictions (Monte) - support return value for custom functions (Monte)
- can access constants via $smarty.const.FOO (Monte)
- cleaned up regex code in compiler (Monte) - cleaned up regex code in compiler (Monte)
- can now pass modifiers to static content, - can now pass modifiers to static content,
among other minor syntax features (monte) among other minor syntax features (monte)

View File

@@ -50,17 +50,18 @@ class Smarty_Compiler extends Smarty {
var $_capture_stack = array(); // keeps track of nested capture buffers var $_capture_stack = array(); // keeps track of nested capture buffers
var $_plugin_info = array(); // keeps track of plugins to load var $_plugin_info = array(); // keeps track of plugins to load
var $_init_smarty_vars = false; var $_init_smarty_vars = false;
var $_permitted_tokens = array('true','false','yes','no','on','off');
var $_db_qstr_regexp = null; // regexps are setup in the constructor var $_db_qstr_regexp = null; // regexps are setup in the constructor
var $_si_qstr_regexp = null; var $_si_qstr_regexp = null;
var $_qstr_regexp = null; var $_qstr_regexp = null;
var $_func_regexp = null; var $_func_regexp = null;
var $_const_regexp = null;
var $_dvar_regexp = null; var $_dvar_regexp = null;
var $_cvar_regexp = null; var $_cvar_regexp = null;
var $_svar_regexp = null; var $_svar_regexp = null;
var $_avar_regexp = null; var $_avar_regexp = null;
var $_mod_regexp = null; var $_mod_regexp = null;
var $_var_regexp = null; var $_var_regexp = null;
var $_param_regexp = null;
var $_obj_regexp = null; var $_obj_regexp = null;
@@ -84,16 +85,6 @@ class Smarty_Compiler extends Smarty {
// matches single or double quoted strings // matches single or double quoted strings
$this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')'; $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
// matches valid function name:
// foo123
// /foo123
$this->_func_regexp = '\/?\w+';
// matches valid constant names:
// _MY_CONST
// aBc_123
$this->_const_regexp = '\w+';
// matches dollar vars: // matches dollar vars:
// $foo // $foo
// $foo.bar // $foo.bar
@@ -102,8 +93,7 @@ class Smarty_Compiler extends Smarty {
// $foo[$bar] // $foo[$bar]
// $foo[5][blah] // $foo[5][blah]
// $foo[5].bar[$foobar][4] // $foo[5].bar[$foobar][4]
$this->_dvar_regexp = '\$\w+(?:\[\$?[\w\.]+\])*(?:\.\$?\w+(?:\[\$?[\w\.]+\])*)*';
$this->_dvar_regexp = '\$\w+(?:\[\$?\w+\])*(?:\.\w+(?:\[\$?\w+\])*)*';
// matches config vars: // matches config vars:
// #foo# // #foo#
@@ -124,17 +114,40 @@ class Smarty_Compiler extends Smarty {
// |foo:$bar // |foo:$bar
// |foo:"bar":$foobar // |foo:"bar":$foobar
// |foo|bar // |foo|bar
// |foo|bar:$foo|bar:"foo":$bar|foo|bar|foobar // |foo
$this->_mod_regexp = '(?>\|@?\w+(?::(?>' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'|\w+))*)*'; $this->_mod_regexp = '(?>\|@?\w+(?::(?>\w+|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)*';
// matches valid variable/parameter syntax: // matches valid variable syntax:
// $foo
// $foo
// #foo#
// #foo#
// "text"
// "text"
$this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
// matches valid function name:
// foo123
// /foo123
$this->_func_regexp = '\/?\w+';
// matches valid parameter syntax (except an object):
// true
// $foo // $foo
// $foo|bar // $foo|bar
// #foo# // #foo#
// #foo#|bar // #foo#|bar
// "text" // "text"
// "text"|bar // "text"|bar
$this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')'; $this->_param_regexp = '(?:\w+|' . $this->_var_regexp . $this->_mod_regexp . ')';
// matches valid object parameters:
//
// "text"
// $foo, $bar, "text"
// $foo|bar, "foo"|bar
$this->_obj_param_regexp = '(?:' . $this->_param_regexp
. '(?:\s*,\s*(?:' . $this->_param_regexp .'))*)';
// matches valid object call: // matches valid object call:
// $foo->bar // $foo->bar
@@ -142,8 +155,9 @@ class Smarty_Compiler extends Smarty {
// $foo->bar("text") // $foo->bar("text")
// $foo->bar($foo, $bar, "text") // $foo->bar($foo, $bar, "text")
// $foo->bar($foo|bar, "foo"|bar) // $foo->bar($foo|bar, "foo"|bar)
$this->_obj_regexp = '(?:\$\w+(?:\.\w+)*\->\w+(?:\((?:(?:\w+|' . $this->_var_regexp . $this->_mod_regexp // $foo->bar->foo()
. ')(?:\s*,\s*(?:\w+|' . $this->_var_regexp . $this->_mod_regexp .'))*)*\))?)'; // $foo->bar->foo->bar()
$this->_obj_regexp = '(?:\$\w+(?:\.\w+)*(?:\->\w+)+(?:\(' . $this->_obj_param_regexp . '\))?)';
} }
/*======================================================================*\ /*======================================================================*\
@@ -312,27 +326,29 @@ class Smarty_Compiler extends Smarty {
\*======================================================================*/ \*======================================================================*/
function _compile_tag($template_tag) function _compile_tag($template_tag)
{ {
/* Matched comment. */ /* Matched comment. */
if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*') if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
return ''; return '';
/* Split tag into two parts: command and the arguments. */ /* Split tag into two three parts: command, command modifiers and the arguments. */
if(! preg_match('/^((?:' . $this->_obj_regexp . '|' . $this->_var_regexp . '|' . $this->_func_regexp . ')' . $this->_mod_regexp . ') if(! preg_match('/^(?:(' . $this->_obj_regexp . '|' . $this->_var_regexp . '|' . $this->_func_regexp . ')(' . $this->_mod_regexp . '))
(?:\s+(.*))?$ (?:\s+(.*))?$
/xs', $template_tag, $match)) { /xs', $template_tag, $match)) {
$this->_syntax_error("unrecognized tag: $template_tag"); $this->_syntax_error("unrecognized tag: $template_tag");
} }
$tag_command = $match[1]; $tag_command = $match[1];
$tag_args = isset($match[2]) ? $match[2] : ''; $tag_modifier = isset($match[2]) ? $match[2] : '';
$tag_args = isset($match[3]) ? $match[3] : '';
/* If the tag name is not a function, we process it. */ /* If the tag name is not a function, we process it. */
if (!preg_match('!^' . $this->_func_regexp . '$!', $tag_command)) { if (!preg_match('!^' . $this->_func_regexp . '$!', $tag_command)) {
settype($tag_command, 'array'); $array_tag_command = array($tag_command . $tag_modifier);
$this->_parse_vars_props($tag_command); $this->_parse_vars_props($array_tag_command);
return "<?php echo $tag_command[0]; ?>\n"; return "<?php echo $array_tag_command[0]; ?>\n";
} }
switch ($tag_command) { switch ($tag_command) {
case 'include': case 'include':
@@ -421,10 +437,10 @@ class Smarty_Compiler extends Smarty {
default: default:
if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) { if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
return $output; return $output;
} else if ($this->_compile_block_tag($tag_command, $tag_args, $output)) { } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
return $output; return $output;
} else { } else {
return $this->_compile_custom_tag($tag_command, $tag_args); return $this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier);
} }
} }
} }
@@ -492,7 +508,7 @@ class Smarty_Compiler extends Smarty {
Function: _compile_block_tag Function: _compile_block_tag
Purpose: compile block function tag Purpose: compile block function tag
\*======================================================================*/ \*======================================================================*/
function _compile_block_tag($tag_command, $tag_args, &$output) function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
{ {
if ($tag_command{0} == '/') { if ($tag_command{0} == '/') {
$start_tag = false; $start_tag = false;
@@ -558,7 +574,13 @@ class Smarty_Compiler extends Smarty {
$output = "<?php \$this->_tag_stack[] = array('$tag_command', array(".implode(',', (array)$arg_list).")); \$this->_plugins['block']['$tag_command'][0](array(".implode(',', (array)$arg_list)."), null, \$this); ob_start(); ?>"; $output = "<?php \$this->_tag_stack[] = array('$tag_command', array(".implode(',', (array)$arg_list).")); \$this->_plugins['block']['$tag_command'][0](array(".implode(',', (array)$arg_list)."), null, \$this); ob_start(); ?>";
} else { } else {
$output = "<?php \$this->_block_content = ob_get_contents(); ob_end_clean(); \$this->_plugins['block']['$tag_command'][0](\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$this->_block_content, \$this); array_pop(\$this->_tag_stack); ?>"; $output = "<?php \$this->_block_content = ob_get_contents(); ob_end_clean(); ";
$out_tag_text = "\$this->_plugins['block']['$tag_command'][0](\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$this->_block_content, \$this)";
if($tag_modifier != '') {
$this->_parse_modifiers($out_tag_text, $tag_modifier);
}
$output .= 'echo ' . $out_tag_text . ';';
$output .= " array_pop(\$this->_tag_stack); ?>";
} }
return true; return true;
@@ -569,7 +591,7 @@ class Smarty_Compiler extends Smarty {
Function: _compile_custom_tag Function: _compile_custom_tag
Purpose: compile custom function tag Purpose: compile custom function tag
\*======================================================================*/ \*======================================================================*/
function _compile_custom_tag($tag_command, $tag_args) function _compile_custom_tag($tag_command, $tag_args, $tag_modifier)
{ {
$this->_add_plugin('function', $tag_command); $this->_add_plugin('function', $tag_command);
@@ -581,7 +603,13 @@ class Smarty_Compiler extends Smarty {
$arg_list[] = "'$arg_name' => $arg_value"; $arg_list[] = "'$arg_name' => $arg_value";
} }
return "<?php \$this->_plugins['function']['$tag_command'][0](array(".implode(',', (array)$arg_list)."), \$this); ?>"; $return = "\$this->_plugins['function']['$tag_command'][0](array(".implode(',', (array)$arg_list)."), \$this)";
if($tag_modifier != '') {
$this->_parse_modifiers($return, $tag_modifier);
}
return '<?php echo ' . $return . " ; ?>\n";
} }
@@ -727,7 +755,7 @@ class Smarty_Compiler extends Smarty {
$attrs = $this->_parse_attrs($tag_args); $attrs = $this->_parse_attrs($tag_args);
$arg_list = array(); $arg_list = array();
$output = "<?php "; $output = '<?php ';
$section_name = $attrs['name']; $section_name = $attrs['name'];
if (empty($section_name)) { if (empty($section_name)) {
$this->_syntax_error("missing section name"); $this->_syntax_error("missing section name");
@@ -921,8 +949,6 @@ class Smarty_Compiler extends Smarty {
)/x', $tag_args, $match); )/x', $tag_args, $match);
$tokens = $match[0]; $tokens = $match[0];
$this->_parse_vars_props($tokens);
$is_arg_stack = array(); $is_arg_stack = array();
for ($i = 0, $for_max = count($tokens); $i < $for_max; $i++) { for ($i = 0, $for_max = count($tokens); $i < $for_max; $i++) {
@@ -1003,14 +1029,32 @@ class Smarty_Compiler extends Smarty {
$i = $is_arg_start; $i = $is_arg_start;
break; break;
case ')':
break;
default: default:
if($this->security &&
$i+1 < count($tokens) && if(preg_match('!^[a-zA-Z_]\w+$!', $token)) {
$tokens[$i+1] == '(' &&
preg_match('!^[a-zA-Z_]\w+$!', $tokens[$i]) && if($this->security &&
!in_array($tokens[$i], $this->security_settings['IF_FUNCS'])) { $i+1 < count($tokens) &&
$this->_syntax_error("(secure mode) '".$tokens[$i]."' not allowed in if statement"); $tokens[$i+1] == '(' &&
} !in_array($token, $this->security_settings['IF_FUNCS'])) {
$this->_syntax_error("(secure mode) '".$tokens[$i]."' not allowed in if statement");
} else {
if(!function_exists($token)) {
$this->_syntax_error("reference to non-existant function: $token");
}
}
} else {
$tmptoken = array($token);
$this->_parse_vars_props($tmptoken);
$token = $tmptoken[0];
unset($tmptoken);
}
break; break;
} }
} }
@@ -1087,13 +1131,13 @@ class Smarty_Compiler extends Smarty {
\*======================================================================*/ \*======================================================================*/
function _parse_attrs($tag_args, $quote = true) function _parse_attrs($tag_args, $quote = true)
{ {
/* Tokenize tag attributes. */ /* Tokenize tag attributes. */
preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+) preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
)+ | )+ |
[=] [=]
/x', $tag_args, $match); /x', $tag_args, $match);
$tokens = $match[0]; $tokens = $match[0];
$var_delims = array('$', '#', '%');
$attrs = array(); $attrs = array();
/* Parse state: /* Parse state:
@@ -1132,13 +1176,11 @@ class Smarty_Compiler extends Smarty {
$token = true; $token = true;
else if (preg_match('!^(off|no|false)$!', $token)) else if (preg_match('!^(off|no|false)$!', $token))
$token = false; $token = false;
/* If the token is not variable (doesn't start with /* If the token is just a string,
'$', '#', or '%') and not enclosed in single or we double-quote it. */
double quotes we single-quote it. */ else if (preg_match('!^\w+$!', $token)) {
else if ($quote && !in_array($token{0}, $var_delims) &&
!(($token{0} == '"' || $token{0} == "'") &&
$token{strlen($token)-1} == $token{0}))
$token = '"'.$token.'"'; $token = '"'.$token.'"';
}
$attrs[$attr_name] = $token; $attrs[$attr_name] = $token;
$state = 0; $state = 0;
@@ -1161,63 +1203,40 @@ class Smarty_Compiler extends Smarty {
\*======================================================================*/ \*======================================================================*/
function _parse_vars_props(&$tokens) function _parse_vars_props(&$tokens)
{ {
foreach($tokens as $key => $val) {
$var_exprs = preg_grep('!^(' . $this->_dvar_regexp . '|' . $this->_obj_regexp . ')' . $this->_mod_regexp . '$!', $tokens); if(preg_match('!^(' . $this->_dvar_regexp . '|' . $this->_obj_regexp . ')' . $this->_mod_regexp . '$!', $val)) {
$conf_var_exprs = preg_grep('!^' . $this->_cvar_regexp . $this->_mod_regexp . '$!', $tokens); // $ variable
$sect_prop_exprs = preg_grep('!^' . $this->_svar_regexp . $this->_mod_regexp . '$!', $tokens); $tokens[$key] = $this->_parse_var($val);
$db_quoted_exprs = preg_grep('!^' . $this->_db_qstr_regexp . $this->_mod_regexp . '$!', $tokens); }
$si_quoted_exprs = preg_grep('!^' . $this->_si_qstr_regexp . $this->_mod_regexp . '$!', $tokens); elseif(preg_match('!^' . $this->_db_qstr_regexp . $this->_mod_regexp . '$!', $val)) {
$const_exprs = preg_grep('!^' . $this->_const_regexp . '$!', $tokens); // double quoted text
preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $val, $match);
if (count($const_exprs)) { $tokens[$key] = $this->_expand_quoted_text($match[1]);
foreach ($const_exprs as $constant) { if($match[2] != '') {
if($this->security && ! $this->security_settings['ALLOW_CONSTANTS']) { $this->_parse_modifiers($tokens[$key], $match[2]);
$this->_syntax_error("(secure) constants not allowed: $constant");
if(!defined($constant)) {
$this->_syntax_error("(secure) undefined constant referenced: $constant");
} }
} }
} elseif(preg_match('!^' . $this->_si_qstr_regexp . $this->_mod_regexp . '$!', $val)) {
} // single quoted text
preg_match('!^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $val, $match);
if($match[2] != '') {
if (count($db_quoted_exprs)) { $this->_parse_modifiers($match[1], $match[2]);
foreach ($db_quoted_exprs as $expr_index => $var_expr) { $tokens[$key] = $match[1];
preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $var_expr, $match); }
$tokens[$expr_index] = $this->_expand_quoted_text($match[1]);
if($match[2] != '') {
$this->_parse_modifiers($tokens[$expr_index], $match[2]);
} }
} elseif(preg_match('!^' . $this->_cvar_regexp . $this->_mod_regexp . '$!', $val)) {
} // config var
$tokens[$key] = $this->_parse_conf_var($val);
if (count($si_quoted_exprs)) {
foreach ($si_quoted_exprs as $expr_index => $var_expr) {
preg_match('!^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $var_expr, $match);
if($match[2] != '') {
$this->_parse_modifiers($match[1], $match[2]);
$tokens[$expr_index] = $match[1];
} }
} elseif(preg_match('!^' . $this->_svar_regexp . $this->_mod_regexp . '$!', $val)) {
// section var
$tokens[$key] = $this->_parse_section_prop($val);
}
elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
// not recognized value
$this->_syntax_error("unknown syntax: $val");
}
} }
if (count($var_exprs)) {
foreach ($var_exprs as $expr_index => $var_expr) {
$tokens[$expr_index] = $this->_parse_var($var_expr);
}
}
if (count($conf_var_exprs)) {
foreach ($conf_var_exprs as $expr_index => $var_expr) {
$tokens[$expr_index] = $this->_parse_conf_var($var_expr);
}
}
if (count($sect_prop_exprs)) {
foreach ($sect_prop_exprs as $expr_index => $section_prop_expr) {
$tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr);
}
}
} }
/*======================================================================*\ /*======================================================================*\
@@ -1257,7 +1276,7 @@ class Smarty_Compiler extends Smarty {
} }
// get [foo] and .foo and ->foo() pieces // get [foo] and .foo and ->foo() pieces
preg_match_all('!(^\w+)|(?:\[\$?\w+\])|\.\w+|->\w+(?:\([^\)]*\))?!', $var_ref, $match); preg_match_all('!(^\w+)|(?:\[\$?[\w\.]+\])|\.\$?\w+|(?:->\w+)+(?:\([^\)]*\))?!', $var_ref, $match);
$indexes = $match[0]; $indexes = $match[0];
$var_name = array_shift($indexes); $var_name = array_shift($indexes);
@@ -1280,6 +1299,7 @@ class Smarty_Compiler extends Smarty {
} }
foreach ($indexes as $index) { foreach ($indexes as $index) {
if ($index{0} == '[') { if ($index{0} == '[') {
$index = substr($index, 1, -1); $index = substr($index, 1, -1);
if (is_numeric($index)) { if (is_numeric($index)) {
@@ -1302,9 +1322,9 @@ class Smarty_Compiler extends Smarty {
$this->_syntax_error('(secure) call to private object member is not allowed'); $this->_syntax_error('(secure) call to private object member is not allowed');
} else { } else {
// parse each parameter to the object // parse each parameter to the object
if(preg_match('!\->\w+(?:\((\w+|' . $this->_var_regexp . $this->_mod_regexp . ')(?:\s*,\s*(\w+|' if(preg_match('!(?:\->\w+)+(?:\((' . $this->_obj_param_regexp . ')\))?!', $index, $match)) {
. $this->_var_regexp . $this->_mod_regexp . '))*\))?!', $index, $match)) { preg_match_all('!' . $this->_param_regexp . '!',$match[1], $match);
array_shift($match); $match = $match[0];
rsort($match); rsort($match);
reset($match); reset($match);
$orig_vals = $match; $orig_vals = $match;
@@ -1527,6 +1547,14 @@ class Smarty_Compiler extends Smarty {
$compiled_ref = "'$this->_version'"; $compiled_ref = "'$this->_version'";
break; break;
case 'const':
array_shift($indexes);
if(!defined(substr($indexes[0],1))) {
$this->_syntax_error('$smarty' . implode('', $indexes) .' is an undefined constant');
}
$compiled_ref = substr($indexes[0],1);
break;
default: default:
$this->_syntax_error('$smarty.' . $ref . ' is an unknown reference'); $this->_syntax_error('$smarty.' . $ref . ' is an unknown reference');
break; break;

View File

@@ -50,17 +50,18 @@ class Smarty_Compiler extends Smarty {
var $_capture_stack = array(); // keeps track of nested capture buffers var $_capture_stack = array(); // keeps track of nested capture buffers
var $_plugin_info = array(); // keeps track of plugins to load var $_plugin_info = array(); // keeps track of plugins to load
var $_init_smarty_vars = false; var $_init_smarty_vars = false;
var $_permitted_tokens = array('true','false','yes','no','on','off');
var $_db_qstr_regexp = null; // regexps are setup in the constructor var $_db_qstr_regexp = null; // regexps are setup in the constructor
var $_si_qstr_regexp = null; var $_si_qstr_regexp = null;
var $_qstr_regexp = null; var $_qstr_regexp = null;
var $_func_regexp = null; var $_func_regexp = null;
var $_const_regexp = null;
var $_dvar_regexp = null; var $_dvar_regexp = null;
var $_cvar_regexp = null; var $_cvar_regexp = null;
var $_svar_regexp = null; var $_svar_regexp = null;
var $_avar_regexp = null; var $_avar_regexp = null;
var $_mod_regexp = null; var $_mod_regexp = null;
var $_var_regexp = null; var $_var_regexp = null;
var $_param_regexp = null;
var $_obj_regexp = null; var $_obj_regexp = null;
@@ -84,16 +85,6 @@ class Smarty_Compiler extends Smarty {
// matches single or double quoted strings // matches single or double quoted strings
$this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')'; $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
// matches valid function name:
// foo123
// /foo123
$this->_func_regexp = '\/?\w+';
// matches valid constant names:
// _MY_CONST
// aBc_123
$this->_const_regexp = '\w+';
// matches dollar vars: // matches dollar vars:
// $foo // $foo
// $foo.bar // $foo.bar
@@ -102,8 +93,7 @@ class Smarty_Compiler extends Smarty {
// $foo[$bar] // $foo[$bar]
// $foo[5][blah] // $foo[5][blah]
// $foo[5].bar[$foobar][4] // $foo[5].bar[$foobar][4]
$this->_dvar_regexp = '\$\w+(?:\[\$?[\w\.]+\])*(?:\.\$?\w+(?:\[\$?[\w\.]+\])*)*';
$this->_dvar_regexp = '\$\w+(?:\[\$?\w+\])*(?:\.\w+(?:\[\$?\w+\])*)*';
// matches config vars: // matches config vars:
// #foo# // #foo#
@@ -124,17 +114,40 @@ class Smarty_Compiler extends Smarty {
// |foo:$bar // |foo:$bar
// |foo:"bar":$foobar // |foo:"bar":$foobar
// |foo|bar // |foo|bar
// |foo|bar:$foo|bar:"foo":$bar|foo|bar|foobar // |foo
$this->_mod_regexp = '(?>\|@?\w+(?::(?>' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'|\w+))*)*'; $this->_mod_regexp = '(?>\|@?\w+(?::(?>\w+|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)*';
// matches valid variable/parameter syntax: // matches valid variable syntax:
// $foo
// $foo
// #foo#
// #foo#
// "text"
// "text"
$this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
// matches valid function name:
// foo123
// /foo123
$this->_func_regexp = '\/?\w+';
// matches valid parameter syntax (except an object):
// true
// $foo // $foo
// $foo|bar // $foo|bar
// #foo# // #foo#
// #foo#|bar // #foo#|bar
// "text" // "text"
// "text"|bar // "text"|bar
$this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')'; $this->_param_regexp = '(?:\w+|' . $this->_var_regexp . $this->_mod_regexp . ')';
// matches valid object parameters:
//
// "text"
// $foo, $bar, "text"
// $foo|bar, "foo"|bar
$this->_obj_param_regexp = '(?:' . $this->_param_regexp
. '(?:\s*,\s*(?:' . $this->_param_regexp .'))*)';
// matches valid object call: // matches valid object call:
// $foo->bar // $foo->bar
@@ -142,8 +155,9 @@ class Smarty_Compiler extends Smarty {
// $foo->bar("text") // $foo->bar("text")
// $foo->bar($foo, $bar, "text") // $foo->bar($foo, $bar, "text")
// $foo->bar($foo|bar, "foo"|bar) // $foo->bar($foo|bar, "foo"|bar)
$this->_obj_regexp = '(?:\$\w+(?:\.\w+)*\->\w+(?:\((?:(?:\w+|' . $this->_var_regexp . $this->_mod_regexp // $foo->bar->foo()
. ')(?:\s*,\s*(?:\w+|' . $this->_var_regexp . $this->_mod_regexp .'))*)*\))?)'; // $foo->bar->foo->bar()
$this->_obj_regexp = '(?:\$\w+(?:\.\w+)*(?:\->\w+)+(?:\(' . $this->_obj_param_regexp . '\))?)';
} }
/*======================================================================*\ /*======================================================================*\
@@ -312,27 +326,29 @@ class Smarty_Compiler extends Smarty {
\*======================================================================*/ \*======================================================================*/
function _compile_tag($template_tag) function _compile_tag($template_tag)
{ {
/* Matched comment. */ /* Matched comment. */
if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*') if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
return ''; return '';
/* Split tag into two parts: command and the arguments. */ /* Split tag into two three parts: command, command modifiers and the arguments. */
if(! preg_match('/^((?:' . $this->_obj_regexp . '|' . $this->_var_regexp . '|' . $this->_func_regexp . ')' . $this->_mod_regexp . ') if(! preg_match('/^(?:(' . $this->_obj_regexp . '|' . $this->_var_regexp . '|' . $this->_func_regexp . ')(' . $this->_mod_regexp . '))
(?:\s+(.*))?$ (?:\s+(.*))?$
/xs', $template_tag, $match)) { /xs', $template_tag, $match)) {
$this->_syntax_error("unrecognized tag: $template_tag"); $this->_syntax_error("unrecognized tag: $template_tag");
} }
$tag_command = $match[1]; $tag_command = $match[1];
$tag_args = isset($match[2]) ? $match[2] : ''; $tag_modifier = isset($match[2]) ? $match[2] : '';
$tag_args = isset($match[3]) ? $match[3] : '';
/* If the tag name is not a function, we process it. */ /* If the tag name is not a function, we process it. */
if (!preg_match('!^' . $this->_func_regexp . '$!', $tag_command)) { if (!preg_match('!^' . $this->_func_regexp . '$!', $tag_command)) {
settype($tag_command, 'array'); $array_tag_command = array($tag_command . $tag_modifier);
$this->_parse_vars_props($tag_command); $this->_parse_vars_props($array_tag_command);
return "<?php echo $tag_command[0]; ?>\n"; return "<?php echo $array_tag_command[0]; ?>\n";
} }
switch ($tag_command) { switch ($tag_command) {
case 'include': case 'include':
@@ -421,10 +437,10 @@ class Smarty_Compiler extends Smarty {
default: default:
if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) { if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
return $output; return $output;
} else if ($this->_compile_block_tag($tag_command, $tag_args, $output)) { } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
return $output; return $output;
} else { } else {
return $this->_compile_custom_tag($tag_command, $tag_args); return $this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier);
} }
} }
} }
@@ -492,7 +508,7 @@ class Smarty_Compiler extends Smarty {
Function: _compile_block_tag Function: _compile_block_tag
Purpose: compile block function tag Purpose: compile block function tag
\*======================================================================*/ \*======================================================================*/
function _compile_block_tag($tag_command, $tag_args, &$output) function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
{ {
if ($tag_command{0} == '/') { if ($tag_command{0} == '/') {
$start_tag = false; $start_tag = false;
@@ -558,7 +574,13 @@ class Smarty_Compiler extends Smarty {
$output = "<?php \$this->_tag_stack[] = array('$tag_command', array(".implode(',', (array)$arg_list).")); \$this->_plugins['block']['$tag_command'][0](array(".implode(',', (array)$arg_list)."), null, \$this); ob_start(); ?>"; $output = "<?php \$this->_tag_stack[] = array('$tag_command', array(".implode(',', (array)$arg_list).")); \$this->_plugins['block']['$tag_command'][0](array(".implode(',', (array)$arg_list)."), null, \$this); ob_start(); ?>";
} else { } else {
$output = "<?php \$this->_block_content = ob_get_contents(); ob_end_clean(); \$this->_plugins['block']['$tag_command'][0](\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$this->_block_content, \$this); array_pop(\$this->_tag_stack); ?>"; $output = "<?php \$this->_block_content = ob_get_contents(); ob_end_clean(); ";
$out_tag_text = "\$this->_plugins['block']['$tag_command'][0](\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$this->_block_content, \$this)";
if($tag_modifier != '') {
$this->_parse_modifiers($out_tag_text, $tag_modifier);
}
$output .= 'echo ' . $out_tag_text . ';';
$output .= " array_pop(\$this->_tag_stack); ?>";
} }
return true; return true;
@@ -569,7 +591,7 @@ class Smarty_Compiler extends Smarty {
Function: _compile_custom_tag Function: _compile_custom_tag
Purpose: compile custom function tag Purpose: compile custom function tag
\*======================================================================*/ \*======================================================================*/
function _compile_custom_tag($tag_command, $tag_args) function _compile_custom_tag($tag_command, $tag_args, $tag_modifier)
{ {
$this->_add_plugin('function', $tag_command); $this->_add_plugin('function', $tag_command);
@@ -581,7 +603,13 @@ class Smarty_Compiler extends Smarty {
$arg_list[] = "'$arg_name' => $arg_value"; $arg_list[] = "'$arg_name' => $arg_value";
} }
return "<?php \$this->_plugins['function']['$tag_command'][0](array(".implode(',', (array)$arg_list)."), \$this); ?>"; $return = "\$this->_plugins['function']['$tag_command'][0](array(".implode(',', (array)$arg_list)."), \$this)";
if($tag_modifier != '') {
$this->_parse_modifiers($return, $tag_modifier);
}
return '<?php echo ' . $return . " ; ?>\n";
} }
@@ -727,7 +755,7 @@ class Smarty_Compiler extends Smarty {
$attrs = $this->_parse_attrs($tag_args); $attrs = $this->_parse_attrs($tag_args);
$arg_list = array(); $arg_list = array();
$output = "<?php "; $output = '<?php ';
$section_name = $attrs['name']; $section_name = $attrs['name'];
if (empty($section_name)) { if (empty($section_name)) {
$this->_syntax_error("missing section name"); $this->_syntax_error("missing section name");
@@ -921,8 +949,6 @@ class Smarty_Compiler extends Smarty {
)/x', $tag_args, $match); )/x', $tag_args, $match);
$tokens = $match[0]; $tokens = $match[0];
$this->_parse_vars_props($tokens);
$is_arg_stack = array(); $is_arg_stack = array();
for ($i = 0, $for_max = count($tokens); $i < $for_max; $i++) { for ($i = 0, $for_max = count($tokens); $i < $for_max; $i++) {
@@ -1003,14 +1029,32 @@ class Smarty_Compiler extends Smarty {
$i = $is_arg_start; $i = $is_arg_start;
break; break;
case ')':
break;
default: default:
if($this->security &&
$i+1 < count($tokens) && if(preg_match('!^[a-zA-Z_]\w+$!', $token)) {
$tokens[$i+1] == '(' &&
preg_match('!^[a-zA-Z_]\w+$!', $tokens[$i]) && if($this->security &&
!in_array($tokens[$i], $this->security_settings['IF_FUNCS'])) { $i+1 < count($tokens) &&
$this->_syntax_error("(secure mode) '".$tokens[$i]."' not allowed in if statement"); $tokens[$i+1] == '(' &&
} !in_array($token, $this->security_settings['IF_FUNCS'])) {
$this->_syntax_error("(secure mode) '".$tokens[$i]."' not allowed in if statement");
} else {
if(!function_exists($token)) {
$this->_syntax_error("reference to non-existant function: $token");
}
}
} else {
$tmptoken = array($token);
$this->_parse_vars_props($tmptoken);
$token = $tmptoken[0];
unset($tmptoken);
}
break; break;
} }
} }
@@ -1087,13 +1131,13 @@ class Smarty_Compiler extends Smarty {
\*======================================================================*/ \*======================================================================*/
function _parse_attrs($tag_args, $quote = true) function _parse_attrs($tag_args, $quote = true)
{ {
/* Tokenize tag attributes. */ /* Tokenize tag attributes. */
preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+) preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
)+ | )+ |
[=] [=]
/x', $tag_args, $match); /x', $tag_args, $match);
$tokens = $match[0]; $tokens = $match[0];
$var_delims = array('$', '#', '%');
$attrs = array(); $attrs = array();
/* Parse state: /* Parse state:
@@ -1132,13 +1176,11 @@ class Smarty_Compiler extends Smarty {
$token = true; $token = true;
else if (preg_match('!^(off|no|false)$!', $token)) else if (preg_match('!^(off|no|false)$!', $token))
$token = false; $token = false;
/* If the token is not variable (doesn't start with /* If the token is just a string,
'$', '#', or '%') and not enclosed in single or we double-quote it. */
double quotes we single-quote it. */ else if (preg_match('!^\w+$!', $token)) {
else if ($quote && !in_array($token{0}, $var_delims) &&
!(($token{0} == '"' || $token{0} == "'") &&
$token{strlen($token)-1} == $token{0}))
$token = '"'.$token.'"'; $token = '"'.$token.'"';
}
$attrs[$attr_name] = $token; $attrs[$attr_name] = $token;
$state = 0; $state = 0;
@@ -1161,63 +1203,40 @@ class Smarty_Compiler extends Smarty {
\*======================================================================*/ \*======================================================================*/
function _parse_vars_props(&$tokens) function _parse_vars_props(&$tokens)
{ {
foreach($tokens as $key => $val) {
$var_exprs = preg_grep('!^(' . $this->_dvar_regexp . '|' . $this->_obj_regexp . ')' . $this->_mod_regexp . '$!', $tokens); if(preg_match('!^(' . $this->_dvar_regexp . '|' . $this->_obj_regexp . ')' . $this->_mod_regexp . '$!', $val)) {
$conf_var_exprs = preg_grep('!^' . $this->_cvar_regexp . $this->_mod_regexp . '$!', $tokens); // $ variable
$sect_prop_exprs = preg_grep('!^' . $this->_svar_regexp . $this->_mod_regexp . '$!', $tokens); $tokens[$key] = $this->_parse_var($val);
$db_quoted_exprs = preg_grep('!^' . $this->_db_qstr_regexp . $this->_mod_regexp . '$!', $tokens); }
$si_quoted_exprs = preg_grep('!^' . $this->_si_qstr_regexp . $this->_mod_regexp . '$!', $tokens); elseif(preg_match('!^' . $this->_db_qstr_regexp . $this->_mod_regexp . '$!', $val)) {
$const_exprs = preg_grep('!^' . $this->_const_regexp . '$!', $tokens); // double quoted text
preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $val, $match);
if (count($const_exprs)) { $tokens[$key] = $this->_expand_quoted_text($match[1]);
foreach ($const_exprs as $constant) { if($match[2] != '') {
if($this->security && ! $this->security_settings['ALLOW_CONSTANTS']) { $this->_parse_modifiers($tokens[$key], $match[2]);
$this->_syntax_error("(secure) constants not allowed: $constant");
if(!defined($constant)) {
$this->_syntax_error("(secure) undefined constant referenced: $constant");
} }
} }
} elseif(preg_match('!^' . $this->_si_qstr_regexp . $this->_mod_regexp . '$!', $val)) {
} // single quoted text
preg_match('!^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $val, $match);
if($match[2] != '') {
if (count($db_quoted_exprs)) { $this->_parse_modifiers($match[1], $match[2]);
foreach ($db_quoted_exprs as $expr_index => $var_expr) { $tokens[$key] = $match[1];
preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $var_expr, $match); }
$tokens[$expr_index] = $this->_expand_quoted_text($match[1]);
if($match[2] != '') {
$this->_parse_modifiers($tokens[$expr_index], $match[2]);
} }
} elseif(preg_match('!^' . $this->_cvar_regexp . $this->_mod_regexp . '$!', $val)) {
} // config var
$tokens[$key] = $this->_parse_conf_var($val);
if (count($si_quoted_exprs)) {
foreach ($si_quoted_exprs as $expr_index => $var_expr) {
preg_match('!^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $var_expr, $match);
if($match[2] != '') {
$this->_parse_modifiers($match[1], $match[2]);
$tokens[$expr_index] = $match[1];
} }
} elseif(preg_match('!^' . $this->_svar_regexp . $this->_mod_regexp . '$!', $val)) {
// section var
$tokens[$key] = $this->_parse_section_prop($val);
}
elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
// not recognized value
$this->_syntax_error("unknown syntax: $val");
}
} }
if (count($var_exprs)) {
foreach ($var_exprs as $expr_index => $var_expr) {
$tokens[$expr_index] = $this->_parse_var($var_expr);
}
}
if (count($conf_var_exprs)) {
foreach ($conf_var_exprs as $expr_index => $var_expr) {
$tokens[$expr_index] = $this->_parse_conf_var($var_expr);
}
}
if (count($sect_prop_exprs)) {
foreach ($sect_prop_exprs as $expr_index => $section_prop_expr) {
$tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr);
}
}
} }
/*======================================================================*\ /*======================================================================*\
@@ -1257,7 +1276,7 @@ class Smarty_Compiler extends Smarty {
} }
// get [foo] and .foo and ->foo() pieces // get [foo] and .foo and ->foo() pieces
preg_match_all('!(^\w+)|(?:\[\$?\w+\])|\.\w+|->\w+(?:\([^\)]*\))?!', $var_ref, $match); preg_match_all('!(^\w+)|(?:\[\$?[\w\.]+\])|\.\$?\w+|(?:->\w+)+(?:\([^\)]*\))?!', $var_ref, $match);
$indexes = $match[0]; $indexes = $match[0];
$var_name = array_shift($indexes); $var_name = array_shift($indexes);
@@ -1280,6 +1299,7 @@ class Smarty_Compiler extends Smarty {
} }
foreach ($indexes as $index) { foreach ($indexes as $index) {
if ($index{0} == '[') { if ($index{0} == '[') {
$index = substr($index, 1, -1); $index = substr($index, 1, -1);
if (is_numeric($index)) { if (is_numeric($index)) {
@@ -1302,9 +1322,9 @@ class Smarty_Compiler extends Smarty {
$this->_syntax_error('(secure) call to private object member is not allowed'); $this->_syntax_error('(secure) call to private object member is not allowed');
} else { } else {
// parse each parameter to the object // parse each parameter to the object
if(preg_match('!\->\w+(?:\((\w+|' . $this->_var_regexp . $this->_mod_regexp . ')(?:\s*,\s*(\w+|' if(preg_match('!(?:\->\w+)+(?:\((' . $this->_obj_param_regexp . ')\))?!', $index, $match)) {
. $this->_var_regexp . $this->_mod_regexp . '))*\))?!', $index, $match)) { preg_match_all('!' . $this->_param_regexp . '!',$match[1], $match);
array_shift($match); $match = $match[0];
rsort($match); rsort($match);
reset($match); reset($match);
$orig_vals = $match; $orig_vals = $match;
@@ -1527,6 +1547,14 @@ class Smarty_Compiler extends Smarty {
$compiled_ref = "'$this->_version'"; $compiled_ref = "'$this->_version'";
break; break;
case 'const':
array_shift($indexes);
if(!defined(substr($indexes[0],1))) {
$this->_syntax_error('$smarty' . implode('', $indexes) .' is an undefined constant');
}
$compiled_ref = substr($indexes[0],1);
break;
default: default:
$this->_syntax_error('$smarty.' . $ref . ' is an unknown reference'); $this->_syntax_error('$smarty.' . $ref . ' is an unknown reference');
break; break;