diff --git a/Smarty.class.php b/Smarty.class.php index dc175acd..f284bfd3 100644 --- a/Smarty.class.php +++ b/Smarty.class.php @@ -22,6 +22,9 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * For questions, help, comments, discussion, etc., please join the + * Smarty mailing list. Send a blank e-mail to smarty-subscribe@lists.ispi.net + * * You may contact the authors of Smarty by e-mail at: * monte@ispi.net * andrei@ispi.net @@ -47,6 +50,11 @@ define("SMARTY_PHP_ALLOW",3); class Smarty { +/**************************************************************************/ +/* BEGIN SMARTY CONFIGURATION SECTION */ +/* Set the following config variables to your liking. */ +/**************************************************************************/ + // public vars var $template_dir = './templates'; // name of directory for templates var $compile_dir = './templates_c'; // name of directory for compiled templates @@ -62,17 +70,17 @@ class Smarty // during development. true/false default true. var $force_compile = false; // force templates to compile every time. - // if cache file exists, it will + // if caching is on, a cached file will // override compile_check and force_compile. // true/false. default false. + var $caching = false; // whether to use caching or not. true/false var $cache_dir = './cache'; // name of directory for template cache var $cache_lifetime = 3600; // number of seconds cached content will persist. // 0 = never expires. default is one hour (3600) - var $tpl_file_ext = '.tpl'; // template file extention - - var $php_handling = SMARTY_PHP_PASSTHRU; // how smarty handles php tags + var $php_handling = SMARTY_PHP_PASSTHRU; + // how smarty handles php tags in the templates // possible values: // SMARTY_PHP_PASSTHRU -> echo tags as is // SMARTY_PHP_QUOTE -> escape tags as entities @@ -107,10 +115,16 @@ class Smarty 'count_paragraphs' => 'smarty_mod_count_paragraphs' ); + var $template_resource_handlers = array(); // where resource handlers are mapped var $version = "1.3.2"; // Smarty version number - var $show_info_header = true; // display info header at top of page output + var $show_info_header = true; // display info header at top of page output +/**************************************************************************/ +/* END SMARTY CONFIGURATION SECTION */ +/* There should be no need to touch anything below this line. */ +/**************************************************************************/ + // internal vars var $_error_msg = false; // error messages. true/false var $_tpl_vars = array(); @@ -351,28 +365,27 @@ class Smarty } } - // compile files - $this->_compile($this->template_dir); - //assemble compile directory path to file - $_compile_file = $this->compile_dir."/".$tpl_file.".php"; - extract($this->_tpl_vars); if($this->show_info_header) - $info_header = ''."\n\n"; + $info_header = ''."\n\n"; else $info_header = ""; + $this->_fetch_compile_path($tpl_file,$compile_path); + // if we just need to display the results, don't perform output // buffering - for speed if ($display && !$this->caching) { echo $info_header; - include($_compile_file); + $this->_process_template($tpl_file,$compile_path); + include($compile_path); } else { ob_start(); echo $info_header; - include($_compile_file); + $this->_process_template($tpl_file,$compile_path); + include($compile_path); $results = ob_get_contents(); ob_end_clean(); } @@ -389,41 +402,172 @@ class Smarty if(isset($results)) { return $results; } } } + +/*======================================================================*\ + Function: _process_template() + Purpose: +\*======================================================================*/ + function _process_template($tpl_file,&$compile_path) + { + // get path to where compiled template is (to be) saved + $this->_fetch_compile_path($tpl_file,$compile_path); + // test if template needs to be compiled + if(!$this->force_compile && $this->_compiled_template_exists($compile_path)) { + if(!$this->compile_check) { + // no need to check if the template needs recompiled + return true; + } else { + // get template source and timestamp + $this->_fetch_template_source($tpl_file,$template_source,$template_timestamp); + if($template_timestamp <= $this->_fetch_compiled_template_timestamp($compile_path)) { + // template not expired, no recompile + return true; + } else { + // compile template + $this->_compile_template($tpl_file,$template_source,$template_compiled); + $this->_write_compiled_template($compile_path,$template_compiled); + return true; + } + } + } else { + // compiled template does not exist, or forced compile + $this->_fetch_template_source($tpl_file,$template_source,$template_timestamp); + $this->_compile_template($tpl_file,$template_source,$template_compiled); + $this->_write_compiled_template($compile_path,$template_compiled); + return true; + } + } /*======================================================================*\ - Function: compile() - Purpose: called to compile the templates + Function: _fetch_compile_path() + Purpose: fetch the path to save the comiled template \*======================================================================*/ - function _compile($tpl_dir) + function _fetch_compile_path($tpl_file,&$compile_path) { - if($this->compile_check || $this->force_compile) - { - include_once("Smarty_Compiler.class.php"); - - $smarty_compile = new Smarty_Compiler; + // for now, everything is in $compile_dir + $compile_path = $this->compile_dir.'/'.md5($tpl_file).'.php'; + return true; + } - $smarty_compile->template_dir = $this->template_dir; - $smarty_compile->compile_dir = $this->compile_dir; - $smarty_compile->config_dir = $this->config_dir; - $smarty_compile->force_compile = $this->force_compile; - $smarty_compile->caching = $this->caching; - $smarty_compile->tpl_file_ext = $this->tpl_file_ext; - $smarty_compile->php_handling = $this->php_handling; - $smarty_compile->left_delimiter = $this->left_delimiter; - $smarty_compile->right_delimiter = $this->right_delimiter; - $smarty_compile->custom_funcs = $this->custom_funcs; - $smarty_compile->custom_mods = $this->custom_mods; - - if($smarty_compile->_traverse_files($tpl_dir, 0)) - return true; - else { - $this->_error_msg = $smarty_compile->_error_msg; +/*======================================================================*\ + Function: _compiled_template_exists + Purpose: +\*======================================================================*/ + function _compiled_template_exists($include_path) + { + // for now, everything is in $compile_dir + return file_exists($include_path); + } + +/*======================================================================*\ + Function: _fetch_compiled_template_timestamp + Purpose: +\*======================================================================*/ + function _fetch_compiled_template_timestamp($include_path) + { + // for now, everything is in $compile_dir + return filemtime($include_path); + } + +/*======================================================================*\ + Function: _write_compiled_template + Purpose: +\*======================================================================*/ + function _write_compiled_template($compile_path,$template_compiled) + { + // for now, we save everything into $compile_dir + $this->_write_file($compile_path,$template_compiled); + return true; + } + +/*======================================================================*\ + Function: _fetch_template_source() + Purpose: fetch the template source and timestamp +\*======================================================================*/ + function _fetch_template_source($tpl_path,&$template_source,&$template_timestamp) + { + // split tpl_path by the first colon + $tpl_path_parts = preg_split("/:/",$tpl_path,2); + + if(count($tpl_path_parts) == 1) { + // no resource type, treat as flat file from template_dir + $resource_type = "file"; + $filename = $this->template_dir.'/'.$tpl_path_parts[0]; + } else { + $resource_type = $tpl_path_parts[0]; + $filename = $tpl_path_parts[1]; + } + + switch($resource_type) { + case "file": + if (file_exists($filename)) { + $template_source = $this->_read_file($filename); + $template_timestamp = filemtime($filename); + return true; + } else { + $this->_set_error_msg("unable to read template resource: \"$filename.\""); + $this->_trigger_error_msg(); + return false; + } + break; + default: + $this->_set_error_msg("unknown resource type: \"$resource_type.\""); + $this->_trigger_error_msg(); return false; - } - } else - return false; + break; + } + return true; + } + +/*======================================================================*\ + Function: _compile_template() + Purpose: called to compile the templates +\*======================================================================*/ + function _compile_template($tpl_file,$template_source,&$template_compiled) + { + + include_once("Smarty_Compiler.class.php"); + + $smarty_compile = new Smarty_Compiler; + + $smarty_compile->template_dir = $this->template_dir; + $smarty_compile->compile_dir = $this->compile_dir; + $smarty_compile->config_dir = $this->config_dir; + $smarty_compile->force_compile = $this->force_compile; + $smarty_compile->caching = $this->caching; + $smarty_compile->php_handling = $this->php_handling; + $smarty_compile->left_delimiter = $this->left_delimiter; + $smarty_compile->right_delimiter = $this->right_delimiter; + $smarty_compile->custom_funcs = $this->custom_funcs; + $smarty_compile->custom_mods = $this->custom_mods; + $smarty_compile->version = $this->version; + + if($smarty_compile->_compile_file($tpl_file,$template_source, $template_compiled)) + return true; + else { + $this->_error_msg = $smarty_compile->_error_msg; + return false; + } + } + +/*======================================================================*\ + Function: _smarty_include() + Purpose: called for included templates +\*======================================================================*/ + function _smarty_include($_smarty_include_tpl_file,$_smarty_def_vars, + $_smarty_include_vars,&$parent_smarty_config) + { + extract($_smarty_def_vars); + extract($_smarty_include_vars); + + $this->_fetch_compile_path($_smarty_include_tpl_file,$compile_path); + $this->_process_template($_smarty_include_tpl_file,$compile_path); + include($compile_path); + + } + /*======================================================================*\ Function: _process_cached_inserts Purpose: Replace cached inserts with the actual results @@ -473,6 +617,7 @@ class Smarty { if (!($fd = fopen($filename, 'r'))) { $this->_set_error_msg("problem reading '$filename.'"); + $this->_trigger_error_msg(); return false; } flock($fd, LOCK_SH); @@ -492,15 +637,14 @@ class Smarty if (!($fd = fopen($filename, 'a'))) { $this->_set_error_msg("problem writing '$filename.'"); + $this->_trigger_error_msg(); return false; } - flock($fd, LOCK_EX); - - $fd_safe = fopen($filename, 'w'); - - fwrite($fd_safe, $contents); - fclose($fd_safe); - fclose($fd); + if (flock($fd, LOCK_EX) && ftruncate( $fd, 0) ){ + fwrite( $fd, $contents ); + fclose($fd); + chmod($filename,0644); + } return true; } @@ -553,8 +697,9 @@ class Smarty $new_dir = ($dir{0} == '/') ? '/' : ''; foreach ($dir_parts as $dir_part) { $new_dir .= $dir_part; - if (!file_exists($new_dir) && !mkdir($new_dir, 0755)) { + if (!file_exists($new_dir) && !mkdir($new_dir, 0701)) { $this->_set_error_msg("problem creating directory \"$dir\""); + $this->_trigger_error_msg(); return false; } $new_dir .= '/'; @@ -577,9 +722,18 @@ class Smarty \*======================================================================*/ function _set_error_msg($error_msg) { - $this->_error_msg="smarty error: $error_msg"; + $this->_error_msg="Smarty error: $error_msg"; return true; } +/*======================================================================*\ + Function: _trigger_error_msg + Purpose: trigger Smarty error +\*======================================================================*/ + function _trigger_error_msg($error_type = E_USER_ERROR) + { + trigger_error($this->_error_msg, $error_type); + } + } /* vim: set expandtab: */ diff --git a/Smarty_Compiler.class.php b/Smarty_Compiler.class.php index a736af5a..95d25a2e 100644 --- a/Smarty_Compiler.class.php +++ b/Smarty_Compiler.class.php @@ -46,82 +46,6 @@ class Smarty_Compiler extends Smarty { var $_current_file = null; // the current template being compiled var $_current_line_no = 1; // line number for error messages -/*======================================================================*\ - Function: _traverse_files() - Purpose: traverse the template files & process each one -\*======================================================================*/ - function _traverse_files($tpl_dir, $depth) - { - $retval = true; - - if (is_dir($tpl_dir)) { - $dir_handle = opendir($tpl_dir); - while ($curr_file = readdir($dir_handle)) { - if ($curr_file == '.' || $curr_file == '..') - continue; - - $filepath = $tpl_dir.'/'.$curr_file; - if (is_readable($filepath)) { - if (is_file($filepath) && substr($curr_file, -strlen($this->tpl_file_ext)) == $this->tpl_file_ext) { - if (!$this->_process_file($filepath)) { - $retval = false; - break; - } - } else if (is_dir($filepath)) { - if (!$this->_traverse_files($filepath, $depth + 1)) { - $retval = false; - break; - } - } else { - // invalid file type, skipping - $this->_set_error_msg("Invalid filetype for $filepath, skipping"); - continue; - } - } - } - - closedir($dir_handle); - return $retval; - } else { - $this->_set_error_msg("Directory \"$tpl_dir\" does not exist or is not a directory."); - return false; - } - } - -/*======================================================================*\ - 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] . '.php'; - - $compile_dir = preg_replace('!^' . preg_quote($this->template_dir, '!') . '!', - $this->compile_dir, $match[1]); - - //create directory if none exists - $this->_create_dir_structure($compile_dir); - - // compile the template file if none exists or has been modified or recompile is forced - if ($this->force_compile || !file_exists($compile_dir."/".$tpl_file_name) || - ($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 { - $this->_set_error_msg("problem matching \"$filepath.\""); - return false; - } - - return true; - } /*======================================================================*\ Function: _modified_file() @@ -138,27 +62,25 @@ class Smarty_Compiler extends Smarty { Function: _compile_file() Input: compile a template file \*======================================================================*/ - function _compile_file($filepath, $compilepath) + function _compile_file($tpl_file, $template_source, &$template_compiled) { - if (!($template_contents = $this->_read_file($filepath))) - return false; - - $this->_current_file = str_replace($this->template_dir . '/', '', $filepath); + + $this->_current_file = $template_source; $this->_current_line_no = 1; $ldq = preg_quote($this->left_delimiter, '!'); $rdq = preg_quote($this->right_delimiter, '!'); /* Pull out the literal blocks. */ - preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_contents, $match); + preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_source, $match); $this->_literal_blocks = $match[1]; - $template_contents = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", - $this->quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $template_contents); + $template_source = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", + $this->quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $template_source); /* Gather all template tags. */ - preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_contents, $match); + preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_source, $match); $template_tags = $match[1]; /* Split content by template tags to obtain non-template content. */ - $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_contents); + $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_source); /* TODO: speed up the following with preg_replace and /F once we require that version of PHP */ @@ -199,27 +121,29 @@ class Smarty_Compiler extends Smarty { $this->_current_line_no += substr_count($template_tags[$i], "\n"); } - $compiled_contents = ''; + $template_compiled = ''; /* Interleave the compiled contents and text blocks to get the final result. */ for ($i = 0; $i < count($compiled_tags); $i++) { - $compiled_contents .= $text_blocks[$i].$compiled_tags[$i]; + $template_compiled .= $text_blocks[$i].$compiled_tags[$i]; } - $compiled_contents .= $text_blocks[$i]; + $template_compiled .= $text_blocks[$i]; /* 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)) { + if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $template_compiled, $match)) { $strip_tags = $match[0]; $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); + $template_compiled = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", + $strip_tags_modified[$i], $template_compiled, 1); } - if(!$this->_write_file($compilepath, $compiled_contents)) - return false; - + // put header at the top of the compiled template + $template_header = "version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; + $template_header .= " compiled from ".$tpl_file." */ ?>\n"; + $template_compiled = $template_header.$template_compiled; + return true; } @@ -405,13 +329,13 @@ class Smarty_Compiler extends Smarty { " include_once 'Config_File.class.php';\n" . "if (!is_object(\$GLOBALS['_smarty_conf_obj']) || get_class(\$GLOBALS['_smarty_conf_obj']) != 'config_file')\n" . " \$GLOBALS['_smarty_conf_obj'] = new Config_File('".$this->config_dir."');\n" . - "if (isset(\$_smarty_config_parent) && $update_parent)\n" . - " \$_smarty_config_parent = array_merge((array)\$_smarty_config_parent, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file']."));\n" . + "if (isset(\$parent_smarty_config) && $update_parent)\n" . + " \$parent_smarty_config = array_merge((array)\$parent_smarty_config, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file']."));\n" . "\$_smarty_config = array_merge((array)\$_smarty_config, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file']."));\n"; if (!empty($attrs['section'])) { - $output .= "if (isset(\$_smarty_config_parent) && $update_parent)\n" . - " \$_smarty_config_parent = array_merge((array)\$_smarty_config_parent, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file'].", ".$attrs['section']."));\n" . + $output .= "if (isset(\$parent_smarty_config) && $update_parent)\n" . + " \$parent_smarty_config = array_merge((array)\$parent_smarty_config, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file'].", ".$attrs['section']."));\n" . "\$_smarty_config = array_merge((array)\$_smarty_config, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file'].", ".$attrs['section']."));\n"; } @@ -427,41 +351,28 @@ class Smarty_Compiler extends Smarty { function _compile_include_tag($tag_args) { $attrs = $this->_parse_attrs($tag_args); - + if (empty($attrs['file'])) { $this->_syntax_error("missing 'file' attribute in include tag"); } else $attrs['file'] = $this->_dequote($attrs['file']); - $include_func_name = uniqid("_include_"); - if ($attrs['file']{0} == '$') - $include_file_name = '"'.$this->compile_dir.'/".'.$attrs['file']; - else - $include_file_name = '"'.$this->compile_dir.'/'.$attrs['file'].'"'; - - foreach ($attrs as $arg_name => $arg_value) { - if ($arg_name == 'file') continue; + if ($arg_name == 'file') { + $_smarty_include_tpl_file = $arg_value; + continue; + } if (is_bool($arg_value)) $arg_value = $arg_value ? 'true' : 'false'; $arg_list[] = "'$arg_name' => $arg_value"; } return ""; + "\$_smarty_defined_vars = get_defined_vars();\n" . + "unset(\$_smarty_defined_vars['_smarty_include_tpl_file']);\n" . + "unset(\$_smarty_defined_vars['_smarty_def_vars']);\n" . + "unset(\$_smarty_defined_vars['_smarty_include_vars']);\n" . + "\$this->_smarty_include(\"".$_smarty_include_tpl_file."\", \$_smarty_defined_vars, array(".implode(',', (array)$arg_list)."), \$_smarty_config);\n?>"; } /*======================================================================*\ @@ -764,22 +675,6 @@ class Smarty_Compiler extends Smarty { return $attrs; } -/*======================================================================*\ - Function: _preg_grep - Purpose: Emulate PHP's preg_grep() -\*======================================================================*/ - function _preg_grep($pattern, $array) - { - $result = array(); - - foreach ($array as $key => $entry) { - if (preg_match($pattern, $entry)) - $result[$key] = $entry; - } - - return $result; - } - /*======================================================================*\ Function: _parse_vars_props Purpose: @@ -788,18 +683,9 @@ class Smarty_Compiler extends Smarty { { $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\''; - /* preg_grep() was fixed to return keys properly in 4.0.4 and later. To - allow people to use older versions of PHP we emulate preg_grep() and - use the version check to see what function to call. */ - if (strnatcmp(PHP_VERSION, '4.0.4') >= 0) { - $var_exprs = preg_grep('!^\$(\w+(\.\w+)?/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - $conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - $sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - } else { - $var_exprs = $this->_preg_grep('!^\$(\w+(\.\w+)?/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - $conf_var_exprs = $this->_preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - $sect_prop_exprs = $this->_preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - } + $var_exprs = preg_grep('!^\$(\w+(\.\w+)?/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); + $conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); + $sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); if (count($var_exprs)) { foreach ($var_exprs as $expr_index => $var_expr) { diff --git a/demo/templates/index.tpl b/demo/templates/index.tpl index cd86e8d8..d485ec19 100644 --- a/demo/templates/index.tpl +++ b/demo/templates/index.tpl @@ -67,3 +67,5 @@ This is an example of the html_options function: {html_options values=$option_values selected=$option_selected output=$option_output} + +{include file="footer.tpl"} diff --git a/libs/Smarty.class.php b/libs/Smarty.class.php index dc175acd..f284bfd3 100644 --- a/libs/Smarty.class.php +++ b/libs/Smarty.class.php @@ -22,6 +22,9 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * For questions, help, comments, discussion, etc., please join the + * Smarty mailing list. Send a blank e-mail to smarty-subscribe@lists.ispi.net + * * You may contact the authors of Smarty by e-mail at: * monte@ispi.net * andrei@ispi.net @@ -47,6 +50,11 @@ define("SMARTY_PHP_ALLOW",3); class Smarty { +/**************************************************************************/ +/* BEGIN SMARTY CONFIGURATION SECTION */ +/* Set the following config variables to your liking. */ +/**************************************************************************/ + // public vars var $template_dir = './templates'; // name of directory for templates var $compile_dir = './templates_c'; // name of directory for compiled templates @@ -62,17 +70,17 @@ class Smarty // during development. true/false default true. var $force_compile = false; // force templates to compile every time. - // if cache file exists, it will + // if caching is on, a cached file will // override compile_check and force_compile. // true/false. default false. + var $caching = false; // whether to use caching or not. true/false var $cache_dir = './cache'; // name of directory for template cache var $cache_lifetime = 3600; // number of seconds cached content will persist. // 0 = never expires. default is one hour (3600) - var $tpl_file_ext = '.tpl'; // template file extention - - var $php_handling = SMARTY_PHP_PASSTHRU; // how smarty handles php tags + var $php_handling = SMARTY_PHP_PASSTHRU; + // how smarty handles php tags in the templates // possible values: // SMARTY_PHP_PASSTHRU -> echo tags as is // SMARTY_PHP_QUOTE -> escape tags as entities @@ -107,10 +115,16 @@ class Smarty 'count_paragraphs' => 'smarty_mod_count_paragraphs' ); + var $template_resource_handlers = array(); // where resource handlers are mapped var $version = "1.3.2"; // Smarty version number - var $show_info_header = true; // display info header at top of page output + var $show_info_header = true; // display info header at top of page output +/**************************************************************************/ +/* END SMARTY CONFIGURATION SECTION */ +/* There should be no need to touch anything below this line. */ +/**************************************************************************/ + // internal vars var $_error_msg = false; // error messages. true/false var $_tpl_vars = array(); @@ -351,28 +365,27 @@ class Smarty } } - // compile files - $this->_compile($this->template_dir); - //assemble compile directory path to file - $_compile_file = $this->compile_dir."/".$tpl_file.".php"; - extract($this->_tpl_vars); if($this->show_info_header) - $info_header = ''."\n\n"; + $info_header = ''."\n\n"; else $info_header = ""; + $this->_fetch_compile_path($tpl_file,$compile_path); + // if we just need to display the results, don't perform output // buffering - for speed if ($display && !$this->caching) { echo $info_header; - include($_compile_file); + $this->_process_template($tpl_file,$compile_path); + include($compile_path); } else { ob_start(); echo $info_header; - include($_compile_file); + $this->_process_template($tpl_file,$compile_path); + include($compile_path); $results = ob_get_contents(); ob_end_clean(); } @@ -389,41 +402,172 @@ class Smarty if(isset($results)) { return $results; } } } + +/*======================================================================*\ + Function: _process_template() + Purpose: +\*======================================================================*/ + function _process_template($tpl_file,&$compile_path) + { + // get path to where compiled template is (to be) saved + $this->_fetch_compile_path($tpl_file,$compile_path); + // test if template needs to be compiled + if(!$this->force_compile && $this->_compiled_template_exists($compile_path)) { + if(!$this->compile_check) { + // no need to check if the template needs recompiled + return true; + } else { + // get template source and timestamp + $this->_fetch_template_source($tpl_file,$template_source,$template_timestamp); + if($template_timestamp <= $this->_fetch_compiled_template_timestamp($compile_path)) { + // template not expired, no recompile + return true; + } else { + // compile template + $this->_compile_template($tpl_file,$template_source,$template_compiled); + $this->_write_compiled_template($compile_path,$template_compiled); + return true; + } + } + } else { + // compiled template does not exist, or forced compile + $this->_fetch_template_source($tpl_file,$template_source,$template_timestamp); + $this->_compile_template($tpl_file,$template_source,$template_compiled); + $this->_write_compiled_template($compile_path,$template_compiled); + return true; + } + } /*======================================================================*\ - Function: compile() - Purpose: called to compile the templates + Function: _fetch_compile_path() + Purpose: fetch the path to save the comiled template \*======================================================================*/ - function _compile($tpl_dir) + function _fetch_compile_path($tpl_file,&$compile_path) { - if($this->compile_check || $this->force_compile) - { - include_once("Smarty_Compiler.class.php"); - - $smarty_compile = new Smarty_Compiler; + // for now, everything is in $compile_dir + $compile_path = $this->compile_dir.'/'.md5($tpl_file).'.php'; + return true; + } - $smarty_compile->template_dir = $this->template_dir; - $smarty_compile->compile_dir = $this->compile_dir; - $smarty_compile->config_dir = $this->config_dir; - $smarty_compile->force_compile = $this->force_compile; - $smarty_compile->caching = $this->caching; - $smarty_compile->tpl_file_ext = $this->tpl_file_ext; - $smarty_compile->php_handling = $this->php_handling; - $smarty_compile->left_delimiter = $this->left_delimiter; - $smarty_compile->right_delimiter = $this->right_delimiter; - $smarty_compile->custom_funcs = $this->custom_funcs; - $smarty_compile->custom_mods = $this->custom_mods; - - if($smarty_compile->_traverse_files($tpl_dir, 0)) - return true; - else { - $this->_error_msg = $smarty_compile->_error_msg; +/*======================================================================*\ + Function: _compiled_template_exists + Purpose: +\*======================================================================*/ + function _compiled_template_exists($include_path) + { + // for now, everything is in $compile_dir + return file_exists($include_path); + } + +/*======================================================================*\ + Function: _fetch_compiled_template_timestamp + Purpose: +\*======================================================================*/ + function _fetch_compiled_template_timestamp($include_path) + { + // for now, everything is in $compile_dir + return filemtime($include_path); + } + +/*======================================================================*\ + Function: _write_compiled_template + Purpose: +\*======================================================================*/ + function _write_compiled_template($compile_path,$template_compiled) + { + // for now, we save everything into $compile_dir + $this->_write_file($compile_path,$template_compiled); + return true; + } + +/*======================================================================*\ + Function: _fetch_template_source() + Purpose: fetch the template source and timestamp +\*======================================================================*/ + function _fetch_template_source($tpl_path,&$template_source,&$template_timestamp) + { + // split tpl_path by the first colon + $tpl_path_parts = preg_split("/:/",$tpl_path,2); + + if(count($tpl_path_parts) == 1) { + // no resource type, treat as flat file from template_dir + $resource_type = "file"; + $filename = $this->template_dir.'/'.$tpl_path_parts[0]; + } else { + $resource_type = $tpl_path_parts[0]; + $filename = $tpl_path_parts[1]; + } + + switch($resource_type) { + case "file": + if (file_exists($filename)) { + $template_source = $this->_read_file($filename); + $template_timestamp = filemtime($filename); + return true; + } else { + $this->_set_error_msg("unable to read template resource: \"$filename.\""); + $this->_trigger_error_msg(); + return false; + } + break; + default: + $this->_set_error_msg("unknown resource type: \"$resource_type.\""); + $this->_trigger_error_msg(); return false; - } - } else - return false; + break; + } + return true; + } + +/*======================================================================*\ + Function: _compile_template() + Purpose: called to compile the templates +\*======================================================================*/ + function _compile_template($tpl_file,$template_source,&$template_compiled) + { + + include_once("Smarty_Compiler.class.php"); + + $smarty_compile = new Smarty_Compiler; + + $smarty_compile->template_dir = $this->template_dir; + $smarty_compile->compile_dir = $this->compile_dir; + $smarty_compile->config_dir = $this->config_dir; + $smarty_compile->force_compile = $this->force_compile; + $smarty_compile->caching = $this->caching; + $smarty_compile->php_handling = $this->php_handling; + $smarty_compile->left_delimiter = $this->left_delimiter; + $smarty_compile->right_delimiter = $this->right_delimiter; + $smarty_compile->custom_funcs = $this->custom_funcs; + $smarty_compile->custom_mods = $this->custom_mods; + $smarty_compile->version = $this->version; + + if($smarty_compile->_compile_file($tpl_file,$template_source, $template_compiled)) + return true; + else { + $this->_error_msg = $smarty_compile->_error_msg; + return false; + } + } + +/*======================================================================*\ + Function: _smarty_include() + Purpose: called for included templates +\*======================================================================*/ + function _smarty_include($_smarty_include_tpl_file,$_smarty_def_vars, + $_smarty_include_vars,&$parent_smarty_config) + { + extract($_smarty_def_vars); + extract($_smarty_include_vars); + + $this->_fetch_compile_path($_smarty_include_tpl_file,$compile_path); + $this->_process_template($_smarty_include_tpl_file,$compile_path); + include($compile_path); + + } + /*======================================================================*\ Function: _process_cached_inserts Purpose: Replace cached inserts with the actual results @@ -473,6 +617,7 @@ class Smarty { if (!($fd = fopen($filename, 'r'))) { $this->_set_error_msg("problem reading '$filename.'"); + $this->_trigger_error_msg(); return false; } flock($fd, LOCK_SH); @@ -492,15 +637,14 @@ class Smarty if (!($fd = fopen($filename, 'a'))) { $this->_set_error_msg("problem writing '$filename.'"); + $this->_trigger_error_msg(); return false; } - flock($fd, LOCK_EX); - - $fd_safe = fopen($filename, 'w'); - - fwrite($fd_safe, $contents); - fclose($fd_safe); - fclose($fd); + if (flock($fd, LOCK_EX) && ftruncate( $fd, 0) ){ + fwrite( $fd, $contents ); + fclose($fd); + chmod($filename,0644); + } return true; } @@ -553,8 +697,9 @@ class Smarty $new_dir = ($dir{0} == '/') ? '/' : ''; foreach ($dir_parts as $dir_part) { $new_dir .= $dir_part; - if (!file_exists($new_dir) && !mkdir($new_dir, 0755)) { + if (!file_exists($new_dir) && !mkdir($new_dir, 0701)) { $this->_set_error_msg("problem creating directory \"$dir\""); + $this->_trigger_error_msg(); return false; } $new_dir .= '/'; @@ -577,9 +722,18 @@ class Smarty \*======================================================================*/ function _set_error_msg($error_msg) { - $this->_error_msg="smarty error: $error_msg"; + $this->_error_msg="Smarty error: $error_msg"; return true; } +/*======================================================================*\ + Function: _trigger_error_msg + Purpose: trigger Smarty error +\*======================================================================*/ + function _trigger_error_msg($error_type = E_USER_ERROR) + { + trigger_error($this->_error_msg, $error_type); + } + } /* vim: set expandtab: */ diff --git a/libs/Smarty_Compiler.class.php b/libs/Smarty_Compiler.class.php index a736af5a..95d25a2e 100644 --- a/libs/Smarty_Compiler.class.php +++ b/libs/Smarty_Compiler.class.php @@ -46,82 +46,6 @@ class Smarty_Compiler extends Smarty { var $_current_file = null; // the current template being compiled var $_current_line_no = 1; // line number for error messages -/*======================================================================*\ - Function: _traverse_files() - Purpose: traverse the template files & process each one -\*======================================================================*/ - function _traverse_files($tpl_dir, $depth) - { - $retval = true; - - if (is_dir($tpl_dir)) { - $dir_handle = opendir($tpl_dir); - while ($curr_file = readdir($dir_handle)) { - if ($curr_file == '.' || $curr_file == '..') - continue; - - $filepath = $tpl_dir.'/'.$curr_file; - if (is_readable($filepath)) { - if (is_file($filepath) && substr($curr_file, -strlen($this->tpl_file_ext)) == $this->tpl_file_ext) { - if (!$this->_process_file($filepath)) { - $retval = false; - break; - } - } else if (is_dir($filepath)) { - if (!$this->_traverse_files($filepath, $depth + 1)) { - $retval = false; - break; - } - } else { - // invalid file type, skipping - $this->_set_error_msg("Invalid filetype for $filepath, skipping"); - continue; - } - } - } - - closedir($dir_handle); - return $retval; - } else { - $this->_set_error_msg("Directory \"$tpl_dir\" does not exist or is not a directory."); - return false; - } - } - -/*======================================================================*\ - 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] . '.php'; - - $compile_dir = preg_replace('!^' . preg_quote($this->template_dir, '!') . '!', - $this->compile_dir, $match[1]); - - //create directory if none exists - $this->_create_dir_structure($compile_dir); - - // compile the template file if none exists or has been modified or recompile is forced - if ($this->force_compile || !file_exists($compile_dir."/".$tpl_file_name) || - ($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 { - $this->_set_error_msg("problem matching \"$filepath.\""); - return false; - } - - return true; - } /*======================================================================*\ Function: _modified_file() @@ -138,27 +62,25 @@ class Smarty_Compiler extends Smarty { Function: _compile_file() Input: compile a template file \*======================================================================*/ - function _compile_file($filepath, $compilepath) + function _compile_file($tpl_file, $template_source, &$template_compiled) { - if (!($template_contents = $this->_read_file($filepath))) - return false; - - $this->_current_file = str_replace($this->template_dir . '/', '', $filepath); + + $this->_current_file = $template_source; $this->_current_line_no = 1; $ldq = preg_quote($this->left_delimiter, '!'); $rdq = preg_quote($this->right_delimiter, '!'); /* Pull out the literal blocks. */ - preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_contents, $match); + preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_source, $match); $this->_literal_blocks = $match[1]; - $template_contents = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", - $this->quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $template_contents); + $template_source = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", + $this->quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $template_source); /* Gather all template tags. */ - preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_contents, $match); + preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_source, $match); $template_tags = $match[1]; /* Split content by template tags to obtain non-template content. */ - $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_contents); + $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_source); /* TODO: speed up the following with preg_replace and /F once we require that version of PHP */ @@ -199,27 +121,29 @@ class Smarty_Compiler extends Smarty { $this->_current_line_no += substr_count($template_tags[$i], "\n"); } - $compiled_contents = ''; + $template_compiled = ''; /* Interleave the compiled contents and text blocks to get the final result. */ for ($i = 0; $i < count($compiled_tags); $i++) { - $compiled_contents .= $text_blocks[$i].$compiled_tags[$i]; + $template_compiled .= $text_blocks[$i].$compiled_tags[$i]; } - $compiled_contents .= $text_blocks[$i]; + $template_compiled .= $text_blocks[$i]; /* 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)) { + if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $template_compiled, $match)) { $strip_tags = $match[0]; $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); + $template_compiled = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", + $strip_tags_modified[$i], $template_compiled, 1); } - if(!$this->_write_file($compilepath, $compiled_contents)) - return false; - + // put header at the top of the compiled template + $template_header = "version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n"; + $template_header .= " compiled from ".$tpl_file." */ ?>\n"; + $template_compiled = $template_header.$template_compiled; + return true; } @@ -405,13 +329,13 @@ class Smarty_Compiler extends Smarty { " include_once 'Config_File.class.php';\n" . "if (!is_object(\$GLOBALS['_smarty_conf_obj']) || get_class(\$GLOBALS['_smarty_conf_obj']) != 'config_file')\n" . " \$GLOBALS['_smarty_conf_obj'] = new Config_File('".$this->config_dir."');\n" . - "if (isset(\$_smarty_config_parent) && $update_parent)\n" . - " \$_smarty_config_parent = array_merge((array)\$_smarty_config_parent, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file']."));\n" . + "if (isset(\$parent_smarty_config) && $update_parent)\n" . + " \$parent_smarty_config = array_merge((array)\$parent_smarty_config, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file']."));\n" . "\$_smarty_config = array_merge((array)\$_smarty_config, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file']."));\n"; if (!empty($attrs['section'])) { - $output .= "if (isset(\$_smarty_config_parent) && $update_parent)\n" . - " \$_smarty_config_parent = array_merge((array)\$_smarty_config_parent, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file'].", ".$attrs['section']."));\n" . + $output .= "if (isset(\$parent_smarty_config) && $update_parent)\n" . + " \$parent_smarty_config = array_merge((array)\$parent_smarty_config, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file'].", ".$attrs['section']."));\n" . "\$_smarty_config = array_merge((array)\$_smarty_config, \$GLOBALS['_smarty_conf_obj']->get(".$attrs['file'].", ".$attrs['section']."));\n"; } @@ -427,41 +351,28 @@ class Smarty_Compiler extends Smarty { function _compile_include_tag($tag_args) { $attrs = $this->_parse_attrs($tag_args); - + if (empty($attrs['file'])) { $this->_syntax_error("missing 'file' attribute in include tag"); } else $attrs['file'] = $this->_dequote($attrs['file']); - $include_func_name = uniqid("_include_"); - if ($attrs['file']{0} == '$') - $include_file_name = '"'.$this->compile_dir.'/".'.$attrs['file']; - else - $include_file_name = '"'.$this->compile_dir.'/'.$attrs['file'].'"'; - - foreach ($attrs as $arg_name => $arg_value) { - if ($arg_name == 'file') continue; + if ($arg_name == 'file') { + $_smarty_include_tpl_file = $arg_value; + continue; + } if (is_bool($arg_value)) $arg_value = $arg_value ? 'true' : 'false'; $arg_list[] = "'$arg_name' => $arg_value"; } return ""; + "\$_smarty_defined_vars = get_defined_vars();\n" . + "unset(\$_smarty_defined_vars['_smarty_include_tpl_file']);\n" . + "unset(\$_smarty_defined_vars['_smarty_def_vars']);\n" . + "unset(\$_smarty_defined_vars['_smarty_include_vars']);\n" . + "\$this->_smarty_include(\"".$_smarty_include_tpl_file."\", \$_smarty_defined_vars, array(".implode(',', (array)$arg_list)."), \$_smarty_config);\n?>"; } /*======================================================================*\ @@ -764,22 +675,6 @@ class Smarty_Compiler extends Smarty { return $attrs; } -/*======================================================================*\ - Function: _preg_grep - Purpose: Emulate PHP's preg_grep() -\*======================================================================*/ - function _preg_grep($pattern, $array) - { - $result = array(); - - foreach ($array as $key => $entry) { - if (preg_match($pattern, $entry)) - $result[$key] = $entry; - } - - return $result; - } - /*======================================================================*\ Function: _parse_vars_props Purpose: @@ -788,18 +683,9 @@ class Smarty_Compiler extends Smarty { { $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\''; - /* preg_grep() was fixed to return keys properly in 4.0.4 and later. To - allow people to use older versions of PHP we emulate preg_grep() and - use the version check to see what function to call. */ - if (strnatcmp(PHP_VERSION, '4.0.4') >= 0) { - $var_exprs = preg_grep('!^\$(\w+(\.\w+)?/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - $conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - $sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - } else { - $var_exprs = $this->_preg_grep('!^\$(\w+(\.\w+)?/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - $conf_var_exprs = $this->_preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - $sect_prop_exprs = $this->_preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); - } + $var_exprs = preg_grep('!^\$(\w+(\.\w+)?/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); + $conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); + $sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens); if (count($var_exprs)) { foreach ($var_exprs as $expr_index => $var_expr) { diff --git a/templates/index.tpl b/templates/index.tpl index cd86e8d8..d485ec19 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -67,3 +67,5 @@ This is an example of the html_options function: {html_options values=$option_values selected=$option_selected output=$option_output} + +{include file="footer.tpl"}