Files
smarty/libs/Smarty.class.php

1005 lines
28 KiB
PHP
Raw Normal View History

2000-08-08 17:05:38 +00:00
<?
/*
* Project: Smarty: the PHP compiled template engine
* File: Smarty.class.php
* Author: Monte Ohrt <monte@ispi.net>
2000-11-17 21:47:41 +00:00
* original idea and implementation
*
* Andrei Zmievski <andrei@ispi.net>
2000-11-20 23:08:17 +00:00
* parsing engine rewrite and a lot more
*
2000-08-08 17:05:38 +00:00
*
*/
2000-11-17 21:47:41 +00:00
require("Smarty.addons.php");
2000-08-08 17:05:38 +00:00
class Smarty
{
// public vars
var $compile_check = true; // whether to check for compiling step or not:
// This is generally set to false once the
// application is entered into production and
// initially compiled. Leave set to true
// during development.
2000-11-15 17:08:52 +00:00
var $template_dir = "templates"; // name of directory for templates
2000-08-08 17:05:38 +00:00
var $compile_dir_ext = "_c"; // the directory extention where
// compiled templates are placed
var $tpl_file_ext = ".tpl"; // template file extentions
var $max_recursion_depth = 10; // maximum recursion depth.
// this is to help catch infinite loops.
// 0 == unlimited recursion.
var $allow_php = false; // whether or not to allow embedded php
// in the templates. By default, php tags
// are escaped.
var $left_delimiter = "{"; // template tag delimiters.
var $right_delimiter = "}";
2000-11-27 17:39:40 +00:00
var $config_dir = "./configs"; // directory where config files are located
2000-11-20 20:06:06 +00:00
2001-01-02 14:51:02 +00:00
var $custom_funcs = array( 'html_options' => 'smarty_func_html_options',
2000-12-04 21:48:51 +00:00
'html_select_date' => 'smarty_func_html_select_date'
2000-11-21 20:29:55 +00:00
);
2000-08-08 17:05:38 +00:00
2001-01-02 14:51:02 +00:00
var $custom_mods = array( 'lower' => 'strtolower',
2000-11-20 22:31:38 +00:00
'upper' => 'strtoupper',
'capitalize' => 'ucwords',
'escape' => 'smarty_mod_escape',
'truncate' => 'smarty_mod_truncate',
2000-12-04 21:48:51 +00:00
'spacify' => 'smarty_mod_spacify',
'date_format' => 'smarty_mod_date_format',
2000-12-18 21:40:21 +00:00
'string_format' => 'smarty_mod_string_format',
2000-12-27 19:58:05 +00:00
'replace' => 'smarty_mod_replace',
'strip_tags' => 'smarty_mod_strip_tags'
2000-11-17 21:47:41 +00:00
);
2000-11-22 16:23:19 +00:00
var $global_assign = array( 'SCRIPT_NAME'
);
2001-01-02 14:51:02 +00:00
2000-08-08 17:05:38 +00:00
// internal vars
2000-11-20 22:31:38 +00:00
var $_error_msg = false; // error messages
2000-08-08 17:05:38 +00:00
var $_tpl_vars = array();
2000-11-20 03:20:55 +00:00
var $_sectionelse_stack = array(); // keeps track of whether section had 'else' part
2000-11-27 17:39:40 +00:00
var $_literal_blocks = array(); // keeps literal template blocks
2000-08-08 17:05:38 +00:00
2000-11-22 16:23:19 +00:00
/*======================================================================*\
Function: Smarty
Purpose: Constructor
\*======================================================================*/
function Smarty()
{
foreach ($this->global_assign as $var_name)
$this->assign($var_name, $GLOBALS[$var_name]);
}
2000-08-08 17:05:38 +00:00
/*======================================================================*\
Function: assign()
2000-11-21 15:21:16 +00:00
Purpose: assigns values to template variables
2000-08-08 17:05:38 +00:00
\*======================================================================*/
function assign($tpl_var, $value = NULL)
{
2000-11-20 20:06:06 +00:00
if (is_array($tpl_var)){
foreach ($tpl_var as $key => $val) {
if (!empty($key))
$this->_tpl_vars[$key] = $val;
2000-08-08 17:05:38 +00:00
}
2000-11-20 20:06:06 +00:00
} else {
if (!empty($tpl_var) && isset($value))
$this->_tpl_vars[$tpl_var] = $value;
2000-08-08 17:05:38 +00:00
}
}
2000-11-21 15:21:16 +00:00
/*======================================================================*\
Function: append
Purpose: appens values to template variables
\*======================================================================*/
function append($tpl_var, $value = NULL)
{
if (is_array($tpl_var)) {
foreach ($tpl_var as $key => $val) {
if (!empty($key)) {
if (!is_array($this->_tpl_vars[$key]))
settype($this->_tpl_vars[$key], 'array');
$this->_tpl_vars[$key][] = $val;
}
}
} else {
if (!empty($tpl_var) && isset($value)) {
if (!is_array($this->_tpl_vars[$tpl_var]))
settype($this->_tpl_vars[$tpl_var], 'array');
$this->_tpl_vars[$tpl_var][] = $value;
}
}
}
2000-08-08 17:05:38 +00:00
/*======================================================================*\
Function: clear_assign()
Purpose: clear the given assigned template variable.
\*======================================================================*/
function clear_assign($tpl_var)
{
unset($this->_tpl_vars[$tpl_var]);
}
/*======================================================================*\
Function: clear_all_assign()
Purpose: clear all the assigned template variables.
\*======================================================================*/
function clear_all_assign()
{
2000-11-21 15:21:16 +00:00
$this->_tpl_vars = array();
2000-08-08 17:05:38 +00:00
}
2000-11-21 15:21:16 +00:00
/*======================================================================*\
Function: get_template_vars
Purpose: Returns an array containing template variables
\*======================================================================*/
function &get_template_vars()
{
return $this->_tpl_vars;
}
2000-08-08 17:05:38 +00:00
/*======================================================================*\
2000-11-20 20:06:06 +00:00
Function: display()
Purpose: executes & displays the template results
2000-08-08 17:05:38 +00:00
\*======================================================================*/
2000-11-20 20:06:06 +00:00
function display($tpl_file)
2000-08-08 17:05:38 +00:00
{
if(preg_match("/^(.+)\/([^\/]+)$/",$tpl_file,$match))
{
// compile files
$this->_compile($match[1]);
//assemble compile directory path to file
2000-11-22 16:23:19 +00:00
$_compile_file = preg_replace("/([\.\/]*[^\/]+)(.*)/","\\1".preg_quote($this->compile_dir_ext,"/")."\\2",$tpl_file);
2000-08-08 17:05:38 +00:00
extract($this->_tpl_vars);
2000-11-22 16:23:19 +00:00
include($_compile_file);
2000-08-08 17:05:38 +00:00
}
2000-12-18 15:34:29 +00:00
}
2000-08-08 17:05:38 +00:00
/*======================================================================*\
Function: fetch()
Purpose: executes & returns the template results
\*======================================================================*/
function fetch($tpl_file)
{
ob_start();
2000-12-07 22:47:01 +00:00
$this->display($tpl_file);
2000-08-08 17:05:38 +00:00
$results = ob_get_contents();
ob_end_clean();
return $results;
}
/*======================================================================*\
Function: compile()
Purpose: called to compile the templates
\*======================================================================*/
function _compile($tpl_dir)
{
if($this->compile_check)
{
if($this->_traverse_files($tpl_dir,0))
return true;
else
return false;
}
else
return false;
}
/*======================================================================*\
Function: _traverse_files()
Purpose: traverse the template files & process each one
\*======================================================================*/
function _traverse_files($tpl_dir,$depth)
{
// exit if recursion depth is met
if($this->max_recursion_depth != 0 && $depth >= $this->max_recursion_depth)
{
2000-11-20 22:31:38 +00:00
$this->_set_error_msg("recursion depth of $depth reached on $tpl_dir/$curr_file. exiting.");
2000-08-08 17:05:38 +00:00
return false;
}
if(is_dir($tpl_dir))
{
if($tpl_dir)
$dir_handle = opendir($tpl_dir);
while($curr_file = readdir($dir_handle))
{
if(!preg_match("/".preg_quote($this->tpl_file_ext,"/")."$/",$curr_file))
{
//echo "skipping $curr_file<br>\n";
continue;
}
$filepath = $tpl_dir."/".$curr_file;
//echo "filepath is $filepath<br>\n";
if(is_readable($filepath))
{
if(is_file($filepath))
{
//echo "is file.<br>\n";
//echo $filepath, $depth<br>\n";
if(!$this->_process_file($filepath))
return false;
}
elseif(is_dir($filepath))
{
//echo "is directory.<br>\n";
if(!$this->_traverse_files($filepath,$depth+1))
return false;
}
else
{
// invalid file type, skipping
2000-11-20 22:31:38 +00:00
$this->_set_error_msg("Invalid filetype for $filepath, skipping");
2000-08-08 17:05:38 +00:00
continue;
}
}
}
}
else
{
2000-11-20 22:31:38 +00:00
$this->_set_error_msg("Directory \"$tpl_dir\" does not exist or is not a directory.");
2000-08-08 17:05:38 +00:00
return false;
}
return true;
}
/*======================================================================*\
Function: _process_file()
Input: test template files for modifications
and execute the compilation for each
one requiring it.
\*======================================================================*/
function _process_file($filepath)
{
if(preg_match("/^(.+)\/([^\/]+)$/",$filepath,$match))
{
$tpl_file_dir = $match[1];
$tpl_file_name = $match[2];
//assemble compile directory path
$compile_dir = preg_replace("/([\.\/]*[^\/]+)(.*)/","\\1".preg_quote($this->compile_dir_ext,"/")."\\2",$match[1]);
//echo "compile dir: $compile_dir<br>\n";
//create directory if none exists
2000-11-27 17:39:40 +00:00
if(!file_exists($compile_dir)) {
$compile_dir_parts = preg_split('!/+!', $compile_dir);
$new_dir = "";
foreach ($compile_dir_parts as $dir_part) {
$new_dir .= $dir_part."/";
if (!file_exists($new_dir) && !mkdir($new_dir, 0755)) {
$this->_set_error_msg("problem creating directory \"$compile_dir\"");
return false;
}
2000-08-08 17:05:38 +00:00
}
2000-11-27 17:39:40 +00:00
}
2000-08-08 17:05:38 +00:00
// compile the template file if none exists or has been modified
2000-11-27 17:39:40 +00:00
if(!file_exists($compile_dir."/".$tpl_file_name) ||
2000-08-08 17:05:38 +00:00
($this->_modified_file($filepath,$compile_dir."/".$tpl_file_name)))
{
if(!$this->_compile_file($filepath,$compile_dir."/".$tpl_file_name))
return false;
}
else
{
// no compilation needed
return true;
}
}
else
{
2000-11-20 22:31:38 +00:00
$this->_set_error_msg("problem matching \"$filepath.\"");
2000-08-08 17:05:38 +00:00
return false;
}
return true;
}
/*======================================================================*\
Function: _modified_file()
Input: return comparison of modification times of files
\*======================================================================*/
function _modified_file($filepath,$compilepath)
{
if(filemtime($filepath) >= filemtime($compilepath))
return true;
return false;
}
/*======================================================================*\
Function: _compile_file()
Input: compile a template file
\*======================================================================*/
function _compile_file($filepath,$compilepath)
{
if(!($template_contents = $this->_read_file($filepath)))
return false;
2000-11-17 21:47:41 +00:00
$ldq = preg_quote($this->left_delimiter, "/");
$rdq = preg_quote($this->right_delimiter, "/");
2000-08-08 21:10:12 +00:00
2000-11-27 17:39:40 +00:00
/* Pull out the literal blocks. */
preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_contents, $match);
$this->_literal_blocks = $match[1];
$template_contents = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s",
'{literal}', $template_contents);
2000-11-17 21:47:41 +00:00
/* Gather all template tags. */
preg_match_all("/$ldq\s*(.*?)\s*$rdq/s", $template_contents, $match);
$template_tags = $match[1];
/* Split content by template tags to obtain non-template content. */
$text_blocks = preg_split("/$ldq.*?$rdq/s", $template_contents);
2000-11-21 20:29:55 +00:00
if(!$this->allow_php) {
/* Escape php tags. */
$text_blocks = preg_replace('!<\?([^?]*?)\?>!', '&lt;?$1?&gt;', $text_blocks);
}
2000-11-17 21:47:41 +00:00
$compiled_tags = array();
2000-11-20 23:08:17 +00:00
foreach ($template_tags as $template_tag)
2000-11-17 21:47:41 +00:00
$compiled_tags[] = $this->_compile_tag($template_tag);
2000-08-08 17:05:38 +00:00
2000-11-17 21:47:41 +00:00
for ($i = 0; $i < count($compiled_tags); $i++) {
$compiled_contents .= $text_blocks[$i].$compiled_tags[$i];
}
$compiled_contents .= $text_blocks[$i];
2000-08-08 17:05:38 +00:00
2000-11-20 23:08:17 +00:00
/* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
2000-11-21 15:21:16 +00:00
if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_contents, $match)) {
$strip_tags = $match[0];
2000-12-04 21:48:51 +00:00
$strip_tags_modified = preg_replace("!$ldq/?strip$rdq|[\t ]+$|^[\t ]+!m", '', $strip_tags);
2001-01-04 21:39:51 +00:00
$strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
2000-11-21 15:21:16 +00:00
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);
}
2000-11-20 23:08:17 +00:00
if(!$this->_write_file($compilepath, $compiled_contents))
2000-11-17 21:47:41 +00:00
return false;
return true;
}
function _compile_tag($template_tag)
{
/* Matched comment. */
2000-11-17 23:14:51 +00:00
if ($template_tag{0} == '*' && $template_tag{strlen($tokens)-1} == '*')
2000-11-17 21:47:41 +00:00
return "";
2000-11-17 23:14:51 +00:00
/* Split tag into two parts: command and the arguments. */
2000-12-04 22:30:15 +00:00
preg_match('/^(
(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | (?>[^"\' ]+))+
)
(?:\s+(.*))?
2001-01-09 17:50:51 +00:00
/xs', $template_tag, $match);
2000-12-04 22:30:15 +00:00
list(, $tag_command, $tag_args) = $match;
2000-11-17 23:14:51 +00:00
/* If the tag name matches a variable or section property definition,
we simply process it. */
2000-12-07 22:47:01 +00:00
if (preg_match('!^\$(\w+/)*\w+(?>\|@?\w+(:[^|]+)?)*$!', $tag_command) || // if a variable
preg_match('!^#(\w+)#(?>\|@?\w+(:[^|]+)?)*$!', $tag_command) || // or a configuration variable
preg_match('!^%\w+\.\w+%(?>\|@?\w+(:[^|]+)?)*$!', $tag_command)) { // or a section property
2000-11-17 23:14:51 +00:00
settype($tag_command, 'array');
$this->_parse_vars_props($tag_command);
2000-11-20 03:20:55 +00:00
return "<?php print $tag_command[0]; ?>";
2000-11-17 21:47:41 +00:00
}
2000-11-17 23:14:51 +00:00
switch ($tag_command) {
2000-11-17 21:47:41 +00:00
case 'include':
2000-11-20 20:06:06 +00:00
return $this->_compile_include_tag($tag_args);
2000-11-17 21:47:41 +00:00
case 'if':
2000-11-17 23:14:51 +00:00
return $this->_compile_if_tag($tag_args);
2000-08-08 17:05:38 +00:00
2000-11-17 21:47:41 +00:00
case 'else':
return '<?php else: ?>';
2000-08-08 17:05:38 +00:00
2000-12-04 21:48:51 +00:00
case 'elseif':
return $this->_compile_if_tag($tag_args, true);
2000-11-17 21:47:41 +00:00
case '/if':
return '<?php endif; ?>';
case 'ldelim':
return $this->left_delimiter;
case 'rdelim':
return $this->right_delimiter;
case 'section':
2000-11-20 03:20:55 +00:00
array_push($this->_sectionelse_stack, false);
2000-11-17 23:14:51 +00:00
return $this->_compile_section_start($tag_args);
2000-11-17 21:47:41 +00:00
2000-11-20 03:20:55 +00:00
case 'sectionelse':
$this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
return "<?php endfor; else: ?>";
2000-11-17 21:47:41 +00:00
case '/section':
2000-11-20 03:20:55 +00:00
if (array_pop($this->_sectionelse_stack))
return "<?php endif; ?>";
else
return "<?php endfor; endif; ?>";
2000-11-17 21:47:41 +00:00
2000-11-20 20:06:06 +00:00
case 'config_load':
return $this->_compile_config_load_tag($tag_args);
2000-11-20 23:08:17 +00:00
case 'strip':
case '/strip':
return $this->left_delimiter.$tag_command.$this->right_delimiter;
2000-11-27 17:39:40 +00:00
case 'literal':
list (,$literal_block) = each($this->_literal_blocks);
return $literal_block;
case 'insert':
return $this->_compile_insert_tag($tag_args);
2000-11-17 21:47:41 +00:00
default:
2001-01-02 14:51:02 +00:00
if (isset($this->custom_funcs[$tag_command])) {
2000-11-21 21:54:26 +00:00
return $this->_compile_custom_tag($tag_command, $tag_args);
2000-11-21 20:29:55 +00:00
} else
/* TODO syntax error: unknown tag */
return "";
2000-11-17 21:47:41 +00:00
}
}
2000-11-21 20:29:55 +00:00
2000-11-21 21:54:26 +00:00
function _compile_custom_tag($tag_command, $tag_args)
2000-11-21 20:29:55 +00:00
{
$attrs = $this->_parse_attrs($tag_args);
2001-01-02 14:51:02 +00:00
$function = $this->custom_funcs[$tag_command];
2000-12-04 21:48:51 +00:00
foreach ($attrs as $arg_name => $arg_value) {
if (is_bool($arg_value))
$arg_value = $arg_value ? 'true' : 'false';
2000-11-21 20:29:55 +00:00
$arg_list[] = "'$arg_name' => $arg_value";
2000-12-04 21:48:51 +00:00
}
2000-11-21 20:29:55 +00:00
2000-12-04 21:48:51 +00:00
return "<?php $function(array(".implode(',', (array)$arg_list).")); ?>";
2000-11-21 20:29:55 +00:00
}
function _compile_insert_tag($tag_args)
{
$attrs = $this->_parse_attrs($tag_args);
$name = substr($attrs['name'], 1, -1);
if (empty($name)) {
/* TODO syntax error: missing insert name */
}
foreach ($attrs as $arg_name => $arg_value) {
if ($arg_name == 'name') continue;
if (is_bool($arg_value))
$arg_value = $arg_value ? 'true' : 'false';
$arg_list[] = "'$arg_name' => $arg_value";
}
return "<?php print insert_$name(array(".implode(',', (array)$arg_list).")); ?>";
}
2000-11-20 20:06:06 +00:00
function _compile_config_load_tag($tag_args)
{
$attrs = $this->_parse_attrs($tag_args);
if (empty($attrs['file'])) {
/* TODO syntax error: missing 'file' attribute */
}
2000-11-27 17:39:40 +00:00
$output = '<?php include_once "Config_File.php";'."\n";
$output .= 'if (!is_object($_conf_obj) || get_class($_conf_obj) != "config_file") {'."\n";
$output .= ' $_conf_obj = new Config_File("'.$this->config_dir.'");'."\n";
$output .= '}'."\n";
$output .= '$_config = array_merge((array)$_config, $_conf_obj->get('.$attrs['file'].'));'."\n";
if (!empty($attrs['section']))
$output .= '$_config = array_merge((array)$_config, $_conf_obj->get('.$attrs['file'].', '.$attrs['section'].')); ';
2000-11-20 20:06:06 +00:00
2000-11-27 17:39:40 +00:00
$output .= '?>';
2000-11-20 20:06:06 +00:00
return $output;
}
function _compile_include_tag($tag_args)
{
$attrs = $this->_parse_attrs($tag_args);
if (empty($attrs['file'])) {
/* TODO syntax error: missing 'file' attribute */
2000-11-27 17:39:40 +00:00
} else
$attrs['file'] = $this->_dequote($attrs['file']);
if (count($attrs) > 1) {
$include_func_name = uniqid("_include_");
$include_file_name = $this->template_dir.$this->compile_dir_ext.'/'.$attrs['file'];
foreach ($attrs as $arg_name => $arg_value) {
if ($arg_name == 'file') continue;
if (is_bool($arg_value))
$arg_value = $arg_value ? 'true' : 'false';
$arg_list[] = "'$arg_name' => $arg_value";
}
return "<?php\n" .
2000-12-27 19:58:05 +00:00
"function $include_func_name(\$file_name, \$def_vars, \$include_vars)\n" .
"{\n" .
2000-12-27 19:58:05 +00:00
" extract(\$def_vars);\n" .
" extract(\$include_vars);\n" .
" include \"\$file_name\";\n" .
"}\n" .
2000-12-27 19:58:05 +00:00
"$include_func_name(\"$include_file_name\", get_defined_vars(), array(".implode(',', (array)$arg_list)."));\n?>\n";
} else
return '<?php include "'.$this->template_dir.$this->compile_dir_ext.'/'.$attrs['file'].'"; ?>';
2000-11-20 20:06:06 +00:00
}
2000-12-04 21:48:51 +00:00
function _compile_section_start($tag_args)
2000-11-17 21:47:41 +00:00
{
2000-12-04 21:48:51 +00:00
$attrs = $this->_parse_attrs($tag_args);
2000-11-20 03:20:55 +00:00
$output = "<?php\n";
$section_name = $attrs['name'];
if (empty($section_name)) {
/* TODO syntax error: section needs a name */
}
2000-11-27 17:39:40 +00:00
$output .= "unset(\$_sections[$section_name]);\n";
$section_props = "\$_sections[$section_name]['properties']";
2000-11-20 03:20:55 +00:00
foreach ($attrs as $attr_name => $attr_value) {
switch ($attr_name) {
case 'loop':
2000-12-04 21:48:51 +00:00
$output .= "{$section_props}['loop'] = count($attr_value);\n";
2000-11-20 03:20:55 +00:00
break;
2000-12-04 22:39:48 +00:00
case 'show':
if (is_bool($attr_value))
$attr_value = $attr_value ? 'true' : 'false';
$output .= "{$section_props}['$attr_name'] = $attr_value;\n";
break;
2000-11-20 03:20:55 +00:00
default:
$output .= "{$section_props}['$attr_name'] = $attr_value;\n";
break;
}
}
if (isset($attrs['loop'])) {
2000-12-13 15:08:25 +00:00
$loop_check_code = "{$section_props}['loop'] > 0 && ";
2000-11-20 03:20:55 +00:00
} else {
$output .= "{$section_props}['loop'] = 1;\n";
}
if (isset($attrs['show'])) {
$show_check_code = "{$section_props}['show'] && ";
2001-01-09 17:50:51 +00:00
} else {
$output .= "{$section_props}['show'] = {$section_props}['loop'] > 0;\n";
2000-11-20 03:20:55 +00:00
}
$output .= "if ($loop_check_code $show_check_code true): ";
$output .= "
for ({$section_props}['index'] = 0;
{$section_props}['index'] < {$section_props}['loop'];
{$section_props}['index']++):\n";
2000-12-27 19:58:05 +00:00
$output .= "{$section_props}['rownum'] = {$section_props}['index'] + 1;\n";
2000-11-20 03:20:55 +00:00
$output .= "?>\n";
return $output;
2000-11-17 23:14:51 +00:00
}
2000-12-04 21:48:51 +00:00
function _compile_if_tag($tag_args, $elseif = false)
2000-11-17 23:14:51 +00:00
{
/* Tokenize args for 'if' tag. */
preg_match_all('/(?:
"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | # match all double quoted strings allowed escaped double quotes
\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | # match all single quoted strings allowed escaped single quotes
[()] | # match parentheses
[^"\'\s()]+ # match any other token that is not any of the above
)/x', $tag_args, $match);
$tokens = $match[0];
2000-11-17 21:47:41 +00:00
$this->_parse_vars_props($tokens);
$is_arg_stack = array();
2000-11-15 17:08:52 +00:00
2000-11-17 21:47:41 +00:00
for ($i = 0; $i < count($tokens); $i++) {
$token = &$tokens[$i];
switch ($token) {
case 'eq':
$token = '==';
break;
case 'ne':
case 'neq':
$token = '!=';
break;
case 'lt':
$token = '<';
break;
case 'le':
case 'lte':
$token = '<=';
break;
case 'gt':
$token = '>';
break;
2000-11-15 17:08:52 +00:00
2000-11-17 21:47:41 +00:00
case 'ge':
case 'gte':
$token = '>=';
break;
case 'and':
$token = '&&';
break;
case 'or':
$token = '||';
break;
case 'not':
$token = '!';
break;
case 'mod':
$token = '%';
break;
case '(':
array_push($is_arg_stack, $i);
break;
case 'is':
/* If last token was a ')', we operate on the parenthesized
expression. The start of the expression is on the stack.
Otherwise, we operate on the last encountered token. */
if ($tokens[$i-1] == ')')
$is_arg_start = array_pop($is_arg_stack);
else
$is_arg_start = $i-1;
/* Construct the argument for 'is' expression, so it knows
what to operate on. */
$is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
/* Pass all tokens from next one until the end to the
'is' expression parsing function. The function will
return modified tokens, where the first one is the result
of the 'is' expression and the rest are the tokens it
didn't touch. */
$new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
/* Replace the old tokens with the new ones. */
array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
/* Adjust argument start so that it won't change from the
current position for the next iteration. */
$i = $is_arg_start;
break;
}
}
2000-11-15 17:08:52 +00:00
2000-12-04 21:48:51 +00:00
if ($elseif)
return '<?php elseif ('.implode(' ', $tokens).'): ?>';
else
return '<?php if ('.implode(' ', $tokens).'): ?>';
2000-11-17 21:47:41 +00:00
}
function _parse_is_expr($is_arg, $tokens)
{
$expr_end = 0;
if (($first_token = array_shift($tokens)) == 'not') {
$negate_expr = true;
$expr_type = array_shift($tokens);
} else
$expr_type = $first_token;
switch ($expr_type) {
case 'even':
if ($tokens[$expr_end] == 'by') {
$expr_end++;
$expr_arg = $tokens[$expr_end++];
2000-11-20 20:06:06 +00:00
$expr = "!(($is_arg / $expr_arg) % $expr_arg)";
2000-11-17 21:47:41 +00:00
}
else
$expr = "!($is_arg % 2)";
break;
case 'odd':
if ($tokens[$expr_end] == 'by') {
$expr_end++;
$expr_arg = $tokens[$expr_end++];
2000-11-20 20:06:06 +00:00
$expr = "(($is_arg / $expr_arg) % $expr_arg)";
2000-11-17 21:47:41 +00:00
}
else
$expr = "($is_arg % 2)";
break;
case 'mod':
$expr_arg = $tokens[$expr_end++];
$expr = "!($is_arg % $expr_arg)";
break;
default:
/* TODO strict syntax checking */
break;
}
if ($negate_expr) {
$expr = "!($expr)";
}
array_splice($tokens, 0, $expr_end, $expr);
return $tokens;
}
2000-11-17 23:14:51 +00:00
function _parse_attrs($tag_args)
{
/* Tokenize tag attributes. */
2000-12-04 21:48:51 +00:00
preg_match_all('/(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" |
\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | (?>[^"\'= ]+)
)+ |
[=]
/x', $tag_args, $match);
2000-11-17 23:14:51 +00:00
$tokens = $match[0];
2000-12-04 21:51:45 +00:00
$var_delims = array('$', '#', '%');
2000-11-17 23:14:51 +00:00
$attrs = array();
/* Parse state:
0 - expecting attr name
1 - expecting '=' or another attr name
2 - expecting attr value (not '=') */
$state = 0;
2000-11-20 03:20:55 +00:00
foreach ($tokens as $token) {
2000-11-17 23:14:51 +00:00
switch ($state) {
case 0:
/* If the token is a valid identifier, we set attribute name
and go to state 1. */
if (preg_match('!\w+!', $token)) {
$attr_name = $token;
$state = 1;
} else
/* TODO syntax error: invalid attr name */;
break;
case 1:
/* If the token is a valid identifier, the previously set
attribute name does not need an argument. We put it in
the attrs array, set the new attribute name to the
current token and don't switch state.
If the token is '=', then we go to state 2. */
2000-12-04 21:48:51 +00:00
if ($token == '=') {
2000-11-17 23:14:51 +00:00
$state = 2;
} else
2000-12-04 21:48:51 +00:00
/* TODO syntax error: expecting '=' */;
2000-11-17 23:14:51 +00:00
break;
case 2:
/* If token is not '=', we set the attribute value and go to
state 0. */
if ($token != '=') {
2000-12-04 21:48:51 +00:00
/* 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. */
2000-12-04 21:51:45 +00:00
else if (!in_array($token{0}, $var_delims) &&
2000-12-04 21:48:51 +00:00
!(($token{0} == '"' || $token[0] == "'") &&
$token{strlen($token)-1} == $token{0}))
2000-11-27 17:39:40 +00:00
$token = "'".$token."'";
2000-12-04 21:48:51 +00:00
2000-11-17 23:14:51 +00:00
$attrs[$attr_name] = $token;
$state = 0;
} else
/* TODO syntax error: '=' can't be a value */;
break;
}
}
2000-12-04 21:48:51 +00:00
$this->_parse_vars_props($attrs);
2000-11-17 23:14:51 +00:00
return $attrs;
}
2000-11-17 21:47:41 +00:00
function _parse_vars_props(&$tokens)
{
2000-12-07 22:47:01 +00:00
$var_exprs = preg_grep('!^\$(\w+/)*\w+(?>\|@?\w+(:[^|]+)?)*$!', $tokens);
$conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:[^|]+)?)*$!', $tokens);
$sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:[^|]+)?)*$!', $tokens);
2000-11-17 21:47:41 +00:00
if (count($var_exprs)) {
foreach ($var_exprs as $expr_index => $var_expr) {
$tokens[$expr_index] = $this->_parse_var($var_expr);
}
}
2000-11-20 20:06:06 +00:00
if (count($conf_var_exprs)) {
foreach ($conf_var_exprs as $expr_index => $var_expr) {
$tokens[$expr_index] = $this->_parse_conf_var($var_expr);
}
}
2000-11-17 21:47:41 +00:00
if (count($sect_prop_exprs)) {
2000-11-20 03:20:55 +00:00
foreach ($sect_prop_exprs as $expr_index => $section_prop_expr) {
2000-11-17 21:47:41 +00:00
$tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr);
}
}
2000-08-08 17:05:38 +00:00
}
2000-11-17 21:47:41 +00:00
function _parse_var($var_expr)
{
2001-01-09 17:50:51 +00:00
$modifiers = explode('|', substr($var_expr, 1));
2000-11-17 21:47:41 +00:00
2001-01-09 17:50:51 +00:00
$sections = explode('/', array_shift($modifiers));
2000-11-17 21:47:41 +00:00
$var_name = array_pop($sections);
$output = "\$$var_name";
foreach ($sections as $section) {
$output .= "[\$_sections['$section']['properties']['index']]";
}
2001-01-09 17:50:51 +00:00
$this->_parse_modifiers($output, $modifiers);
2000-11-17 21:47:41 +00:00
return $output;
}
2000-11-20 20:06:06 +00:00
function _parse_conf_var($conf_var_expr)
{
2001-01-09 17:50:51 +00:00
$modifiers = explode('|', $conf_var_expr);
2000-11-20 20:06:06 +00:00
2001-01-09 17:50:51 +00:00
$var_name = substr(array_shift($modifiers), 1, -1);
2000-11-20 20:06:06 +00:00
$output = "\$_config['$var_name']";
2001-01-09 17:50:51 +00:00
$this->_parse_modifiers($output, $modifiers);
2000-11-20 20:06:06 +00:00
return $output;
}
2000-11-17 21:47:41 +00:00
function _parse_section_prop($section_prop_expr)
{
2001-01-09 17:50:51 +00:00
$modifiers = explode('|', $section_prop_expr);
2000-11-17 21:47:41 +00:00
2001-01-09 17:50:51 +00:00
preg_match('!%(\w+)\.(\w+)%!', array_shift($modifiers), $match);
2000-11-17 21:47:41 +00:00
$section_name = $match[1];
$prop_name = $match[2];
$output = "\$_sections['$section_name']['properties']['$prop_name']";
2001-01-09 17:50:51 +00:00
$this->_parse_modifiers($output, $modifiers);
2000-11-17 21:47:41 +00:00
return $output;
}
2001-01-09 17:50:51 +00:00
function _parse_modifiers(&$output, $modifiers)
2000-11-17 21:47:41 +00:00
{
2001-01-09 17:50:51 +00:00
foreach ($modifiers as $modifier) {
2000-11-17 21:47:41 +00:00
$modifier = explode(':', $modifier);
$modifier_name = array_shift($modifier);
2000-12-07 22:47:01 +00:00
if ($modifier_name{0} == '@') {
$map_array = 'false';
$modifier_name = substr($modifier_name, 1);
} else
$map_array = 'true';
2000-11-17 21:47:41 +00:00
/*
* First we lookup the modifier function name in the registered
* modifiers table.
*/
2001-01-02 14:51:02 +00:00
$mod_func_name = $this->custom_mods[$modifier_name];
2000-11-17 21:47:41 +00:00
/*
* If we don't find that modifier there, we assume it's just a PHP
* function name.
*/
/* TODO strict syntax check */
if (!isset($mod_func_name))
$mod_func_name = $modifier_name;
2000-11-20 20:06:06 +00:00
$this->_parse_vars_props($modifier);
2000-11-17 21:47:41 +00:00
if (count($modifier) > 0)
2000-11-27 17:39:40 +00:00
$modifier_args = ', '.implode(', ', $modifier);
2000-11-17 21:47:41 +00:00
else
2000-11-27 17:39:40 +00:00
$modifier_args = '';
2000-11-17 21:47:41 +00:00
2000-12-07 22:47:01 +00:00
$output = "_smarty_mod_handler('$mod_func_name', $map_array, $output$modifier_args)";
2000-11-17 21:47:41 +00:00
}
}
2000-11-27 17:39:40 +00:00
/*======================================================================*\
Function: _dequote
Purpose: Remove starting and ending quotes from the string
\*======================================================================*/
function _dequote($string)
{
if (($string{0} == "'" || $string{0} == '"') &&
$string{strlen($string)-1} == $string{0})
return substr($string, 1, -1);
}
2000-08-08 17:05:38 +00:00
/*======================================================================*\
Function: _read_file()
Purpose: read in a file
\*======================================================================*/
function _read_file($filename)
{
if(! ($fd = fopen($filename,"r")))
{
2000-11-20 22:31:38 +00:00
$this->_set_error_msg("problem reading \"$filename.\"");
2000-08-08 17:05:38 +00:00
return false;
}
$contents = fread($fd,filesize($filename));
fclose($fd);
return $contents;
}
/*======================================================================*\
Function: _write_file()
Purpose: write out a file
\*======================================================================*/
function _write_file($filename,$contents)
{
if(!($fd = fopen($filename,"w")))
{
2000-11-20 22:31:38 +00:00
$this->_set_error_msg("problem writing \"$filename.\"");
2000-08-08 17:05:38 +00:00
return false;
}
fwrite($fd,$contents);
fclose($fd);
return true;
}
/*======================================================================*\
2000-11-20 22:31:38 +00:00
Function: _set_error_msg()
2000-08-08 17:05:38 +00:00
Purpose: set the error message
\*======================================================================*/
2000-11-20 22:31:38 +00:00
function _set_error_msg($error_msg)
2000-08-08 17:05:38 +00:00
{
2000-11-20 22:31:38 +00:00
$this->_error_msg="smarty error: $error_msg";
2000-08-08 17:05:38 +00:00
return true;
}
}
?>