diff --git a/Smarty.addons.php b/Smarty.addons.php index 61cdfab6..c5557ab9 100644 --- a/Smarty.addons.php +++ b/Smarty.addons.php @@ -8,6 +8,26 @@ * */ +/*============================================*\ + Modifiers +\*============================================*/ + +function _smarty_mod_handler() +{ + $args = func_get_args(); + $func_name = array_shift($args); + $var = $args[0]; + + if (is_array($var)) { + foreach ($var as $key => $val) { + $args[0] = $val; + $var[$key] = call_user_func_array($func_name, $args); + } + return $var; + } else + return call_user_func_array($func_name, $args); +} + /*======================================================================*\ Function: smarty_mod_escape @@ -55,17 +75,31 @@ function smarty_mod_spacify($string, $spacify_char = ' ') } +function smarty_mod_date_format($string, $format) +{ + return strftime($format, $string); +} + + +function smarty_mod_replace($string, $search, $replace) +{ + return str_replace($search, $replace, $string); +} + + /*============================================*\ Custom tag functions \*============================================*/ /*======================================================================*\ Function: smarty_func_html_options - Purpose: Returns the list of \n"; } + if ($print_result) + print $html_result; + else + return $html_result; +} + + +/*======================================================================*\ + Function: smarty_func_html_select_date + Purpose: Prints the dropdowns for date selection. +\*======================================================================*/ +function smarty_func_html_select_date() +{ + /* Default values. */ + $prefix = "Date_"; + $time = time(); + $start_year = strftime("%Y"); + $end_year = $start_year; + $display_days = true; + $display_months = true; + $display_years = true; + $month_format = "%B"; + $day_format = "%02d"; + $year_as_text = false; + + extract(func_get_arg(0)); + + $html_result = ""; + + if ($display_months) { + $month_names = array(); + + for ($i = 1; $i <= 12; $i++) + $month_names[] = strftime($month_format, mktime(0, 0, 0, $i, 1, 2000)); + + $html_result .= '\n"; + } + + if ($display_days) { + $days = range(1, 31); + array_walk($days, create_function('&$x', '$x = sprintf("'.$day_format.'", $x);')); + + $html_result .= '\n"; + } + + if ($display_years) { + if ($year_as_text) { + $html_result .= ''; + } else { + $years = range($start_year, $end_year); + + $html_result .= '\n"; + } + } + print $html_result; } diff --git a/Smarty.class.php b/Smarty.class.php index a4540d71..ff74d081 100644 --- a/Smarty.class.php +++ b/Smarty.class.php @@ -41,7 +41,8 @@ class Smarty var $config_dir = "./configs"; // directory where config files are located - var $custom_tags = array( 'html_options' => 'smarty_func_html_options' + var $custom_tags = array( 'html_options' => 'smarty_func_html_options', + 'html_select_date' => 'smarty_func_html_select_date' ); var $modifiers = array( 'lower' => 'strtolower', @@ -49,7 +50,9 @@ class Smarty 'capitalize' => 'ucwords', 'escape' => 'smarty_mod_escape', 'truncate' => 'smarty_mod_truncate', - 'spacify' => 'smarty_mod_spacify' + 'spacify' => 'smarty_mod_spacify', + 'date_format' => 'smarty_mod_date_format', + 'replace' => 'smarty_mod_replace' ); var $global_assign = array( 'SCRIPT_NAME' ); @@ -362,7 +365,8 @@ class Smarty /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */ if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_contents, $match)) { $strip_tags = $match[0]; - $strip_tags_modified = preg_replace("!$ldq/?strip$rdq|[\t ]+$|^[\t ]+|/[\r\n]+!m", '', $strip_tags); + $strip_tags_modified = preg_replace("!$ldq/?strip$rdq|[\t ]+$|^[\t ]+!m", '', $strip_tags); + $strip_tags_modified = preg_replace('![\r\n]+!m', ' ', $strip_tags_modified); for ($i = 0; $i < count($strip_tags); $i++) $compiled_contents = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $strip_tags_modified[$i], $compiled_contents, 1); @@ -403,6 +407,9 @@ class Smarty case 'else': return ''; + case 'elseif': + return $this->_compile_if_tag($tag_args, true); + case '/if': return ''; @@ -451,10 +458,13 @@ class Smarty { $attrs = $this->_parse_attrs($tag_args); $function = $this->custom_tags[$tag_command]; - foreach ($attrs as $arg_name => $arg_value) + foreach ($attrs as $arg_name => $arg_value) { + if (is_bool($arg_value)) + $arg_value = $arg_value ? 'true' : 'false'; $arg_list[] = "'$arg_name' => $arg_value"; + } - return ""; + return ""; } @@ -493,9 +503,9 @@ class Smarty return 'template_dir.$this->compile_dir_ext.'/'.$attrs['file'].'"; ?>'; } - function _compile_section_start($tokens) + function _compile_section_start($tag_args) { - $attrs = $this->_parse_attrs($tokens); + $attrs = $this->_parse_attrs($tag_args); $output = " $attr_value) { switch ($attr_name) { case 'loop': - $output .= " - if (is_array($attr_value)) - {$section_props}['loop'] = count($attr_value); - else - {$section_props}['loop'] = $attr_value;\n"; + $output .= "{$section_props}['loop'] = count($attr_value);\n"; break; default: @@ -544,7 +550,7 @@ class Smarty return $output; } - function _compile_if_tag($tag_args) + function _compile_if_tag($tag_args, $elseif = false) { /* Tokenize args for 'if' tag. */ preg_match_all('/(?: @@ -638,7 +644,10 @@ class Smarty } } - return ''; + if ($elseif) + return ''; + else + return ''; } function _parse_is_expr($is_arg, $tokens) @@ -694,15 +703,13 @@ class Smarty function _parse_attrs($tag_args) { /* Tokenize tag attributes. */ - preg_match_all('/(?: - "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | # match all double quoted strings allowed escaped double quotes - \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | # match all single quoted strings allowed escaped single quotes - [=] | # match equal sign - [^"\'\s=]+ # match any other token that is not any of the above - )/x', $tag_args, $match); + preg_match_all('/(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | + \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | (?>[^"\'= ]+) + )+ | + [=] + /x', $tag_args, $match); $tokens = $match[0]; - $this->_parse_vars_props($tokens); $attrs = array(); /* Parse state: 0 - expecting attr name @@ -729,27 +736,30 @@ class Smarty current token and don't switch state. If the token is '=', then we go to state 2. */ - if (preg_match('!\w+!', $token)) { - $attrs[$attr_name] = ""; - $attr_name = $token; - } else if ($token == '=') { + if ($token == '=') { $state = 2; } else - /* TODO syntax error: expecting attr name or '=' */; + /* TODO syntax error: expecting '=' */; break; case 2: /* If token is not '=', we set the attribute value and go to state 0. */ if ($token != '=') { - /* If the token is not variable (doesn't start with '$') - and not enclosed in single or double quotes - we single-quote it. */ - if ($token{0} != '$' && - !(($token{0} == '"' || $token[0] == "'") && - $token{strlen($token)-1} == $token{0})) { + /* We booleanize the token if it's a non-quoted possible + boolean value. */ + if (preg_match('!^(on|yes|true)$!', $token)) + $token = true; + else if (preg_match('!^(off|no|false)$!', $token)) + $token = false; + /* If the token is not variable (doesn't start with + '$', '#', or '%') and not enclosed in single or + double quotes we single-quote it. */ + else if (!in_array($token{0}, array('$', '#', '%')) && + !(($token{0} == '"' || $token[0] == "'") && + $token{strlen($token)-1} == $token{0})) $token = "'".$token."'"; - } + $attrs[$attr_name] = $token; $state = 0; } else @@ -758,6 +768,8 @@ class Smarty } } + $this->_parse_vars_props($attrs); + return $attrs; } @@ -859,7 +871,7 @@ class Smarty else $modifier_args = ''; - $output = "$mod_func_name($output$modifier_args)"; + $output = "_smarty_mod_handler('$mod_func_name', $output$modifier_args)"; } } diff --git a/demo/templates/index.tpl b/demo/templates/index.tpl index 7d1db15d..762f54d9 100644 --- a/demo/templates/index.tpl +++ b/demo/templates/index.tpl @@ -1,11 +1,11 @@ -{config_load file=test.conf section=my foo} +{config_load file=test.conf section="my foo"} Title: {#title#|capitalize} {$SCRIPT_NAME} {* A simple variable test *} -hello, my name is {$Name} +hello, my name is {$Name|upper} My interests are: {section name=outer loop=$FirstName} @@ -18,4 +18,4 @@ My interests are: none {/section} -{html_options output=$FirstName values=$LastName selected="Case"} +{html_options values=$LastName output=$FirstName|replace:"a":"@"} diff --git a/libs/Smarty.class.php b/libs/Smarty.class.php index a4540d71..ff74d081 100644 --- a/libs/Smarty.class.php +++ b/libs/Smarty.class.php @@ -41,7 +41,8 @@ class Smarty var $config_dir = "./configs"; // directory where config files are located - var $custom_tags = array( 'html_options' => 'smarty_func_html_options' + var $custom_tags = array( 'html_options' => 'smarty_func_html_options', + 'html_select_date' => 'smarty_func_html_select_date' ); var $modifiers = array( 'lower' => 'strtolower', @@ -49,7 +50,9 @@ class Smarty 'capitalize' => 'ucwords', 'escape' => 'smarty_mod_escape', 'truncate' => 'smarty_mod_truncate', - 'spacify' => 'smarty_mod_spacify' + 'spacify' => 'smarty_mod_spacify', + 'date_format' => 'smarty_mod_date_format', + 'replace' => 'smarty_mod_replace' ); var $global_assign = array( 'SCRIPT_NAME' ); @@ -362,7 +365,8 @@ class Smarty /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */ if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_contents, $match)) { $strip_tags = $match[0]; - $strip_tags_modified = preg_replace("!$ldq/?strip$rdq|[\t ]+$|^[\t ]+|/[\r\n]+!m", '', $strip_tags); + $strip_tags_modified = preg_replace("!$ldq/?strip$rdq|[\t ]+$|^[\t ]+!m", '', $strip_tags); + $strip_tags_modified = preg_replace('![\r\n]+!m', ' ', $strip_tags_modified); for ($i = 0; $i < count($strip_tags); $i++) $compiled_contents = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $strip_tags_modified[$i], $compiled_contents, 1); @@ -403,6 +407,9 @@ class Smarty case 'else': return ''; + case 'elseif': + return $this->_compile_if_tag($tag_args, true); + case '/if': return ''; @@ -451,10 +458,13 @@ class Smarty { $attrs = $this->_parse_attrs($tag_args); $function = $this->custom_tags[$tag_command]; - foreach ($attrs as $arg_name => $arg_value) + foreach ($attrs as $arg_name => $arg_value) { + if (is_bool($arg_value)) + $arg_value = $arg_value ? 'true' : 'false'; $arg_list[] = "'$arg_name' => $arg_value"; + } - return ""; + return ""; } @@ -493,9 +503,9 @@ class Smarty return 'template_dir.$this->compile_dir_ext.'/'.$attrs['file'].'"; ?>'; } - function _compile_section_start($tokens) + function _compile_section_start($tag_args) { - $attrs = $this->_parse_attrs($tokens); + $attrs = $this->_parse_attrs($tag_args); $output = " $attr_value) { switch ($attr_name) { case 'loop': - $output .= " - if (is_array($attr_value)) - {$section_props}['loop'] = count($attr_value); - else - {$section_props}['loop'] = $attr_value;\n"; + $output .= "{$section_props}['loop'] = count($attr_value);\n"; break; default: @@ -544,7 +550,7 @@ class Smarty return $output; } - function _compile_if_tag($tag_args) + function _compile_if_tag($tag_args, $elseif = false) { /* Tokenize args for 'if' tag. */ preg_match_all('/(?: @@ -638,7 +644,10 @@ class Smarty } } - return ''; + if ($elseif) + return ''; + else + return ''; } function _parse_is_expr($is_arg, $tokens) @@ -694,15 +703,13 @@ class Smarty function _parse_attrs($tag_args) { /* Tokenize tag attributes. */ - preg_match_all('/(?: - "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | # match all double quoted strings allowed escaped double quotes - \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | # match all single quoted strings allowed escaped single quotes - [=] | # match equal sign - [^"\'\s=]+ # match any other token that is not any of the above - )/x', $tag_args, $match); + preg_match_all('/(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | + \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | (?>[^"\'= ]+) + )+ | + [=] + /x', $tag_args, $match); $tokens = $match[0]; - $this->_parse_vars_props($tokens); $attrs = array(); /* Parse state: 0 - expecting attr name @@ -729,27 +736,30 @@ class Smarty current token and don't switch state. If the token is '=', then we go to state 2. */ - if (preg_match('!\w+!', $token)) { - $attrs[$attr_name] = ""; - $attr_name = $token; - } else if ($token == '=') { + if ($token == '=') { $state = 2; } else - /* TODO syntax error: expecting attr name or '=' */; + /* TODO syntax error: expecting '=' */; break; case 2: /* If token is not '=', we set the attribute value and go to state 0. */ if ($token != '=') { - /* If the token is not variable (doesn't start with '$') - and not enclosed in single or double quotes - we single-quote it. */ - if ($token{0} != '$' && - !(($token{0} == '"' || $token[0] == "'") && - $token{strlen($token)-1} == $token{0})) { + /* We booleanize the token if it's a non-quoted possible + boolean value. */ + if (preg_match('!^(on|yes|true)$!', $token)) + $token = true; + else if (preg_match('!^(off|no|false)$!', $token)) + $token = false; + /* If the token is not variable (doesn't start with + '$', '#', or '%') and not enclosed in single or + double quotes we single-quote it. */ + else if (!in_array($token{0}, array('$', '#', '%')) && + !(($token{0} == '"' || $token[0] == "'") && + $token{strlen($token)-1} == $token{0})) $token = "'".$token."'"; - } + $attrs[$attr_name] = $token; $state = 0; } else @@ -758,6 +768,8 @@ class Smarty } } + $this->_parse_vars_props($attrs); + return $attrs; } @@ -859,7 +871,7 @@ class Smarty else $modifier_args = ''; - $output = "$mod_func_name($output$modifier_args)"; + $output = "_smarty_mod_handler('$mod_func_name', $output$modifier_args)"; } } diff --git a/templates/index.tpl b/templates/index.tpl index 7d1db15d..762f54d9 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -1,11 +1,11 @@ -{config_load file=test.conf section=my foo} +{config_load file=test.conf section="my foo"} Title: {#title#|capitalize} {$SCRIPT_NAME} {* A simple variable test *} -hello, my name is {$Name} +hello, my name is {$Name|upper} My interests are: {section name=outer loop=$FirstName} @@ -18,4 +18,4 @@ My interests are: none {/section} -{html_options output=$FirstName values=$LastName selected="Case"} +{html_options values=$LastName output=$FirstName|replace:"a":"@"}