update compiler code, clean up regex, add new syntax features

This commit is contained in:
mohrt
2002-12-18 16:51:48 +00:00
parent d6ba422ce9
commit 339210ff3a
5 changed files with 381 additions and 99 deletions

6
NEWS
View File

@@ -1,7 +1,11 @@
- can now pass modifiers to static content,
among other minor syntax features (monte)
- fix up regex code in compiler, make much more flexible
(Monte)
- added support for object method access (Monte) - added support for object method access (Monte)
- added day_value_format to html_select_date (Marcus - added day_value_format to html_select_date (Marcus
Bointon, Monte) Bointon, Monte)
- assigned variables are not longer in global - assigned variables are no longer in global
namespace, saving extract() calls and speeding namespace, saving extract() calls and speeding
up fetch() and display() linearly with no. of up fetch() and display() linearly with no. of
assigned variables (Monte) assigned variables (Monte)

View File

@@ -141,7 +141,8 @@ class Smarty
'in_array', 'is_array'), 'in_array', 'is_array'),
'INCLUDE_ANY' => false, 'INCLUDE_ANY' => false,
'PHP_TAGS' => false, 'PHP_TAGS' => false,
'MODIFIER_FUNCS' => array('count') 'MODIFIER_FUNCS' => array('count'),
'ALLOW_CONSTANTS' => false
); );
var $trusted_dir = array(); // directories where trusted templates & php scripts var $trusted_dir = array(); // directories where trusted templates & php scripts
// reside ($security is disabled during their // reside ($security is disabled during their
@@ -190,6 +191,8 @@ class Smarty
var $_smarty_debug_id = 'SMARTY_DEBUG'; // text in URL to enable debug mode var $_smarty_debug_id = 'SMARTY_DEBUG'; // text in URL to enable debug mode
var $_smarty_debug_info = array(); // debugging information for debug console var $_smarty_debug_info = array(); // debugging information for debug console
var $_cache_info = array(); // info that makes up a cache file var $_cache_info = array(); // info that makes up a cache file
var $_file_perms = 0644; // default file permissions
var $_dir_perms = 0771; // default dir permissions
var $_plugins = array( // table keeping track of plugins var $_plugins = array( // table keeping track of plugins
'modifier' => array(), 'modifier' => array(),
'function' => array(), 'function' => array(),
@@ -1501,7 +1504,7 @@ function _run_insert_handler($args)
if ( strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' || (flock($fd, LOCK_EX)) ) { if ( strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' || (flock($fd, LOCK_EX)) ) {
fwrite( $fd, $contents ); fwrite( $fd, $contents );
fclose($fd); fclose($fd);
chmod($filename, 0644); chmod($filename, $this->_file_perms);
} }
return true; return true;
@@ -1675,7 +1678,7 @@ function _run_insert_handler($args)
$_make_new_dir = true; $_make_new_dir = true;
} }
if ($_make_new_dir && !file_exists($_new_dir) && !@mkdir($_new_dir, 0771)) { if ($_make_new_dir && !file_exists($_new_dir) && !@mkdir($_new_dir, $this->_dir_perms)) {
$this->trigger_error("problem creating directory \"$dir\""); $this->trigger_error("problem creating directory \"$dir\"");
return false; return false;
} }

View File

@@ -50,9 +50,102 @@ 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 $_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\''; var $_db_qstr_regexp = null; // regexps are setup in the constructor
var $_si_qstr_regexp = null;
var $_qstr_regexp = null;
var $_func_regexp = null;
var $_const_regexp = null;
var $_dvar_regexp = null;
var $_cvar_regexp = null;
var $_svar_regexp = null;
var $_avar_regexp = null;
var $_mod_regexp = null;
var $_var_regexp = null;
var $_obj_regexp = null;
/*======================================================================*\
Function: Smarty_Compiler()
Input: constructor
\*======================================================================*/
function Smarty_Compiler()
{
// matches double quoted strings:
// "foobar"
// "foo\"bar"
$this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
// matches single quoted strings:
// 'foobar'
// 'foo\'bar'
$this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
// matches single or double quoted strings
$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:
// $foo
// $foo.bar
// $foo.bar.foobar
// $foo[0]
// $foo[$bar]
// $foo[5][blah]
// $foo[5].bar[$foobar][4]
$this->_dvar_regexp = '\$\w+(?:\[\$?\w+\])*(?:\.\w+(?:\[\$?\w+\])*)*';
// matches config vars:
// #foo#
// #foobar123_foo#
$this->_cvar_regexp = '\#\w+\#';
// matches section vars:
// %foo.bar%
$this->_svar_regexp = '\%\w+\.\w+\%';
// matches all valid variables (no quotes, no modifiers)
$this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|' . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
// matches valid modifier syntax:
// |foo
// |@foo
// |foo:"bar"
// |foo:$bar
// |foo:"bar":$foobar
// |foo|bar
// |foo|bar:$foo|bar:"foo":$bar|foo|bar|foobar
$this->_mod_regexp = '(?>\|@?\w+(?::(?>' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'|\w+))*)*';
// matches valid variable/parameter syntax:
// $foo
// $foo|bar
// #foo#
// #foo#|bar
// "text"
// "text"|bar
$this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
// matches valid object call:
// $foo->bar
// $foo->bar()
// $foo->bar("text")
// $foo->bar($foo, $bar, "text")
// $foo->bar($foo|bar, "foo"|bar)
$this->_obj_regexp = '(?:\$\w+(?:\.\w+)*\->\w+(?:\((?:(?:\w+|' . $this->_var_regexp . $this->_mod_regexp
. ')(?:\s*,\s*(?:\w+|' . $this->_var_regexp . $this->_mod_regexp .'))*)*\))?)';
}
/*======================================================================*\ /*======================================================================*\
Function: _compile_file() Function: _compile_file()
Input: compile a template file Input: compile a template file
@@ -224,20 +317,18 @@ class Smarty_Compiler extends Smarty {
return ''; return '';
/* Split tag into two parts: command and the arguments. */ /* Split tag into two parts: command and the arguments. */
preg_match('/^( if(! preg_match('/^((?:' . $this->_obj_regexp . '|' . $this->_var_regexp . '|' . $this->_func_regexp . ')' . $this->_mod_regexp . ')
(?: ' . $this->_qstr_regexp . ' | (?>[^"\'\s]+))+ (?:\s+(.*))?$
) /xs', $template_tag, $match)) {
(?:\s+(.*))? $this->_syntax_error("unrecognized tag: $template_tag");
/xs', $template_tag, $match); }
$tag_command = $match[1]; $tag_command = $match[1];
$tag_args = isset($match[2]) ? $match[2] : ''; $tag_args = isset($match[2]) ? $match[2] : '';
/* If the tag name matches a variable or section property definition, /* If the tag name is not a function, we process it. */
we simply process it. */
if (preg_match('!^\$\w+(?>(\[(\d+|\$\w+|\w+(\.\w+)?)\])|((\.|->)\$?\w+(?:\((?:' . $this->_qstr_regexp . ')*\))*))*(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tag_command) || // if a variable if (!preg_match('!^' . $this->_func_regexp . '$!', $tag_command)) {
preg_match('!^#(\w+)#(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tag_command) || // or a configuration variable
preg_match('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tag_command)) { // or a section property
settype($tag_command, 'array'); settype($tag_command, 'array');
$this->_parse_vars_props($tag_command); $this->_parse_vars_props($tag_command);
return "<?php echo $tag_command[0]; ?>\n"; return "<?php echo $tag_command[0]; ?>\n";
@@ -824,9 +915,7 @@ class Smarty_Compiler extends Smarty {
function _compile_if_tag($tag_args, $elseif = false) function _compile_if_tag($tag_args, $elseif = false)
{ {
/* Tokenize args for 'if' tag. */ /* Tokenize args for 'if' tag. */
preg_match_all('/(?: preg_match_all('/(?:' . $this->_qstr_regexp . ' | # match all quoted strings
"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | # match all double quoted strings allowing escaped double quotes
\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | # match all single quoted strings allowing escaped single quotes
[(),] | # match parentheses and commas [(),] | # match parentheses and commas
[^\s(),]+ # match any other token that is not any of the above [^\s(),]+ # match any other token that is not any of the above
)/x', $tag_args, $match); )/x', $tag_args, $match);
@@ -999,8 +1088,7 @@ 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('/(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | (?>[^"\'=\s]+)
)+ | )+ |
[=] [=]
/x', $tag_args, $match); /x', $tag_args, $match);
@@ -1074,21 +1162,41 @@ class Smarty_Compiler extends Smarty {
function _parse_vars_props(&$tokens) function _parse_vars_props(&$tokens)
{ {
$var_exprs = preg_grep('!^\$\w+(?>(\[(\d+|\$\w+|\w+(\.\w+)?)\])|((\.|->)\$?\w+(?:\((?:' . $this->_qstr_regexp . ')*\))?))*(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tokens); $var_exprs = preg_grep('!^(' . $this->_dvar_regexp . '|' . $this->_obj_regexp . ')' . $this->_mod_regexp . '$!', $tokens);
$conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tokens); $conf_var_exprs = preg_grep('!^' . $this->_cvar_regexp . $this->_mod_regexp . '$!', $tokens);
$sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tokens); $sect_prop_exprs = preg_grep('!^' . $this->_svar_regexp . $this->_mod_regexp . '$!', $tokens);
$db_quoted_exprs = preg_grep('!^"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"$!', $tokens); $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);
$const_exprs = preg_grep('!^' . $this->_const_regexp . '$!', $tokens);
if (count($const_exprs)) {
foreach ($const_exprs as $constant) {
if($this->security && ! $this->security_settings['ALLOW_CONSTANTS']) {
$this->_syntax_error("(secure) constants not allowed: $constant");
if(!defined($constant)) {
$this->_syntax_error("(secure) undefined constant referenced: $constant");
}
}
}
}
if (count($db_quoted_exprs)) { if (count($db_quoted_exprs)) {
// replace variables embedded in quotes
foreach ($db_quoted_exprs as $expr_index => $var_expr) { foreach ($db_quoted_exprs as $expr_index => $var_expr) {
if(preg_match_all('|(?<!\\\\)\$\w+|', $var_expr, $match)) { preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $var_expr, $match);
rsort($match[0]); $tokens[$expr_index] = $this->_expand_quoted_text($match[1]);
reset($match[0]); if($match[2] != '') {
foreach($match[0] as $var) { $this->_parse_modifiers($tokens[$expr_index], $match[2]);
$var_expr = str_replace ($var, '".' . $this->_parse_var($var) . '."', $var_expr);
} }
$tokens[$expr_index] = preg_replace('!\.""|""\.!', '', $var_expr); }
}
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];
} }
} }
} }
@@ -1110,9 +1218,26 @@ class Smarty_Compiler extends Smarty {
$tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr); $tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr);
} }
} }
} }
/*======================================================================*\
Function: _expand_quoted_text
Purpose: expand quoted text with embedded variables
\*======================================================================*/
function _expand_quoted_text($var_expr)
{
// if contains unescaped $, expand it
if(preg_match_all('|(?<!\\\\)\$\w+|', $var_expr, $match)) {
rsort($match[0]);
reset($match[0]);
foreach($match[0] as $var) {
$var_expr = str_replace ($var, '".' . $this->_parse_var($var) . '."', $var_expr);
}
return preg_replace('!\.""|""\.!', '', $var_expr);
} else {
return $var_expr;
}
}
/*======================================================================*\ /*======================================================================*\
Function: _parse_var Function: _parse_var
@@ -1120,16 +1245,19 @@ class Smarty_Compiler extends Smarty {
\*======================================================================*/ \*======================================================================*/
function _parse_var($var_expr) function _parse_var($var_expr)
{ {
$parts = explode('|', substr($var_expr, 1), 2);
$var_ref = $parts[0]; preg_match('!(' . $this->_obj_regexp . '|' . $this->_var_regexp . ')(' . $this->_mod_regexp . ')$!', $var_expr, $match);
$modifiers = isset($parts[1]) ? $parts[1] : '';
$var_ref = substr($match[1],1);
$modifiers = $match[2];
if(!empty($this->default_modifiers) && !preg_match('!(^|\|)smarty:nodefaults($|\|)!',$modifiers)) { if(!empty($this->default_modifiers) && !preg_match('!(^|\|)smarty:nodefaults($|\|)!',$modifiers)) {
$_default_mod_string = implode('|',(array)$this->default_modifiers); $_default_mod_string = implode('|',(array)$this->default_modifiers);
$modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers; $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
} }
preg_match_all('!\[(?:\$\w+|\w+(\.\w+)?)\]|(->|\.)\$?\w+(?:\((?:' . $this->_qstr_regexp . ')*\))?|^\w+!', $var_ref, $match); // get [foo] and .foo and ->foo() pieces
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);
@@ -1170,9 +1298,19 @@ class Smarty_Compiler extends Smarty {
else else
$output .= "['" . substr($index, 1) . "']"; $output .= "['" . substr($index, 1) . "']";
} else if (substr($index,0,2) == '->') { } else if (substr($index,0,2) == '->') {
if(substr($index,2,1) == '_') { if($this->security && substr($index,2,1) == '_') {
$this->_syntax_error('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
if(preg_match('!\->\w+(?:\((\w+|' . $this->_var_regexp . $this->_mod_regexp . ')(?:\s*,\s*(\w+|'
. $this->_var_regexp . $this->_mod_regexp . '))*\))?!', $index, $match)) {
array_shift($match);
rsort($match);
reset($match);
$orig_vals = $match;
$this->_parse_vars_props($match);
$index = str_replace($orig_vals, $match, $index);
}
$output .= $index; $output .= $index;
} }
} else { } else {
@@ -1181,10 +1319,8 @@ class Smarty_Compiler extends Smarty {
} }
// look for variables imbedded in quoted strings, replace them // look for variables imbedded in quoted strings, replace them
if(preg_match('!"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"!', $output, $match)) { if(preg_match('!' . $this->_qstr_regexp . '!', $output, $match)) {
$dbq_string = $match[0]; $output = str_replace($match[0], $this->_expand_quoted_text($match[0]), $output);
$this->_parse_vars_props($match);
$output = str_replace($dbq_string, $match[0], $output);
} }
$this->_parse_modifiers($output, $modifiers); $this->_parse_modifiers($output, $modifiers);

View File

@@ -141,7 +141,8 @@ class Smarty
'in_array', 'is_array'), 'in_array', 'is_array'),
'INCLUDE_ANY' => false, 'INCLUDE_ANY' => false,
'PHP_TAGS' => false, 'PHP_TAGS' => false,
'MODIFIER_FUNCS' => array('count') 'MODIFIER_FUNCS' => array('count'),
'ALLOW_CONSTANTS' => false
); );
var $trusted_dir = array(); // directories where trusted templates & php scripts var $trusted_dir = array(); // directories where trusted templates & php scripts
// reside ($security is disabled during their // reside ($security is disabled during their
@@ -190,6 +191,8 @@ class Smarty
var $_smarty_debug_id = 'SMARTY_DEBUG'; // text in URL to enable debug mode var $_smarty_debug_id = 'SMARTY_DEBUG'; // text in URL to enable debug mode
var $_smarty_debug_info = array(); // debugging information for debug console var $_smarty_debug_info = array(); // debugging information for debug console
var $_cache_info = array(); // info that makes up a cache file var $_cache_info = array(); // info that makes up a cache file
var $_file_perms = 0644; // default file permissions
var $_dir_perms = 0771; // default dir permissions
var $_plugins = array( // table keeping track of plugins var $_plugins = array( // table keeping track of plugins
'modifier' => array(), 'modifier' => array(),
'function' => array(), 'function' => array(),
@@ -1501,7 +1504,7 @@ function _run_insert_handler($args)
if ( strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' || (flock($fd, LOCK_EX)) ) { if ( strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' || (flock($fd, LOCK_EX)) ) {
fwrite( $fd, $contents ); fwrite( $fd, $contents );
fclose($fd); fclose($fd);
chmod($filename, 0644); chmod($filename, $this->_file_perms);
} }
return true; return true;
@@ -1675,7 +1678,7 @@ function _run_insert_handler($args)
$_make_new_dir = true; $_make_new_dir = true;
} }
if ($_make_new_dir && !file_exists($_new_dir) && !@mkdir($_new_dir, 0771)) { if ($_make_new_dir && !file_exists($_new_dir) && !@mkdir($_new_dir, $this->_dir_perms)) {
$this->trigger_error("problem creating directory \"$dir\""); $this->trigger_error("problem creating directory \"$dir\"");
return false; return false;
} }

View File

@@ -50,9 +50,102 @@ 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 $_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\''; var $_db_qstr_regexp = null; // regexps are setup in the constructor
var $_si_qstr_regexp = null;
var $_qstr_regexp = null;
var $_func_regexp = null;
var $_const_regexp = null;
var $_dvar_regexp = null;
var $_cvar_regexp = null;
var $_svar_regexp = null;
var $_avar_regexp = null;
var $_mod_regexp = null;
var $_var_regexp = null;
var $_obj_regexp = null;
/*======================================================================*\
Function: Smarty_Compiler()
Input: constructor
\*======================================================================*/
function Smarty_Compiler()
{
// matches double quoted strings:
// "foobar"
// "foo\"bar"
$this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
// matches single quoted strings:
// 'foobar'
// 'foo\'bar'
$this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
// matches single or double quoted strings
$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:
// $foo
// $foo.bar
// $foo.bar.foobar
// $foo[0]
// $foo[$bar]
// $foo[5][blah]
// $foo[5].bar[$foobar][4]
$this->_dvar_regexp = '\$\w+(?:\[\$?\w+\])*(?:\.\w+(?:\[\$?\w+\])*)*';
// matches config vars:
// #foo#
// #foobar123_foo#
$this->_cvar_regexp = '\#\w+\#';
// matches section vars:
// %foo.bar%
$this->_svar_regexp = '\%\w+\.\w+\%';
// matches all valid variables (no quotes, no modifiers)
$this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|' . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
// matches valid modifier syntax:
// |foo
// |@foo
// |foo:"bar"
// |foo:$bar
// |foo:"bar":$foobar
// |foo|bar
// |foo|bar:$foo|bar:"foo":$bar|foo|bar|foobar
$this->_mod_regexp = '(?>\|@?\w+(?::(?>' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'|\w+))*)*';
// matches valid variable/parameter syntax:
// $foo
// $foo|bar
// #foo#
// #foo#|bar
// "text"
// "text"|bar
$this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
// matches valid object call:
// $foo->bar
// $foo->bar()
// $foo->bar("text")
// $foo->bar($foo, $bar, "text")
// $foo->bar($foo|bar, "foo"|bar)
$this->_obj_regexp = '(?:\$\w+(?:\.\w+)*\->\w+(?:\((?:(?:\w+|' . $this->_var_regexp . $this->_mod_regexp
. ')(?:\s*,\s*(?:\w+|' . $this->_var_regexp . $this->_mod_regexp .'))*)*\))?)';
}
/*======================================================================*\ /*======================================================================*\
Function: _compile_file() Function: _compile_file()
Input: compile a template file Input: compile a template file
@@ -224,20 +317,18 @@ class Smarty_Compiler extends Smarty {
return ''; return '';
/* Split tag into two parts: command and the arguments. */ /* Split tag into two parts: command and the arguments. */
preg_match('/^( if(! preg_match('/^((?:' . $this->_obj_regexp . '|' . $this->_var_regexp . '|' . $this->_func_regexp . ')' . $this->_mod_regexp . ')
(?: ' . $this->_qstr_regexp . ' | (?>[^"\'\s]+))+ (?:\s+(.*))?$
) /xs', $template_tag, $match)) {
(?:\s+(.*))? $this->_syntax_error("unrecognized tag: $template_tag");
/xs', $template_tag, $match); }
$tag_command = $match[1]; $tag_command = $match[1];
$tag_args = isset($match[2]) ? $match[2] : ''; $tag_args = isset($match[2]) ? $match[2] : '';
/* If the tag name matches a variable or section property definition, /* If the tag name is not a function, we process it. */
we simply process it. */
if (preg_match('!^\$\w+(?>(\[(\d+|\$\w+|\w+(\.\w+)?)\])|((\.|->)\$?\w+(?:\((?:' . $this->_qstr_regexp . ')*\))*))*(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tag_command) || // if a variable if (!preg_match('!^' . $this->_func_regexp . '$!', $tag_command)) {
preg_match('!^#(\w+)#(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tag_command) || // or a configuration variable
preg_match('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tag_command)) { // or a section property
settype($tag_command, 'array'); settype($tag_command, 'array');
$this->_parse_vars_props($tag_command); $this->_parse_vars_props($tag_command);
return "<?php echo $tag_command[0]; ?>\n"; return "<?php echo $tag_command[0]; ?>\n";
@@ -824,9 +915,7 @@ class Smarty_Compiler extends Smarty {
function _compile_if_tag($tag_args, $elseif = false) function _compile_if_tag($tag_args, $elseif = false)
{ {
/* Tokenize args for 'if' tag. */ /* Tokenize args for 'if' tag. */
preg_match_all('/(?: preg_match_all('/(?:' . $this->_qstr_regexp . ' | # match all quoted strings
"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | # match all double quoted strings allowing escaped double quotes
\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | # match all single quoted strings allowing escaped single quotes
[(),] | # match parentheses and commas [(),] | # match parentheses and commas
[^\s(),]+ # match any other token that is not any of the above [^\s(),]+ # match any other token that is not any of the above
)/x', $tag_args, $match); )/x', $tag_args, $match);
@@ -999,8 +1088,7 @@ 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('/(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | (?>[^"\'=\s]+)
)+ | )+ |
[=] [=]
/x', $tag_args, $match); /x', $tag_args, $match);
@@ -1074,21 +1162,41 @@ class Smarty_Compiler extends Smarty {
function _parse_vars_props(&$tokens) function _parse_vars_props(&$tokens)
{ {
$var_exprs = preg_grep('!^\$\w+(?>(\[(\d+|\$\w+|\w+(\.\w+)?)\])|((\.|->)\$?\w+(?:\((?:' . $this->_qstr_regexp . ')*\))?))*(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tokens); $var_exprs = preg_grep('!^(' . $this->_dvar_regexp . '|' . $this->_obj_regexp . ')' . $this->_mod_regexp . '$!', $tokens);
$conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tokens); $conf_var_exprs = preg_grep('!^' . $this->_cvar_regexp . $this->_mod_regexp . '$!', $tokens);
$sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $this->_qstr_regexp . '|[^|]+))*)*$!', $tokens); $sect_prop_exprs = preg_grep('!^' . $this->_svar_regexp . $this->_mod_regexp . '$!', $tokens);
$db_quoted_exprs = preg_grep('!^"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"$!', $tokens); $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);
$const_exprs = preg_grep('!^' . $this->_const_regexp . '$!', $tokens);
if (count($const_exprs)) {
foreach ($const_exprs as $constant) {
if($this->security && ! $this->security_settings['ALLOW_CONSTANTS']) {
$this->_syntax_error("(secure) constants not allowed: $constant");
if(!defined($constant)) {
$this->_syntax_error("(secure) undefined constant referenced: $constant");
}
}
}
}
if (count($db_quoted_exprs)) { if (count($db_quoted_exprs)) {
// replace variables embedded in quotes
foreach ($db_quoted_exprs as $expr_index => $var_expr) { foreach ($db_quoted_exprs as $expr_index => $var_expr) {
if(preg_match_all('|(?<!\\\\)\$\w+|', $var_expr, $match)) { preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . ')$!', $var_expr, $match);
rsort($match[0]); $tokens[$expr_index] = $this->_expand_quoted_text($match[1]);
reset($match[0]); if($match[2] != '') {
foreach($match[0] as $var) { $this->_parse_modifiers($tokens[$expr_index], $match[2]);
$var_expr = str_replace ($var, '".' . $this->_parse_var($var) . '."', $var_expr);
} }
$tokens[$expr_index] = preg_replace('!\.""|""\.!', '', $var_expr); }
}
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];
} }
} }
} }
@@ -1110,9 +1218,26 @@ class Smarty_Compiler extends Smarty {
$tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr); $tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr);
} }
} }
} }
/*======================================================================*\
Function: _expand_quoted_text
Purpose: expand quoted text with embedded variables
\*======================================================================*/
function _expand_quoted_text($var_expr)
{
// if contains unescaped $, expand it
if(preg_match_all('|(?<!\\\\)\$\w+|', $var_expr, $match)) {
rsort($match[0]);
reset($match[0]);
foreach($match[0] as $var) {
$var_expr = str_replace ($var, '".' . $this->_parse_var($var) . '."', $var_expr);
}
return preg_replace('!\.""|""\.!', '', $var_expr);
} else {
return $var_expr;
}
}
/*======================================================================*\ /*======================================================================*\
Function: _parse_var Function: _parse_var
@@ -1120,16 +1245,19 @@ class Smarty_Compiler extends Smarty {
\*======================================================================*/ \*======================================================================*/
function _parse_var($var_expr) function _parse_var($var_expr)
{ {
$parts = explode('|', substr($var_expr, 1), 2);
$var_ref = $parts[0]; preg_match('!(' . $this->_obj_regexp . '|' . $this->_var_regexp . ')(' . $this->_mod_regexp . ')$!', $var_expr, $match);
$modifiers = isset($parts[1]) ? $parts[1] : '';
$var_ref = substr($match[1],1);
$modifiers = $match[2];
if(!empty($this->default_modifiers) && !preg_match('!(^|\|)smarty:nodefaults($|\|)!',$modifiers)) { if(!empty($this->default_modifiers) && !preg_match('!(^|\|)smarty:nodefaults($|\|)!',$modifiers)) {
$_default_mod_string = implode('|',(array)$this->default_modifiers); $_default_mod_string = implode('|',(array)$this->default_modifiers);
$modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers; $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
} }
preg_match_all('!\[(?:\$\w+|\w+(\.\w+)?)\]|(->|\.)\$?\w+(?:\((?:' . $this->_qstr_regexp . ')*\))?|^\w+!', $var_ref, $match); // get [foo] and .foo and ->foo() pieces
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);
@@ -1170,9 +1298,19 @@ class Smarty_Compiler extends Smarty {
else else
$output .= "['" . substr($index, 1) . "']"; $output .= "['" . substr($index, 1) . "']";
} else if (substr($index,0,2) == '->') { } else if (substr($index,0,2) == '->') {
if(substr($index,2,1) == '_') { if($this->security && substr($index,2,1) == '_') {
$this->_syntax_error('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
if(preg_match('!\->\w+(?:\((\w+|' . $this->_var_regexp . $this->_mod_regexp . ')(?:\s*,\s*(\w+|'
. $this->_var_regexp . $this->_mod_regexp . '))*\))?!', $index, $match)) {
array_shift($match);
rsort($match);
reset($match);
$orig_vals = $match;
$this->_parse_vars_props($match);
$index = str_replace($orig_vals, $match, $index);
}
$output .= $index; $output .= $index;
} }
} else { } else {
@@ -1181,10 +1319,8 @@ class Smarty_Compiler extends Smarty {
} }
// look for variables imbedded in quoted strings, replace them // look for variables imbedded in quoted strings, replace them
if(preg_match('!"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"!', $output, $match)) { if(preg_match('!' . $this->_qstr_regexp . '!', $output, $match)) {
$dbq_string = $match[0]; $output = str_replace($match[0], $this->_expand_quoted_text($match[0]), $output);
$this->_parse_vars_props($match);
$output = str_replace($dbq_string, $match[0], $output);
} }
$this->_parse_modifiers($output, $modifiers); $this->_parse_modifiers($output, $modifiers);