/[corp_html]/inc/Smarty.class.php
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /inc/Smarty.class.php

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.3 by dpavlin, Sat Mar 17 15:46:05 2001 UTC revision 1.5 by dpavlin, Mon Jul 9 14:22:07 2001 UTC
# Line 1  Line 1 
1  <?  <?php
2  /*  /*
3   * Project:             Smarty: the PHP compiling template engine   * Project:     Smarty: the PHP compiling template engine
4   * File:                Smarty.class.php   * File:        Smarty.class.php
5   * Author:              Monte Ohrt <monte@ispi.net>   * Author:      Monte Ohrt <monte@ispi.net>
6   *              Andrei Zmievski <andrei@ispi.net>   *              Andrei Zmievski <andrei@ispi.net>
7   *   *
8   * Version:             1.2.2   * Version:             1.3.0
9   * Copyright:           2001 ispi of Lincoln, Inc.   * Copyright:           2001 ispi of Lincoln, Inc.
10   *                                 *              
11   * This program is free software; you can redistribute it and/or   * This library is free software; you can redistribute it and/or
12   * modify it under the terms of the GNU General Public License   * modify it under the terms of the GNU Lesser General Public
13   * as published by the Free Software Foundation; either version 2   * License as published by the Free Software Foundation; either
14   * of the License, or (at your option) any later version.   * version 2.1 of the License, or (at your option) any later version.
15   *   *
16   * This program is distributed in the hope that it will be useful,   * This library is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19   * GNU General Public License for more details.   * Lesser General Public License for more details.
20   *   *
21   * You should have received a copy of the GNU General Public License   * You should have received a copy of the GNU Lesser General Public
22   * along with this program; if not, write to the Free Software   * License along with this library; if not, write to the Free Software
23   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24   *   *
25   * You may contact the authors of Smarty by e-mail at:   * You may contact the authors of Smarty by e-mail at:
26   * monte@ispi.net   * monte@ispi.net
# Line 37  Line 37 
37   *   *
38   */   */
39    
40  require("Smarty.addons.php");  require('Smarty.addons.php');
41  require("Smarty.local.php");  require("Smarty.local.php");
42    
43    define("SMARTY_PHP_PASSTHRU",0);
44    define("SMARTY_PHP_QUOTE",1);
45    define("SMARTY_PHP_REMOVE",2);
46    define("SMARTY_PHP_ALLOW",3);
47    
48  class Smarty  class Smarty
49  {  {
50    
51          // public vars      // public vars
52          var     $compile_check  =       true;   // whether to check for compiling step or not:      var $template_dir    =  './templates';     // name of directory for templates  
53                                                                          // This is generally set to false once the      var $compile_dir     =  './templates_c';   // name of directory for compiled templates
54                                                                          // application is entered into production and      var $config_dir      =  './configs';       // directory where config files are located
55                                                                          // initially compiled. Leave set to true  
56                                                                          // during development.      var $global_assign   =  array('SCRIPT_NAME'); // variables from the GLOBALS array
57                                                      // that are implicitly assigned
58          var $template_dir                       =       "./templates"; // name of directory for templates                                                          // to all templates  
59          var $compile_dir                        =       "./templates_c"; // name of directory for compiled templates          var $compile_check   =  true;       // whether to check for compiling step or not:
60                                                    // This is generally set to false once the
61                                                    // application is entered into production and
62          var $tpl_file_ext                       =       ".tpl"; // template file extentions                                          // initially compiled. Leave set to true
63                                                    // during development. true/false default true.
64          var $allow_php                          =       false;          // whether or not to allow embedded php  
65                                                                                                  // in the templates. By default, php tags      var $force_compile   =  false;      // force templates to compile every time.
66                                                                                                  // are escaped.                                          // if cache file exists, it will
67          var $left_delimiter                     =       "{";            // template tag delimiters.                                          // override compile_check and force_compile.
68          var $right_delimiter            =       "}";                                          // true/false. default false.
69        var $caching         =  false;      // whether to use caching or not. true/false
70          var $config_dir                         =       "./configs";    // directory where config files are located      var $cache_dir       =  './cache';  // name of directory for template cache
71        var $cache_lifetime  =  3600;       // number of seconds cached content will persist.
72          var $custom_funcs                       =       array(  'html_options'          => 'smarty_func_html_options',                                          // 0 = never expires. default is one hour (3600)
73                                                                                          'html_select_date'      => 'smarty_func_html_select_date',  
74                                                                                  //----- local      var $tpl_file_ext    =  '.tpl';     // template file extention
75                                                                                          'img'   => 'smarty_func_img',      
76                                                                                          'html_checkboxes' => 'smarty_func_html_checkboxes',      var $php_handling    =  SMARTY_PHP_PASSTHRU; // how smarty handles php tags
77                                                                                   );                                          // possible values:
78                                                    // SMARTY_PHP_PASSTHRU -> echo tags as is
79          var $custom_mods                        =       array(  'lower'                 => 'strtolower',                                          // SMARTY_PHP_QUOTE    -> escape tags as entities
80                                                                                          'upper'                 => 'strtoupper',                                          // SMARTY_PHP_REMOVE   -> remove php tags
81                                                                                          'capitalize'    => 'ucwords',                                          // SMARTY_PHP_ALLOW    -> execute php tags
82                                                                                          'escape'                => 'smarty_mod_escape',                                          // default: SMARTY_PHP_PASSTHRU
83                                                                                          'truncate'              => 'smarty_mod_truncate',  
84                                                                                          'spacify'               => 'smarty_mod_spacify',      var $left_delimiter  =  '{';        // template tag delimiters.
85                                                                                          'date_format'   => 'smarty_mod_date_format',      var $right_delimiter =  '}';
86                                                                                          'string_format' => 'smarty_mod_string_format',  
87                                                                                          'replace'               => 'smarty_mod_replace',  
88                                                                                          'strip_tags'    => 'smarty_mod_strip_tags',      var $custom_funcs    =  array(  'html_options'      => 'smarty_func_html_options',
89                                                                                          'default'               => 'smarty_mod_default',                                      'html_select_date'  => 'smarty_func_html_select_date',
90                                                                                  //----- local                                      'header'            => 'smarty_func_header',
91                                                                                          'filesize' => 'smarty_mod_filesize',  
92                                                                                   );  //----- local custom_funcs
93          var $global_assign                      =       array(  'SCRIPT_NAME'          'img'           => 'smarty_func_img',
94                                                                                   );          'html_checkboxes' => 'smarty_func_html_checkboxes',
95                    'input'         => 'smarty_func_input',
96          // internal vars          'rinput'        => 'smarty_func_rinput',
97          var $_error_msg                         =       false;          // error messages  
98          var $_tpl_vars                          =       array();                                   );
99          var $_sectionelse_stack         =       array();        // keeps track of whether section had 'else' part      
100          var $_literal_blocks            =       array();        // keeps literal template blocks      var $custom_mods     =  array(  'lower'         => 'strtolower',
101          var $_current_file                      =       null;           // the current template being compiled                                      'upper'         => 'strtoupper',
102          var $_current_line_no           =       1;                      // line number for error messages                                      'capitalize'    => 'ucwords',
103                                        'escape'        => 'smarty_mod_escape',
104                                                'truncate'      => 'smarty_mod_truncate',
105  /*======================================================================*\                                      'spacify'       => 'smarty_mod_spacify',
106          Function: Smarty                                      'date_format'   => 'smarty_mod_date_format',
107          Purpose:  Constructor                                      'string_format' => 'smarty_mod_string_format',
108  \*======================================================================*/                                      'replace'       => 'smarty_mod_replace',
109          function Smarty()                                      'strip_tags'    => 'smarty_mod_strip_tags',
110          {                                      'default'       => 'smarty_mod_default',
111                  foreach ($this->global_assign as $var_name)  
112                          $this->assign($var_name, $GLOBALS[$var_name]);  //----- local custom_mods
113          }          'filesize' => 'smarty_mod_filesize',
114    
115                                     );
116  /*======================================================================*\      
117          Function:       assign()      // internal vars
118          Purpose:        assigns values to template variables      var $_error_msg             =   false;      // error messages. true/false
119  \*======================================================================*/      var $_tpl_vars              =   array();
120        var $_sectionelse_stack     =   array();    // keeps track of whether section had 'else' part
121          function assign($tpl_var, $value = NULL)      var $_literal_blocks        =   array();    // keeps literal template blocks
122          {      var $_current_file          =   null;       // the current template being compiled
123                  if (is_array($tpl_var)){      var $_current_line_no       =   1;          // line number for error messages
124                          foreach ($tpl_var as $key => $val) {      var $_smarty_md5            =   'f8d698aea36fcbead2b9d5359ffca76f'; // md5 checksum of the string 'Smarty'
125                                  if (!empty($key))  
126                                          $this->_tpl_vars[$key] = $val;      
127                          }  /*======================================================================*\
128                  } else {      Function: Smarty
129                          if (!empty($tpl_var) && isset($value))      Purpose:  Constructor
130                                  $this->_tpl_vars[$tpl_var] = $value;  \*======================================================================*/
131                  }      function Smarty()
132          }      {
133            foreach ($this->global_assign as $var_name)
134                        $this->assign($var_name, $GLOBALS[$var_name]);
135  /*======================================================================*\      }
136          Function: append  
137          Purpose:  appens values to template variables  
138  \*======================================================================*/  /*======================================================================*\
139          function append($tpl_var, $value = NULL)      Function:   assign()
140          {      Purpose:    assigns values to template variables
141                  if (is_array($tpl_var)) {  \*======================================================================*/
142                          foreach ($tpl_var as $key => $val) {  
143                                  if (!empty($key)) {      function assign($tpl_var, $value = NULL)
144                                          if (!is_array($this->_tpl_vars[$key]))      {
145                                                  settype($this->_tpl_vars[$key], 'array');          if (is_array($tpl_var)){
146                                          $this->_tpl_vars[$key][] = $val;              foreach ($tpl_var as $key => $val) {
147                                  }                  if (!empty($key))
148                          }                      $this->_tpl_vars[$key] = $val;
149                  } else {              }
150                          if (!empty($tpl_var) && isset($value)) {          } else {
151                                  if (!is_array($this->_tpl_vars[$tpl_var]))              if (!empty($tpl_var) && isset($value))
152                                          settype($this->_tpl_vars[$tpl_var], 'array');                  $this->_tpl_vars[$tpl_var] = $value;
153                                  $this->_tpl_vars[$tpl_var][] = $value;          }
154                          }      }
155                  }  
156          }      
157    /*======================================================================*\
158        Function: append
159  /*======================================================================*\      Purpose:  appens values to template variables
160          Function:       clear_assign()  \*======================================================================*/
161          Purpose:        clear the given assigned template variable.      function append($tpl_var, $value = NULL)
162  \*======================================================================*/      {
163            if (is_array($tpl_var)) {
164          function clear_assign($tpl_var)              foreach ($tpl_var as $key => $val) {
165          {                  if (!empty($key)) {
166                  unset($this->_tpl_vars[$tpl_var]);                      if (!is_array($this->_tpl_vars[$key]))
167          }                          settype($this->_tpl_vars[$key], 'array');
168                        $this->_tpl_vars[$key][] = $val;
169  /*======================================================================*\                  }
170          Function:       clear_all_assign()              }
171          Purpose:        clear all the assigned template variables.          } else {
172  \*======================================================================*/              if (!empty($tpl_var) && isset($value)) {
173                    if (!is_array($this->_tpl_vars[$tpl_var]))
174          function clear_all_assign()                      settype($this->_tpl_vars[$tpl_var], 'array');
175          {                  $this->_tpl_vars[$tpl_var][] = $value;
176                  $this->_tpl_vars = array();              }
177          }          }
178        }
179    
180  /*======================================================================*\  
181          Function: get_template_vars  /*======================================================================*\
182          Purpose:  Returns an array containing template variables      Function:   clear_assign()
183  \*======================================================================*/      Purpose:    clear the given assigned template variable.
184          function &get_template_vars()  \*======================================================================*/
185          {  
186                  return $this->_tpl_vars;      function clear_assign($tpl_var)
187          }      {
188            if(is_array($tpl_var))
189                foreach($tpl_var as $curr_var)
190  /*======================================================================*\                  unset($this->_tpl_vars[$curr_var]);
191          Function:       display()          else
192          Purpose:        executes & displays the template results              unset($this->_tpl_vars[$tpl_var]);
193  \*======================================================================*/      }
194    
195          function display($tpl_file)      
196          {  /*======================================================================*\
197                  // compile files      Function: register_function
198                  $this->_compile($this->template_dir);      Purpose:  Registers custom function to be used in templates
199                  //assemble compile directory path to file  \*======================================================================*/
200                  $_compile_file = $this->compile_dir.'/'.$tpl_file.'.php';      function register_function($function, $function_impl)
201                        {
202                  extract($this->_tpl_vars);                        $this->custom_funcs[$function] = $function_impl;
203                  include($_compile_file);      }
204          }        
205                        
206  /*======================================================================*\  /*======================================================================*\
207          Function:       fetch()      Function: register_modifier
208          Purpose:        executes & returns the template results      Purpose:  Registers modifier to be used in templates
209  \*======================================================================*/  \*======================================================================*/
210        function register_modifier($modifier, $modifier_impl)
211          function fetch($tpl_file)      {
212          {          $this->custom_mods[$modifier] = $modifier_impl;
213                  ob_start();      }
214                  $this->display($tpl_file);  
215                  $results = ob_get_contents();      
216                  ob_end_clean();  /*======================================================================*\
217                  return $results;      Function:   clear_cache()
218          }      Purpose:    clear cached content for the given template and cache id
219    \*======================================================================*/
220  /*======================================================================*\  
221          Function:       compile()      function clear_cache($tpl_file, $cache_id = null)
222          Purpose:        called to compile the templates      {
223  \*======================================================================*/          $cache_tpl_md5 = md5(realpath($this->template_dir.'/'.$tpl_file));
224            $cache_dir = $this->cache_dir.'/'.$cache_tpl_md5;
225          function _compile($tpl_dir)          
226          {          if (!is_dir($cache_dir))
227                  if($this->compile_check)              return false;
228                  {  
229                          if($this->_traverse_files($tpl_dir, 0))          if (isset($cache_id)) {
230                                  return true;              $cache_id_md5 = md5($cache_id);
231                          else              $cache_id_dir = substr($cache_id_md5, 0, 2);
232                                  return false;              $cache_file = "$cache_dir/$cache_id_dir/{$cache_tpl_md5}_$cache_id_md5.cache";
233                  }              return (bool)(is_file($cache_file) && unlink($cache_file));
234                  else          } else
235                          return false;              return $this->_clear_tpl_cache_dir($cache_tpl_md5);
236          }      }
237        
238  /*======================================================================*\      
239          Function:       _traverse_files()  /*======================================================================*\
240          Purpose:        traverse the template files & process each one      Function:   clear_all_cache()
241  \*======================================================================*/      Purpose:    clear the entire contents of cache (all templates)
242    \*======================================================================*/
243          function _traverse_files($tpl_dir, $depth)  
244          {      function clear_all_cache()
245                  if(is_dir($tpl_dir)) {      {
246                          if($tpl_dir)          if (!is_dir($this->cache_dir))
247                                  $dir_handle = opendir($tpl_dir);              return false;
248    
249                          while($curr_file = readdir($dir_handle)) {          $dir_handle = opendir($this->cache_dir);
250                                  if ($curr_file == '.' || $curr_file == '..')          while ($curr_dir = readdir($dir_handle)) {
251                                          continue;              if ($curr_dir == '.' || $curr_dir == '..' ||
252                    !is_dir($this->cache_dir.'/'.$curr_dir))
253                                  $filepath = $tpl_dir."/".$curr_file;                  continue;
254                                  if(is_readable($filepath)) {  
255                                          if(is_file($filepath) && preg_match('!' . preg_quote($this->tpl_file_ext, '!') . '$!', $curr_file)) {              $this->_clear_tpl_cache_dir($curr_dir);
256                                                  if(!$this->_process_file($filepath))          }
257                                                          return false;          closedir($dir_handle);
258                                          } else if(is_dir($filepath)) {  
259                                                  if(!$this->_traverse_files($filepath, $depth + 1))          return true;
260                                                          return false;      }
261                                          } else {  
262                                                  // invalid file type, skipping  
263                                                  $this->_set_error_msg("Invalid filetype for $filepath, skipping");  /*======================================================================*\
264                                                  continue;      Function:   is_cached()
265                                          }      Purpose:    test to see if valid cache exists for this template
266                                  }  \*======================================================================*/
267                          }  
268                  } else {      function is_cached($tpl_file, $cache_id = null)
269                          $this->_set_error_msg("Directory \"$tpl_dir\" does not exist or is not a directory.");      {
270                          return false;          if (!$this->caching)
271                  }              return false;
272    
273                  return true;          // cache name = template path + cache_id
274          }          $cache_tpl_md5 = md5(realpath($this->template_dir.'/'.$tpl_file));
275            $cache_id_md5 = md5($cache_id);
276  /*======================================================================*\          $cache_id_dir = substr($cache_id_md5, 0, 2);
277          Function:       _process_file()          $cache_file = $this->cache_dir."/$cache_tpl_md5/$cache_id_dir/{$cache_tpl_md5}_$cache_id_md5.cache";
278          Input:          test template files for modifications  
279                                  and execute the compilation for each          if (file_exists($cache_file) &&
280                                  one requiring it.              ($this->cache_lifetime == 0 ||
281  \*======================================================================*/               (time() - filemtime($cache_file) <= $this->cache_lifetime)))
282                return true;
283          function _process_file($filepath)          else
284          {              return false;
285                  if(preg_match("/^(.+)\/([^\/]+)$/", $filepath, $match)) {          
286                          $tpl_file_dir = $match[1];                            }
287                          $tpl_file_name = $match[2] . ".php";      
288            
289                          $compile_dir = preg_replace('!^' . preg_quote($this->template_dir, '!') . '!',  /*======================================================================*\
290                                                                                  $this->compile_dir, $match[1]);      Function:   clear_all_assign()
291        Purpose:    clear all the assigned template variables.
292                          //create directory if none exists  \*======================================================================*/
293                          if(!file_exists($compile_dir)) {  
294                                  $compile_dir_parts = preg_split('!/+!', $compile_dir);      function clear_all_assign()
295                                  $new_dir = "";      {
296                                  foreach ($compile_dir_parts as $dir_part) {          $this->_tpl_vars = array();
297                                          $new_dir .= $dir_part."/";      }
298                                          if (!file_exists($new_dir) && !mkdir($new_dir, 0755)) {  
299                                                  $this->_set_error_msg("problem creating directory \"$compile_dir\"");  
300                                                  return false;                            /*======================================================================*\
301                                          }      Function: get_template_vars
302                                  }      Purpose:  Returns an array containing template variables
303                          }  \*======================================================================*/
304        function &get_template_vars()
305                          // compile the template file if none exists or has been modified      {
306                          if (!file_exists($compile_dir."/".$tpl_file_name) ||          return $this->_tpl_vars;
307                                  ($this->_modified_file($filepath, $compile_dir."/".$tpl_file_name))) {      }
308                                  if (!$this->_compile_file($filepath, $compile_dir."/".$tpl_file_name))  
309                                          return false;                            
310                          } else {  /*======================================================================*\
311                                  // no compilation needed      Function:   display()
312                                  return true;      Purpose:    executes & displays the template results
313                          }  \*======================================================================*/
314                  } else {  
315                          $this->_set_error_msg("problem matching \"$filepath.\"");      function display($tpl_file, $cache_id = null)
316                          return false;      {
317                  }          $this->fetch($tpl_file, $cache_id, true);
318        }  
319                  return true;          
320          }  /*======================================================================*\
321        Function:   fetch()
322  /*======================================================================*\      Purpose:    executes & returns or displays the template results
323          Function:       _modified_file()  \*======================================================================*/
324          Input:          return comparison of modification times of files  
325  \*======================================================================*/      function fetch($tpl_file, $cache_id = null, $display = false)
326        {
327          function _modified_file($filepath, $compilepath)          global $HTTP_SERVER_VARS;
328          {  
329                  if(filemtime($filepath) >= filemtime($compilepath))          if ($this->caching) {
330                          return true;              // cache name = template path + cache_id
331                  return false;              $cache_tpl_md5 = md5(realpath($this->template_dir.'/'.$tpl_file));
332          }              $cache_id_md5 = md5($cache_id);
333                $cache_id_dir = substr($cache_id_md5, 0, 2);
334  /*======================================================================*\              $cache_file = $this->cache_dir."/$cache_tpl_md5/$cache_id_dir/{$cache_tpl_md5}_$cache_id_md5.cache";
335          Function:       _compile_file()              
336          Input:          compile a template file              if (file_exists($cache_file) &&
337  \*======================================================================*/                  ($this->cache_lifetime == 0 ||
338                     (time() - filemtime($cache_file) <= $this->cache_lifetime))) {
339          function _compile_file($filepath, $compilepath)                  $results = $this->_read_file($cache_file);
340          {                  $results = $this->_process_cached_inserts($results);
341                  if (!($template_contents = $this->_read_file($filepath)))                  if ($display) {
342                          return false;                      echo $results;
343                        return;
344                  $this->_current_file = str_replace($this->template_dir . "/", "", $filepath);                  } else
345                  $this->_current_line_no = 1;                      return $results;
346                  $ldq = preg_quote($this->left_delimiter, '!');              }
347                  $rdq = preg_quote($this->right_delimiter, '!');          }
348    
349                  /* Pull out the literal blocks. */          // compile files
350                  preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_contents, $match);          $this->_compile($this->template_dir);
351                  $this->_literal_blocks = $match[1];          //assemble compile directory path to file
352                  $template_contents = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s",          $_compile_file = $this->compile_dir."/".$tpl_file.".php";
353                                                                                    '{literal}', $template_contents);  
354            extract($this->_tpl_vars);
355                  /* Gather all template tags. */  
356                  preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_contents, $match);          // if we just need to display the results, don't perform output
357                  $template_tags = $match[1];          // buffering - for speed
358                  /* Split content by template tags to obtain non-template content. */          if ($display && !$this->caching)
359                  $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_contents);              include($_compile_file);
360                  if(!$this->allow_php) {          else {
361                          /* Escape php tags. */              ob_start();
362                          $text_blocks = preg_replace('!<\?([^?]*?)\?>!', '&lt;?$1?&gt;', $text_blocks);              include($_compile_file);
363                  }              $results = ob_get_contents();
364                ob_end_clean();
365                  /* Compile the template tags into PHP code. */          }
366                  $compiled_tags = array();  
367                  for ($i = 0; $i < count($template_tags); $i++) {          if($this->caching) {
368                          $this->_current_line_no += substr_count($text_blocks[$i], "\n");              $this->_write_file($cache_file, $results, true);
369                          $compiled_tags[] = $this->_compile_tag($template_tags[$i]);              $results = $this->_process_cached_inserts($results);
370                          $this->_current_line_no += substr_count($template_tags[$i], "\n");          }
371                  }  
372            if ($display) {
373                  $compiled_contents = "";              echo $results;
374                                return;
375                  /* Interleave the compiled contents and text blocks to get the final result. */          } else
376                  for ($i = 0; $i < count($compiled_tags); $i++) {              return $results;
377                          $compiled_contents .= $text_blocks[$i].$compiled_tags[$i];      }  
378                  }      
379                  $compiled_contents .= $text_blocks[$i];  /*======================================================================*\
380                        Function:   compile()
381                  /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */      Purpose:    called to compile the templates
382                  if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_contents, $match)) {  \*======================================================================*/
383                          $strip_tags = $match[0];  
384                          $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);      function _compile($tpl_dir)
385                          $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);      {
386                          for ($i = 0; $i < count($strip_tags); $i++)          if($this->compile_check || $this->force_compile)
387                                  $compiled_contents = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",          {
388                                                                                                    $strip_tags_modified[$i], $compiled_contents, 1);              if($this->_traverse_files($tpl_dir, 0))
389                  }                  return true;
390                else
391                  if(!$this->_write_file($compilepath, $compiled_contents))                  return false;
392                          return false;          } else
393                return false;
394                  return true;      }  
395          }      
396    /*======================================================================*\
397          function _compile_tag($template_tag)      Function:   _traverse_files()
398          {      Purpose:    traverse the template files & process each one
399                  /* Matched comment. */  \*======================================================================*/
400                  if ($template_tag{0} == '*' && $template_tag{strlen($template_tag)-1} == '*')  
401                          return "";      function _traverse_files($tpl_dir, $depth)
402        {
403                  /* Split tag into two parts: command and the arguments. */          $retval = true;
404                  preg_match('/^(  
405                                             (?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" | \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' | (?>[^"\'\s]+))+          if (is_dir($tpl_dir)) {
406                                            )              $dir_handle = opendir($tpl_dir);
407                                        (?:\s+(.*))?              while ($curr_file = readdir($dir_handle)) {
408                                          /xs', $template_tag, $match);                  if ($curr_file == '.' || $curr_file == '..')
409                  list(, $tag_command, $tag_args) = $match;                      continue;
410    
411                  /* If the tag name matches a variable or section property definition,                  $filepath = $tpl_dir.'/'.$curr_file;
412                     we simply process it. */                  if (is_readable($filepath)) {
413                  if (preg_match('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:[^|]+)?)*$!', $tag_command) ||      // if a variable                      if (is_file($filepath) && substr($curr_file, -strlen($this->tpl_file_ext)) == $this->tpl_file_ext) {
414                          preg_match('!^#(\w+)#(?>\|@?\w+(:[^|]+)?)*$!', $tag_command)            ||  // or a configuration variable                          if (!$this->_process_file($filepath)) {
415                          preg_match('!^%\w+\.\w+%(?>\|@?\w+(:[^|]+)?)*$!', $tag_command)) {    // or a section property                              $retval = false;
416                          settype($tag_command, 'array');                              break;
417                          $this->_parse_vars_props($tag_command);                          }
418                          return "<?php print $tag_command[0]; ?>";                      } else if (is_dir($filepath)) {
419                  }                          if (!$this->_traverse_files($filepath, $depth + 1)) {
420                                $retval = false;
421                  switch ($tag_command) {                              break;
422                          case 'include':                          }
423                                  return $this->_compile_include_tag($tag_args);                      } else {
424                            // invalid file type, skipping
425                          case 'if':                          $this->_set_error_msg("Invalid filetype for $filepath, skipping");
426                                  return $this->_compile_if_tag($tag_args);                          continue;
427                        }
428                          case 'else':                  }
429                                  return '<?php else: ?>';              }
430    
431                          case 'elseif':              closedir($dir_handle);
432                                  return $this->_compile_if_tag($tag_args, true);              return $retval;
433            } else {
434                          case '/if':              $this->_set_error_msg("Directory \"$tpl_dir\" does not exist or is not a directory.");
435                                  return '<?php endif; ?>';              return false;
436            }
437                          case 'ldelim':      }
438                                  return $this->left_delimiter;  
439    /*======================================================================*\
440                          case 'rdelim':      Function:   _process_file()
441                                  return $this->right_delimiter;      Input:      test template files for modifications
442                    and execute the compilation for each
443                          case 'section':                  one requiring it.
444                                  array_push($this->_sectionelse_stack, false);  \*======================================================================*/
445                                  return $this->_compile_section_start($tag_args);  
446        function _process_file($filepath)
447                          case 'sectionelse':      {
448                                  $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;          if(preg_match("/^(.+)\/([^\/]+)$/", $filepath, $match)) {
449                                  return "<?php endfor; else: ?>";              $tpl_file_dir = $match[1];          
450                $tpl_file_name = $match[2] . '.php';
451                          case '/section':  
452                                  if (array_pop($this->_sectionelse_stack))              $compile_dir = preg_replace('!^' . preg_quote($this->template_dir, '!') . '!',
453                                          return "<?php endif; ?>";                                          $this->compile_dir, $match[1]);
454                                  else  
455                                          return "<?php endfor; endif; ?>";              //create directory if none exists
456                $this->_create_dir_structure($compile_dir);
457                          case 'config_load':  
458                                  return $this->_compile_config_load_tag($tag_args);              // compile the template file if none exists or has been modified or recompile is forced
459                if ($this->force_compile || !file_exists($compile_dir."/".$tpl_file_name) ||
460                          case 'strip':                  ($this->_modified_file($filepath, $compile_dir."/".$tpl_file_name))) {
461                          case '/strip':                  if (!$this->_compile_file($filepath, $compile_dir."/".$tpl_file_name))
462                                  return $this->left_delimiter.$tag_command.$this->right_delimiter;                      return false;
463                } else {
464                          case 'literal':                  // no compilation needed
465                                  list (,$literal_block) = each($this->_literal_blocks);                  return true;
466                                  $this->_current_line_no += substr_count($literal_block, "\n");              }
467                                  return $literal_block;          } else {
468                $this->_set_error_msg("problem matching \"$filepath.\"");
469                          case 'insert':              return false;
470                                  return $this->_compile_insert_tag($tag_args);          }
471    
472                          default:          return true;
473                                  if (isset($this->custom_funcs[$tag_command])) {      }
474                                          return $this->_compile_custom_tag($tag_command, $tag_args);  
475                                  } else {  /*======================================================================*\
476                                          $this->_syntax_error("unknown tag - '$tag_command'", E_USER_WARNING);      Function: _create_dir_structure
477                                          return "";      Purpose:  create full directory structure
478                                  }  \*======================================================================*/
479                  }      function _create_dir_structure($dir)
480          }      {
481            if (!file_exists($dir)) {
482                $dir_parts = preg_split('!/+!', $dir, -1, PREG_SPLIT_NO_EMPTY);
483          function _compile_custom_tag($tag_command, $tag_args)              $new_dir = ($dir{0} == '/') ? '/' : '';
484          {              foreach ($dir_parts as $dir_part) {
485                  $attrs = $this->_parse_attrs($tag_args);                  $new_dir .= $dir_part;
486                  $function = $this->custom_funcs[$tag_command];                  if (!file_exists($new_dir) && !mkdir($new_dir, 0755)) {
487                  foreach ($attrs as $arg_name => $arg_value) {                      $this->_set_error_msg("problem creating directory \"$dir\"");
488                          if (is_bool($arg_value))                      return false;              
489                                  $arg_value = $arg_value ? 'true' : 'false';                  }
490                          $arg_list[] = "'$arg_name' => $arg_value";                  $new_dir .= '/';
491                  }              }
492            }
493                  return "<?php $function(array(".implode(',', (array)$arg_list).")); ?>";      }
494          }  
495    /*======================================================================*\
496        Function:   _modified_file()
497          function _compile_insert_tag($tag_args)      Input:      return comparison of modification times of files
498          {  \*======================================================================*/
499                  $attrs = $this->_parse_attrs($tag_args);  
500                  $name = substr($attrs['name'], 1, -1);      function _modified_file($filepath, $compilepath)
501        {
502                  if (empty($name)) {          if (filemtime($filepath) >= filemtime($compilepath))
503                          $this->_syntax_error("missing insert name");              return true;
504                  }          return false;
505        }
506                  foreach ($attrs as $arg_name => $arg_value) {  
507                          if ($arg_name == 'name') continue;  /*======================================================================*\
508                          if (is_bool($arg_value))      Function:   _compile_file()
509                                  $arg_value = $arg_value ? 'true' : 'false';      Input:      compile a template file
510                          $arg_list[] = "'$arg_name' => $arg_value";  \*======================================================================*/
511                  }  
512        function _compile_file($filepath, $compilepath)
513                  return "<?php print insert_$name(array(".implode(',', (array)$arg_list).")); ?>";      {
514          }          if (!($template_contents = $this->_read_file($filepath)))
515                return false;
516    
517          function _compile_config_load_tag($tag_args)          $this->_current_file = str_replace($this->template_dir . '/', '', $filepath);
518          {          $this->_current_line_no = 1;
519                  $attrs = $this->_parse_attrs($tag_args);          $ldq = preg_quote($this->left_delimiter, '!');
520            $rdq = preg_quote($this->right_delimiter, '!');
521                  if (empty($attrs['file'])) {  
522                          $this->_syntax_error("missing 'file' attribute in config_load tag");          /* Pull out the literal blocks. */
523                  }          preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_contents, $match);
524            $this->_literal_blocks = $match[1];
525                  $output  = "<?php if (!class_exists('Config_File'))\n" .          $template_contents = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s",
526                                     "    include_once 'Config_File.class.php';\n" .                                            '{literal}', $template_contents);
527                             "if (!is_object(\$_conf_obj) || get_class(\$_conf_obj) != 'config_file') {\n" .  
528                                     "    \$_conf_obj  = new Config_File('".$this->config_dir."');\n" .          /* Gather all template tags. */
529                             "}\n" .          preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_contents, $match);
530                                     "\$_config = array_merge((array)\$_config, \$_conf_obj->get(".$attrs['file']."));\n";          $template_tags = $match[1];
531            /* Split content by template tags to obtain non-template content. */
532                  if (!empty($attrs['section']))          $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_contents);
533                          $output .= '$_config = array_merge((array)$_config, $_conf_obj->get('.$attrs['file'].', '.$attrs['section'].')); ';  
534            /* TODO: speed up the following with preg_replace and /F once we require that version of PHP */
535                  $output .= '?>';          
536            /* loop through text blocks */
537                  return $output;          for ($curr_tb = 0; $curr_tb <= count($text_blocks); $curr_tb++) {
538          }              /* match anything within <? ?> */
539                if (preg_match_all('!(<\?[^?]*?\?>|<script\s+language\s*=\s*[\"\']?php[\"\']?\s*>)!is', $text_blocks[$curr_tb], $sp_match)) {
540                    /* found at least one match, loop through each one */
541          function _compile_include_tag($tag_args)                  for ($curr_sp = 0; $curr_sp < count($sp_match[0]); $curr_sp++) {
542          {                      if (preg_match('!^(<\?(php\s|\s|=\s)|<script\s*language\s*=\s*[\"\']?php[\"\']?\s*>)!is', $sp_match[0][$curr_sp])) {
543                  $attrs = $this->_parse_attrs($tag_args);                          /* php tag */
544                            if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
545                  if (empty($attrs['file'])) {                              /* echo php contents */
546                          $this->_syntax_error("missing 'file' attribute in include tag");                              $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '<?php echo \''.str_replace("'", "\'", $sp_match[0][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
547                  } else                         } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
548                          $attrs['file'] = $this->_dequote($attrs['file']);                              /* quote php tags */
549                                $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], htmlspecialchars($sp_match[0][$curr_sp]), $text_blocks[$curr_tb]);
550                  if (count($attrs) > 1) {                          } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
551                          $include_func_name = uniqid("_include_");                              /* remove php tags */
552                          $include_file_name = $this->compile_dir.'/'.$attrs['file'];                              if (substr($sp_match[0][$curr_sp], 0, 2) == '<?')
553                                    $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '', $text_blocks[$curr_tb]);
554                          foreach ($attrs as $arg_name => $arg_value) {                              else
555                                  if ($arg_name == 'file') continue;                                  /* attempt to remove everything between <script ...> and </script> */
556                                  if (is_bool($arg_value))                                  $text_blocks[$curr_tb] = preg_replace('!'.preg_quote($sp_match[0][$curr_sp], '!').'.*?</script\s*>!is', '', $text_blocks[$curr_tb]);
557                                          $arg_value = $arg_value ? 'true' : 'false';                          }
558                                  $arg_list[] = "'$arg_name' => $arg_value";                      } else
559                          }                          /* echo the non-php tags */
560                            $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '<?php echo \''.str_replace("'", "\'", $sp_match[0][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
561                          return  "<?php " .                  }
562                                          "function $include_func_name(\$file_name, \$def_vars, \$include_vars)\n" .              }
563                                          "{\n" .          }
564                                          "       extract(\$def_vars);\n" .                        
565                                          "       extract(\$include_vars);\n" .          /* Compile the template tags into PHP code. */
566                                          "       include \"\$file_name.php\";\n" .          $compiled_tags = array();
567                                          "}\n" .          for ($i = 0; $i < count($template_tags); $i++) {
568                                          "$include_func_name(\"$include_file_name\", get_defined_vars(), array(".implode(',', (array)$arg_list)."));\n?>\n";              $this->_current_line_no += substr_count($text_blocks[$i], "\n");
569                  } else              $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
570                           return '<?php include "'.$this->compile_dir.'/'.$attrs['file'].'.php"; ?>';              $this->_current_line_no += substr_count($template_tags[$i], "\n");
571          }          }
572    
573          function _compile_section_start($tag_args)          $compiled_contents = '';
574          {          
575                  $attrs = $this->_parse_attrs($tag_args);          /* Interleave the compiled contents and text blocks to get the final result. */
576            for ($i = 0; $i < count($compiled_tags); $i++) {
577                  $output = "<?php ";              $compiled_contents .= $text_blocks[$i].$compiled_tags[$i];
578                  $section_name = $attrs['name'];          }
579                  if (empty($section_name)) {          $compiled_contents .= $text_blocks[$i];
580                          $this->_syntax_error("missing section name");          
581                  }          /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
582            if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_contents, $match)) {
583                  $output .= "unset(\$_sections[$section_name]);\n";              $strip_tags = $match[0];
584                  $section_props = "\$_sections[$section_name]['properties']";              $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);
585                $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
586                  foreach ($attrs as $attr_name => $attr_value) {              for ($i = 0; $i < count($strip_tags); $i++)
587                          switch ($attr_name) {                  $compiled_contents = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
588                                  case 'loop':                                                    $strip_tags_modified[$i], $compiled_contents, 1);
589                                          $output .= "{$section_props}['loop'] = is_array($attr_value) ? count($attr_value) : $attr_value;\n";          }
590                                          break;  
591            if(!$this->_write_file($compilepath, $compiled_contents))
592                                  case 'show':              return false;
593                                          if (is_bool($attr_value))  
594                                                  $attr_value = $attr_value ? 'true' : 'false';          return true;
595                                          $output .= "{$section_props}['$attr_name'] = $attr_value;\n";      }
596                                          break;  
597    /*======================================================================*\
598                                  default:      Function: _process_cached_inserts
599                                          $output .= "{$section_props}['$attr_name'] = $attr_value;\n";      Purpose:  Replace cached inserts with the actual results
600                                          break;  \*======================================================================*/
601                          }      function _process_cached_inserts($results)
602                  }      {
603            preg_match_all('!'.$this->_smarty_md5.'{insert_cache (.*)}'.$this->_smarty_md5.'!Uis',
604                  if (isset($attrs['loop'])) {                         $results, $match);
605                          $loop_check_code = "{$section_props}['loop'] > 0 && ";          list($cached_inserts, $insert_args) = $match;
606                  } else {  
607                          $output .= "{$section_props}['loop'] = 1; #-fix- \n";          for ($i = 0; $i < count($cached_inserts); $i++) {
608                  }              $args = unserialize($insert_args[$i]);
609                $name = $args['name'];
610                  if (isset($attrs['show'])) {              unset($args['name']);
611                          $show_check_code = "{$section_props}['show'] && ";  
612                  } else {              $function_name = 'insert_' . $name;
613                          $output .= "{$section_props}['show'] = {$section_props}['loop'] > 0;\n";              $replace = $function_name($args);
614                  }  
615                $results = str_replace($cached_inserts[$i], $replace, $results);
616                  $output .= "if ($loop_check_code $show_check_code true): ";          }
617    
618                  $output .= "          return $results;
619                          for ({$section_props}['index'] = 0;      }  
620                                   {$section_props}['index'] < {$section_props}['loop'];  
621                                   {$section_props}['index']++):\n";  
622                  $output .= "{$section_props}['rownum'] = {$section_props}['index'] + 1;\n";  /*======================================================================*\
623        Function: _compile_tag
624                  $output .= "?>\n";      Purpose:  Compile a template tag
625    \*======================================================================*/
626                  return $output;      function _compile_tag($template_tag)
627          }      {
628            /* Matched comment. */
629          function _compile_if_tag($tag_args, $elseif = false)          if ($template_tag{0} == '*' && $template_tag{strlen($template_tag)-1} == '*')
630          {              return '';
631                  /* Tokenize args for 'if' tag. */  
632                  preg_match_all('/(?:          $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
633                                                   "[^"\\\\]*(?:\\\\.[^"\\\\]*)*"                 | # match all double quoted strings allowed escaped double quotes  
634                                                   \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'             | # match all single quoted strings allowed escaped single quotes          /* Split tag into two parts: command and the arguments. */
635                                                   [()]                                                                   | # match parentheses          preg_match('/^(
636                                                   [^"\'\s()]+                                                      # match any other token that is not any of the above                         (?: ' . $qstr_regexp . ' | (?>[^"\'\s]+))+
637                                                  )/x', $tag_args, $match);                        )
638                  $tokens = $match[0];                        (?:\s+(.*))?
639                        /xs', $template_tag, $match);
640                  $this->_parse_vars_props($tokens);          list(, $tag_command, $tag_args) = $match;
641    
642                  $is_arg_stack = array();          /* If the tag name matches a variable or section property definition,
643                               we simply process it. */
644                  for ($i = 0; $i < count($tokens); $i++) {          if (preg_match('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command) ||   // if a variable
645                          $token = &$tokens[$i];              preg_match('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command)     ||  // or a configuration variable
646                          switch ($token) {              preg_match('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command)) {    // or a section property
647                                  case 'eq':              settype($tag_command, 'array');
648                                          $token = '==';              $this->_parse_vars_props($tag_command);
649                                          break;              return "<?php echo $tag_command[0]; ?>\n";
650            }
651                                  case 'ne':  
652                                  case 'neq':          switch ($tag_command) {
653                                          $token = '!=';              case 'include':
654                                          break;                  return $this->_compile_include_tag($tag_args);
655    
656                                  case 'lt':              case 'if':
657                                          $token = '<';                  return $this->_compile_if_tag($tag_args);
658                                          break;  
659                case 'else':
660                                  case 'le':                  return '<?php else: ?>';
661                                  case 'lte':  
662                                          $token = '<=';              case 'elseif':
663                                          break;                  return $this->_compile_if_tag($tag_args, true);
664    
665                                  case 'gt':              case '/if':
666                                          $token = '>';                  return '<?php endif; ?>';
667                                          break;  
668                                                case 'ldelim':
669                                  case 'ge':                  return $this->left_delimiter;
670                                  case 'gte':  
671                                          $token = '>=';              case 'rdelim':
672                                          break;                  return $this->right_delimiter;
673    
674                                  case 'and':              case 'section':
675                                          $token = '&&';                  array_push($this->_sectionelse_stack, false);
676                                          break;                  return $this->_compile_section_start($tag_args);
677    
678                                  case 'or':              case 'sectionelse':
679                                          $token = '||';                  $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
680                                          break;                  return "<?php endfor; else: ?>";
681    
682                                  case 'not':              case '/section':
683                                          $token = '!';                  if (array_pop($this->_sectionelse_stack))
684                                          break;                      return "<?php endif; ?>";
685                    else
686                                  case 'mod':                      return "<?php endfor; endif; ?>";
687                                          $token = '%';  
688                                          break;              case 'config_load':
689                    return $this->_compile_config_load_tag($tag_args);
690                                  case '(':  
691                                          array_push($is_arg_stack, $i);              case 'strip':
692                                          break;              case '/strip':
693                    return $this->left_delimiter.$tag_command.$this->right_delimiter;
694                                  case 'is':  
695                                          /* If last token was a ')', we operate on the parenthesized              case 'literal':
696                                             expression. The start of the expression is on the stack.                  list (,$literal_block) = each($this->_literal_blocks);
697                                             Otherwise, we operate on the last encountered token. */                  $this->_current_line_no += substr_count($literal_block, "\n");
698                                          if ($tokens[$i-1] == ')')                  return $literal_block;
699                                                  $is_arg_start = array_pop($is_arg_stack);  
700                                          else              case 'insert':
701                                                  $is_arg_start = $i-1;                  return $this->_compile_insert_tag($tag_args);
702                                          /* Construct the argument for 'is' expression, so it knows  
703                                             what to operate on. */              default:
704                                          $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));                  if (isset($this->custom_funcs[$tag_command])) {
705                        return $this->_compile_custom_tag($tag_command, $tag_args);
706                                          /* Pass all tokens from next one until the end to the                  } else {
707                                             'is' expression parsing function. The function will                      $this->_syntax_error("unknown tag - '$tag_command'", E_USER_WARNING);
708                                             return modified tokens, where the first one is the result                      return;
709                                             of the 'is' expression and the rest are the tokens it                  }
710                                             didn't touch. */          }
711                                          $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));      }
712    
713                                          /* Replace the old tokens with the new ones. */      function _compile_custom_tag($tag_command, $tag_args)
714                                          array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);      {
715            $function = $this->custom_funcs[$tag_command];
716                                          /* Adjust argument start so that it won't change from the  
717                                             current position for the next iteration. */          if (!function_exists($function)) {
718                                          $i = $is_arg_start;              $this->_syntax_error("custom function '$tag_command' is not implemented", E_USER_WARNING);
719                                          break;              return;
720                          }          }
721                  }          
722            $attrs = $this->_parse_attrs($tag_args);
723                  if ($elseif)          foreach ($attrs as $arg_name => $arg_value) {
724                          return '<?php elseif ('.implode(' ', $tokens).'): ?>';              if (is_bool($arg_value))
725                  else                  $arg_value = $arg_value ? 'true' : 'false';
726                          return '<?php if ('.implode(' ', $tokens).'): ?>';              $arg_list[] = "'$arg_name' => $arg_value";
727          }          }
728    
729          function _parse_is_expr($is_arg, $tokens)          return "<?php $function(array(".implode(',', (array)$arg_list).")); ?>";
730          {      }
731                  $expr_end = 0;  
732    
733                  if (($first_token = array_shift($tokens)) == 'not') {  /*======================================================================*\
734                          $negate_expr = true;      Function: _compile_insert_tag
735                          $expr_type = array_shift($tokens);      Purpose:  Compile {insert ...} tag
736                  } else  \*======================================================================*/
737                          $expr_type = $first_token;      function _compile_insert_tag($tag_args)
738        {
739                  switch ($expr_type) {          $attrs = $this->_parse_attrs($tag_args);
740                          case 'even':          $name = substr($attrs['name'], 1, -1);
741                                  if ($tokens[$expr_end] == 'by') {  
742                                          $expr_end++;          if (empty($name)) {
743                                          $expr_arg = $tokens[$expr_end++];              $this->_syntax_error("missing insert name");
744                                          $expr = "!(($is_arg / $expr_arg) % $expr_arg)";          }
745                                  }  
746                                  else          foreach ($attrs as $arg_name => $arg_value) {
747                                          $expr = "!($is_arg % 2)";              if (is_bool($arg_value))
748                                  break;                  $arg_value = $arg_value ? 'true' : 'false';
749                $arg_list[] = "'$arg_name' => $arg_value";
750                          case 'odd':          }
751                                  if ($tokens[$expr_end] == 'by') {  
752                                          $expr_end++;          return "<?php echo _smarty_insert_handler(array(".implode(', ', (array)$arg_list)."), \$this->caching, \$this->_smarty_md5); ?>\n";
753                                          $expr_arg = $tokens[$expr_end++];      }  
754                                          $expr = "(($is_arg / $expr_arg) % $expr_arg)";  
755                                  }  
756                                  else  /*======================================================================*\
757                                          $expr = "($is_arg % 2)";      Function: _compile_config_load_tag
758                                  break;      Purpose:  Compile {config_load ...} tag
759    \*======================================================================*/
760                          case 'div':      function _compile_config_load_tag($tag_args)
761                                  if ($tokens[$expr_end] == 'by') {      {
762                                          $expr_end++;          $attrs = $this->_parse_attrs($tag_args);
763                                          $expr_arg = $tokens[$expr_end++];  
764                                          $expr = "!($is_arg % $expr_arg)";          if (empty($attrs['file'])) {
765                                  } else {              $this->_syntax_error("missing 'file' attribute in config_load tag");
766                                          $this->_syntax_error("expecting 'by' after 'div'");          }
767                                  }  
768                                  break;          $output  = "<?php if (!class_exists('Config_File'))\n" .
769                       "    include_once 'Config_File.class.php';\n" .
770                          default:                     "if (!is_object(\$_conf_obj) || get_class(\$_conf_obj) != 'config_file') {\n" .
771                                  $this->_syntax_error("unknown 'is' expression - '$expr_type'");                     "    \$_conf_obj  = new Config_File('".$this->config_dir."');\n" .
772                                  break;                     "}\n" .
773                  }                     "\$_config = array_merge((array)\$_config, \$_conf_obj->get(".$attrs['file']."));\n";
774    
775                  if ($negate_expr) {          if (!empty($attrs['section']))
776                          $expr = "!($expr)";              $output .= '$_config = array_merge((array)$_config, $_conf_obj->get('.$attrs['file'].', '.$attrs['section'].')); ';
777                  }  
778            $output .= '?>';
779                  array_splice($tokens, 0, $expr_end, $expr);  
780            return $output;
781                  return $tokens;      }
782          }  
783    
784          function _parse_attrs($tag_args)  /*======================================================================*\
785          {      Function: _compile_include_tag
786                  /* Tokenize tag attributes. */      Purpose:  Compile {include ...} tag
787                  preg_match_all('/(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"       |  \*======================================================================*/
788                                                    \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'     | (?>[^"\'=\s]+)      function _compile_include_tag($tag_args)
789                                                   )+ |      {
790                                                   [=]          $attrs = $this->_parse_attrs($tag_args);
791                                              /x', $tag_args, $match);  
792                  $tokens = $match[0];          if (empty($attrs['file'])) {
793                  $var_delims = array('$', '#', '%');              $this->_syntax_error("missing 'file' attribute in include tag");
794            } else
795                  $attrs = array();              $attrs['file'] = $this->_dequote($attrs['file']);
796                  /* Parse state:  
797                          0 - expecting attr name          if (count($attrs) > 1) {
798                          1 - expecting '=' or another attr name              $include_func_name = uniqid("_include_");
799                          2 - expecting attr value (not '=') */              $include_file_name = $this->compile_dir.'/'.$attrs['file'];
800                  $state = 0;  
801                foreach ($attrs as $arg_name => $arg_value) {
802                  foreach ($tokens as $token) {                  if ($arg_name == 'file') continue;
803                          switch ($state) {                  if (is_bool($arg_value))
804                                  case 0:                      $arg_value = $arg_value ? 'true' : 'false';
805                                          /* If the token is a valid identifier, we set attribute name                  $arg_list[] = "'$arg_name' => $arg_value";
806                                             and go to state 1. */              }
807                                          if (preg_match('!\w+!', $token)) {  
808                                                  $attr_name = $token;              return  "<?php " .
809                                                  $state = 1;                      "if (!function_exists('$include_func_name')) {\n".
810                                          } else                      "   function $include_func_name(\$file_name, \$def_vars, \$include_vars)\n" .
811                                                  $this->_syntax_error("invalid attribute name - '$token'");                      "   {\n" .
812                                          break;                      "       extract(\$def_vars);\n" .
813                        "       extract(\$include_vars);\n" .
814                                  case 1:                      "       include \"\$file_name.php\";\n" .
815                                          /* If the token is a valid identifier, the previously set                      "   }\n" .
816                                             attribute name does not need an argument. We put it in                      "}\n" .
817                                             the attrs array, set the new attribute name to the                      "$include_func_name(\"$include_file_name\", get_defined_vars(), array(".implode(',', (array)$arg_list)."));\n?>";
818                                             current token and don't switch state.          } else
819                 return '<?php include "'.$this->compile_dir.'/'.$attrs['file'].'.php"; ?>';
820                                             If the token is '=', then we go to state 2. */      }
821                                          if ($token == '=') {  
822                                                  $state = 2;  
823                                          } else  /*======================================================================*\
824                                                  $this->_syntax_error("expecting '=' after attribute name");      Function: _compile_section_start
825                                          break;      Purpose:  Compile {section ...} tag
826    \*======================================================================*/
827                                  case 2:      function _compile_section_start($tag_args)
828                                          /* If token is not '=', we set the attribute value and go to      {
829                                             state 0. */          $attrs = $this->_parse_attrs($tag_args);
830                                          if ($token != '=') {  
831                                                  /* We booleanize the token if it's a non-quoted possible          $output = "<?php ";
832                                                     boolean value. */          $section_name = $attrs['name'];
833                                                  if (preg_match('!^(on|yes|true)$!', $token))          if (empty($section_name)) {
834                                                          $token = true;              $this->_syntax_error("missing section name");
835                                                  else if (preg_match('!^(off|no|false)$!', $token))          }
836                                                          $token = false;  
837                                                  /* If the token is not variable (doesn't start with          $output .= "unset(\$_sections[$section_name]);\n";
838                                                     '$', '#', or '%') and not enclosed in single or          $section_props = "\$_sections[$section_name]['properties']";
839                                                     double quotes we single-quote it. */  
840                                                  else if (!in_array($token{0}, $var_delims) &&          foreach ($attrs as $attr_name => $attr_value) {
841                                                                   !(($token{0} == '"' || $token[0] == "'") &&              switch ($attr_name) {
842                                                                   $token{strlen($token)-1} == $token{0}))                  case 'loop':
843                                                          $token = "'".$token."'";                      $output .= "{$section_props}['loop'] = is_array($attr_value) ? count($attr_value) : $attr_value;\n";
844                        break;
845                                                  $attrs[$attr_name] = $token;  
846                                                  $state = 0;                  case 'show':
847                                          } else                      if (is_bool($attr_value))
848                                                  $this->_syntax_error("'=' cannot be an attribute value");                          $attr_value = $attr_value ? 'true' : 'false';
849                                          break;                      $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
850                          }                      break;
851                  }  
852                    default:
853                  $this->_parse_vars_props($attrs);                      $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
854                        break;
855                  return $attrs;              }
856          }          }
857    
858          function _preg_grep($pattern, $array)          if (isset($attrs['loop'])) {
859          {              $loop_check_code = "{$section_props}['loop'] > 0 && ";
860                  $result = array();          } else {
861                $output .= "{$section_props}['loop'] = 1;\n";
862                  foreach ($array as $key => $entry) {          }
863                          if (preg_match($pattern, $entry))  
864                                  $result[$key] = $entry;          if (isset($attrs['show'])) {
865                  }              $show_check_code = "{$section_props}['show'] && ";
866            } else {
867                  return $result;              $output .= "{$section_props}['show'] = {$section_props}['loop'] > 0;\n";
868          }          }
869            
870          function _parse_vars_props(&$tokens)          $output .= "if ($loop_check_code $show_check_code true): ";
871          {  
872                  /* preg_grep() was fixed to return keys properly in 4.0.4 and later. To          $output .= "
873                     allow people to use older versions of PHP we emulate preg_grep() and              for ({$section_props}['index'] = 0;
874                     use the version check to see what function to call. */                   {$section_props}['index'] < {$section_props}['loop'];
875                  if (strnatcmp(PHP_VERSION, '4.0.4') >= 0) {                   {$section_props}['index']++):\n";
876                          $var_exprs = preg_grep('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:[^|]+)?)*$!', $tokens);          $output .= "{$section_props}['rownum'] = {$section_props}['index'] + 1;\n";
877                          $conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:[^|]+)?)*$!', $tokens);  
878                          $sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:[^|]+)?)*$!', $tokens);          $output .= "?>";
879                  } else {  
880                          $var_exprs = $this->_preg_grep('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:[^|]+)?)*$!', $tokens);          return $output;
881                          $conf_var_exprs = $this->_preg_grep('!^#(\w+)#(?>\|@?\w+(:[^|]+)?)*$!', $tokens);      }
882                          $sect_prop_exprs = $this->_preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:[^|]+)?)*$!', $tokens);  
883                  }  
884    /*======================================================================*\
885                  if (count($var_exprs)) {      Function: _compile_if_tag
886                          foreach ($var_exprs as $expr_index => $var_expr) {      Purpose:  Compile {if ...} tag
887                                  $tokens[$expr_index] = $this->_parse_var($var_expr);  \*======================================================================*/
888                          }      function _compile_if_tag($tag_args, $elseif = false)
889                  }      {
890                            /* Tokenize args for 'if' tag. */
891                  if (count($conf_var_exprs)) {          preg_match_all('/(?:
892                          foreach ($conf_var_exprs as $expr_index => $var_expr) {                           "[^"\\\\]*(?:\\\\.[^"\\\\]*)*"         | # match all double quoted strings allowed escaped double quotes
893                                  $tokens[$expr_index] = $this->_parse_conf_var($var_expr);                           \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'     | # match all single quoted strings allowed escaped single quotes
894                          }                           [()]                                   | # match parentheses
895                  }                           [^"\'\s()]+                              # match any other token that is not any of the above
896                            )/x', $tag_args, $match);
897                  if (count($sect_prop_exprs)) {          $tokens = $match[0];
898                          foreach ($sect_prop_exprs as $expr_index => $section_prop_expr) {  
899                                  $tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr);          $this->_parse_vars_props($tokens);
900                          }  
901                  }          $is_arg_stack = array();
902          }          
903            for ($i = 0; $i < count($tokens); $i++) {
904          function _parse_var($var_expr)              $token = &$tokens[$i];
905          {              switch ($token) {
906                  $modifiers = explode('|', substr($var_expr, 1));                  case 'eq':
907                        $token = '==';
908                  $sections = explode('/', array_shift($modifiers));                      break;
909                  $props = explode('.', array_pop($sections));  
910                  $var_name = array_shift($props);                  case 'ne':
911                    case 'neq':
912                  $output = "\$$var_name";                      $token = '!=';
913                        break;
914                  foreach ($sections as $section) {  
915                          $output .= "[\$_sections['$section']['properties']['index']]";                  case 'lt':
916                  }                      $token = '<';
917                  foreach ($props as $prop) {                      break;
918                          $output .= "['$prop']";  
919                  }                  case 'le':
920                    case 'lte':
921                  $this->_parse_modifiers($output, $modifiers);                      $token = '<=';
922                        break;
923                  return $output;  
924          }                  case 'gt':
925                        $token = '>';
926          function _parse_conf_var($conf_var_expr)                      break;
927          {                  
928                  $modifiers = explode('|', $conf_var_expr);                  case 'ge':
929                    case 'gte':
930                  $var_name = substr(array_shift($modifiers), 1, -1);                      $token = '>=';
931                        break;
932                  $output = "\$_config['$var_name']";  
933                    case 'and':
934                  $this->_parse_modifiers($output, $modifiers);                      $token = '&&';
935                        break;
936                  return $output;  
937          }                  case 'or':
938                        $token = '||';
939          function _parse_section_prop($section_prop_expr)                      break;
940          {  
941                  $modifiers = explode('|', $section_prop_expr);                  case 'not':
942                        $token = '!';
943                  preg_match('!%(\w+)\.(\w+)%!', array_shift($modifiers), $match);                      break;
944                  $section_name = $match[1];  
945                  $prop_name = $match[2];                  case 'mod':
946                        $token = '%';
947                  $output = "\$_sections['$section_name']['properties']['$prop_name']";                      break;
948    
949                  $this->_parse_modifiers($output, $modifiers);                  case '(':
950                        array_push($is_arg_stack, $i);
951                  return $output;                      break;
952          }  
953                    case 'is':
954          function _parse_modifiers(&$output, $modifiers)                      /* If last token was a ')', we operate on the parenthesized
955          {                         expression. The start of the expression is on the stack.
956                  foreach ($modifiers as $modifier) {                         Otherwise, we operate on the last encountered token. */
957                          $modifier = explode(':', $modifier);                      if ($tokens[$i-1] == ')')
958                          $modifier_name = array_shift($modifier);                          $is_arg_start = array_pop($is_arg_stack);
959                        else
960                          if ($modifier_name{0} == '@') {                          $is_arg_start = $i-1;
961                                  $map_array = 'false';                      /* Construct the argument for 'is' expression, so it knows
962                                  $modifier_name = substr($modifier_name, 1);                         what to operate on. */
963                          } else                      $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
964                                  $map_array = 'true';  
965                        /* Pass all tokens from next one until the end to the
966                          /*                         'is' expression parsing function. The function will
967                           * First we lookup the modifier function name in the registered                         return modified tokens, where the first one is the result
968                           * modifiers table.                         of the 'is' expression and the rest are the tokens it
969                           */                         didn't touch. */
970                          $mod_func_name = $this->custom_mods[$modifier_name];                      $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
971    
972                          /*                      /* Replace the old tokens with the new ones. */
973                           * If we don't find that modifier there, we assume it's just a PHP                      array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
974                           * function name.  
975                           */                      /* Adjust argument start so that it won't change from the
976                          if (!isset($mod_func_name))                         current position for the next iteration. */
977                                  $mod_func_name = $modifier_name;                      $i = $is_arg_start;
978                        break;
979                          $this->_parse_vars_props($modifier);              }
980            }
981                          if (count($modifier) > 0)  
982                                  $modifier_args = ', '.implode(', ', $modifier);          if ($elseif)
983                          else              return '<?php elseif ('.implode(' ', $tokens).'): ?>';
984                                  $modifier_args = '';          else
985                return '<?php if ('.implode(' ', $tokens).'): ?>';
986                          $output = "_smarty_mod_handler('$mod_func_name', $map_array, $output$modifier_args)";      }
987                  }  
988          }      function _parse_is_expr($is_arg, $tokens)
989                {
990            $expr_end = 0;
991  /*======================================================================*\  
992          Function: _dequote          if (($first_token = array_shift($tokens)) == 'not') {
993          Purpose:  Remove starting and ending quotes from the string              $negate_expr = true;
994  \*======================================================================*/              $expr_type = array_shift($tokens);
995          function _dequote($string)          } else
996          {              $expr_type = $first_token;
997                  if (($string{0} == "'" || $string{0} == '"') &&  
998                          $string{strlen($string)-1} == $string{0})          switch ($expr_type) {
999                          return substr($string, 1, -1);              case 'even':
1000          }                  if ($tokens[$expr_end] == 'by') {
1001                        $expr_end++;
1002                                $expr_arg = $tokens[$expr_end++];
1003  /*======================================================================*\                      $expr = "!(($is_arg / $expr_arg) % $expr_arg)";
1004          Function:       _read_file()                  }
1005          Purpose:        read in a file                  else
1006  \*======================================================================*/                      $expr = "!($is_arg % 2)";
1007                    break;
1008          function _read_file($filename)  
1009          {              case 'odd':
1010                  if(!($fd = fopen($filename, 'r'))) {                  if ($tokens[$expr_end] == 'by') {
1011                          $this->_set_error_msg("problem reading '$filename.'");                      $expr_end++;
1012                          return false;                      $expr_arg = $tokens[$expr_end++];
1013                  }                      $expr = "(($is_arg / $expr_arg) % $expr_arg)";
1014                  $contents = fread($fd, filesize($filename));                  }
1015                  fclose($fd);                  else
1016                  return $contents;                      $expr = "($is_arg % 2)";
1017          }                  break;
1018    
1019  /*======================================================================*\              case 'div':
1020          Function:       _write_file()                  if ($tokens[$expr_end] == 'by') {
1021          Purpose:        write out a file                      $expr_end++;
1022  \*======================================================================*/                      $expr_arg = $tokens[$expr_end++];
1023                        $expr = "!($is_arg % $expr_arg)";
1024          function _write_file($filename,$contents)                  } else {
1025          {                      $this->_syntax_error("expecting 'by' after 'div'");
1026                  if(!($fd = fopen($filename, 'w'))) {                  }
1027                          $this->_set_error_msg("problem writing '$filename.'");                  break;
1028                          return false;  
1029                  }              default:
1030                  fwrite($fd, $contents);                  $this->_syntax_error("unknown 'is' expression - '$expr_type'");
1031                  fclose($fd);                  break;
1032                  return true;          }
1033          }  
1034            if ($negate_expr) {
1035  /*======================================================================*\              $expr = "!($expr)";
1036          Function:       _set_error_msg()          }
1037          Purpose:        set the error message  
1038  \*======================================================================*/          array_splice($tokens, 0, $expr_end, $expr);
1039    
1040          function _set_error_msg($error_msg)          return $tokens;
1041          {      }
1042                  $this->_error_msg="smarty error: $error_msg";  
1043                  return true;  
1044          }  /*======================================================================*\
1045        Function: _parse_attrs
1046  /*======================================================================*\      Purpose:  Parse attribute string
1047          Function: _syntax_error  \*======================================================================*/
1048          Purpose:  display Smarty syntax error      function _parse_attrs($tag_args, $quote = true)
1049  \*======================================================================*/      {
1050          function _syntax_error($error_msg, $error_type = E_USER_ERROR)          /* Tokenize tag attributes. */
1051          {          preg_match_all('/(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"       |
1052                  trigger_error("Smarty: [in " . $this->_current_file . " line " .                            \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'     | (?>[^"\'=\s]+)
1053                                            $this->_current_line_no . "]: syntax error: $error_msg", $error_type);                           )+ |
1054          }                           [=]
1055                            /x', $tag_args, $match);
1056            $tokens       = $match[0];
1057            $var_delims   = array('$', '#', '%');
1058    
1059            $attrs = array();
1060            /* Parse state:
1061                0 - expecting attribute name
1062                1 - expecting '='
1063                2 - expecting attribute value (not '=') */
1064            $state = 0;
1065    
1066            foreach ($tokens as $token) {
1067                switch ($state) {
1068                    case 0:
1069                        /* If the token is a valid identifier, we set attribute name
1070                           and go to state 1. */
1071                        if (preg_match('!^\w+$!', $token)) {
1072                            $attr_name = $token;
1073                            $state = 1;
1074                        } else
1075                            $this->_syntax_error("invalid attribute name - '$token'");
1076                        break;
1077    
1078                    case 1:
1079                        /* If the token is '=', then we go to state 2. */
1080                        if ($token == '=') {
1081                            $state = 2;
1082                        } else
1083                            $this->_syntax_error("expecting '=' after attribute name");
1084                        break;
1085    
1086                    case 2:
1087                        /* If token is not '=', we set the attribute value and go to
1088                           state 0. */
1089                        if ($token != '=') {
1090                            /* We booleanize the token if it's a non-quoted possible
1091                               boolean value. */
1092                            if (preg_match('!^(on|yes|true)$!', $token))
1093                                $token = true;
1094                            else if (preg_match('!^(off|no|false)$!', $token))
1095                                $token = false;
1096                            /* If the token is not variable (doesn't start with
1097                               '$', '#', or '%') and not enclosed in single or
1098                               double quotes we single-quote it. */
1099                            else if ($quote && !in_array($token{0}, $var_delims) &&
1100                                     !(($token{0} == '"' || $token[0] == "'") &&
1101                                     $token{strlen($token)-1} == $token{0}))
1102                                $token = "'".$token."'";
1103    
1104                            $attrs[$attr_name] = $token;
1105                            $state = 0;
1106                        } else
1107                            $this->_syntax_error("'=' cannot be an attribute value");
1108                        break;
1109                }
1110            }
1111    
1112            $this->_parse_vars_props($attrs);
1113    
1114            return $attrs;
1115        }
1116    
1117    
1118    /*======================================================================*\
1119        Function: _preg_grep
1120        Purpose:  Emulate PHP's preg_grep()
1121    \*======================================================================*/
1122        function _preg_grep($pattern, $array)
1123        {
1124            $result = array();
1125    
1126            foreach ($array as $key => $entry) {
1127                if (preg_match($pattern, $entry))
1128                    $result[$key] = $entry;
1129            }
1130    
1131            return $result;
1132        }
1133        
1134        function _parse_vars_props(&$tokens)
1135        {
1136            $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
1137    
1138            /* preg_grep() was fixed to return keys properly in 4.0.4 and later. To
1139               allow people to use older versions of PHP we emulate preg_grep() and
1140               use the version check to see what function to call. */
1141            if (strnatcmp(PHP_VERSION, '4.0.4') >= 0) {
1142                $var_exprs = preg_grep('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' .  $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1143                $conf_var_exprs = preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1144                $sect_prop_exprs = preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' .  $qstr_regexp .  '|[^|]+))*)*$!', $tokens);
1145            } else {
1146                $var_exprs = $this->_preg_grep('!^\$(\w+/)*\w+(?>\.\w+)*(?>\|@?\w+(:(?>' .  $qstr_regexp .  '|[^|]+))*)*$!', $tokens);
1147                $conf_var_exprs = $this->_preg_grep('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tokens);
1148                $sect_prop_exprs = $this->_preg_grep('!^%\w+\.\w+%(?>\|@?\w+(:(?>' .  $qstr_regexp .  '|[^|]+))*)*$!', $tokens);
1149            }
1150    
1151            if (count($var_exprs)) {
1152                foreach ($var_exprs as $expr_index => $var_expr) {
1153                    $tokens[$expr_index] = $this->_parse_var($var_expr);
1154                }
1155            }
1156            
1157            if (count($conf_var_exprs)) {
1158                foreach ($conf_var_exprs as $expr_index => $var_expr) {
1159                    $tokens[$expr_index] = $this->_parse_conf_var($var_expr);
1160                }
1161            }
1162    
1163            if (count($sect_prop_exprs)) {
1164                foreach ($sect_prop_exprs as $expr_index => $section_prop_expr) {
1165                    $tokens[$expr_index] = $this->_parse_section_prop($section_prop_expr);
1166                }
1167            }
1168        }
1169    
1170        function _parse_var($var_expr)
1171        {
1172            list($var_ref, $modifiers) = explode('|', substr($var_expr, 1), 2);
1173    
1174            $sections = explode('/', $var_ref);
1175            $props = explode('.', array_pop($sections));
1176            $var_name = array_shift($props);
1177    
1178            $output = "\$$var_name";
1179    
1180            foreach ($sections as $section) {
1181                $output .= "[\$_sections['$section']['properties']['index']]";
1182            }
1183            foreach ($props as $prop) {
1184                $output .= "['$prop']";
1185            }
1186    
1187            $this->_parse_modifiers($output, $modifiers);
1188    
1189            return $output;
1190        }
1191    
1192        function _parse_conf_var($conf_var_expr)
1193        {
1194            list($var_ref, $modifiers) = explode('|', $conf_var_expr, 2);
1195    
1196            $var_name = substr($var_ref, 1, -1);
1197    
1198            $output = "\$_config['$var_name']";
1199    
1200            $this->_parse_modifiers($output, $modifiers);
1201    
1202            return $output;
1203        }
1204    
1205        function _parse_section_prop($section_prop_expr)
1206        {
1207            list($var_ref, $modifiers) = explode('|', $section_prop_expr, 2);
1208    
1209            preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1210            $section_name = $match[1];
1211            $prop_name = $match[2];
1212    
1213            $output = "\$_sections['$section_name']['properties']['$prop_name']";
1214    
1215            $this->_parse_modifiers($output, $modifiers);
1216    
1217            return $output;
1218        }
1219    
1220        function _parse_modifiers(&$output, $modifier_string)
1221        {
1222            $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
1223            preg_match_all('!\|(@?\w+)((?>:(?:'. $qstr_regexp . '|[^|]+))*)!', '|' . $modifier_string, $match);
1224            list(, $modifiers, $modifier_arg_strings) = $match;
1225    
1226            for ($i = 0; $i < count($modifiers); $i++) {
1227                $modifier_name = $modifiers[$i];
1228                preg_match_all('!:(' . $qstr_regexp . '|[^:]+)!', $modifier_arg_strings[$i], $match);
1229                $modifier_args = $match[1];
1230    
1231                if ($modifier_name{0} == '@') {
1232                    $map_array = 'false';
1233                    $modifier_name = substr($modifier_name, 1);
1234                } else
1235                    $map_array = 'true';
1236    
1237                /*
1238                 * First we lookup the modifier function name in the registered
1239                 * modifiers table.
1240                 */
1241                $mod_func_name = $this->custom_mods[$modifier_name];
1242    
1243                /*
1244                 * If we don't find that modifier there, we assume it's just a PHP
1245                 * function name.
1246                 */
1247                if (!isset($mod_func_name))
1248                    $mod_func_name = $modifier_name;
1249    
1250                if (!function_exists($mod_func_name)) {
1251                    $this->_syntax_error("modifier '$modifier_name' is not implemented", E_USER_WARNING);
1252                    continue;
1253                }
1254    
1255                $this->_parse_vars_props($modifier_args);
1256    
1257                if (count($modifier_args) > 0)
1258                    $modifier_args = ', '.implode(', ', $modifier_args);
1259                else
1260                    $modifier_args = '';
1261    
1262                $output = "_smarty_mod_handler('$mod_func_name', $map_array, $output$modifier_args)";
1263            }
1264        }
1265        
1266    
1267    /*======================================================================*\
1268        Function: _dequote
1269        Purpose:  Remove starting and ending quotes from the string
1270    \*======================================================================*/
1271        function _dequote($string)
1272        {
1273            if (($string{0} == "'" || $string{0} == '"') &&
1274                $string{strlen($string)-1} == $string{0})
1275                return substr($string, 1, -1);
1276            else
1277                return $string;
1278        }
1279    
1280        
1281    /*======================================================================*\
1282        Function:   _read_file()
1283        Purpose:    read in a file
1284    \*======================================================================*/
1285    
1286        function _read_file($filename)
1287    
1288        {
1289            if (!($fd = fopen($filename, 'r'))) {
1290                $this->_set_error_msg("problem reading '$filename.'");
1291                return false;
1292            }
1293            flock($fd, LOCK_SH);
1294            $contents = fread($fd, filesize($filename));
1295            fclose($fd);
1296            return $contents;
1297        }
1298    
1299    /*======================================================================*\
1300        Function:   _write_file()
1301        Purpose:    write out a file
1302    \*======================================================================*/
1303    
1304        function _write_file($filename, $contents, $create_dirs = false)
1305        {
1306            if($create_dirs)
1307                $this->_create_dir_structure(dirname($filename));
1308            
1309            if (!($fd = fopen($filename, 'a'))) {
1310                $this->_set_error_msg("problem writing '$filename.'");
1311                return false;
1312            }
1313            flock($fd, LOCK_EX);
1314    
1315            $fd_safe = fopen($filename, 'w');
1316    
1317            fwrite($fd_safe, $contents);
1318            fclose($fd_safe);
1319            fclose($fd);
1320    
1321            return true;
1322        }    
1323    
1324    
1325    /*======================================================================*\
1326        Function: _clear_tpl_cache_dir
1327        Purpose:  Clear the specified template cache
1328    \*======================================================================*/
1329        function _clear_tpl_cache_dir($cache_tpl_md5)
1330        {
1331            $cache_dir = $this->cache_dir.'/'.$cache_tpl_md5;
1332    
1333            if (!is_dir($cache_dir))
1334                return false;
1335    
1336            $dir_handle = opendir($cache_dir);
1337            while ($curr_dir = readdir($dir_handle)) {
1338                $cache_path_dir = $cache_dir.'/'.$curr_dir;
1339                if ($curr_dir == '.' || $curr_dir == '..' ||
1340                    !is_dir($cache_path_dir))
1341                    continue;
1342    
1343                $dir_handle2 = opendir($cache_path_dir);
1344                while ($curr_file = readdir($dir_handle2)) {
1345                    if ($curr_file == '.' || $curr_file == '..')
1346                        continue;
1347    
1348                    $cache_file = $cache_path_dir.'/'.$curr_file;
1349                    if (is_file($cache_file))
1350                        unlink($cache_file);
1351                }
1352                closedir($dir_handle2);
1353                @rmdir($cache_path_dir);
1354            }
1355            closedir($dir_handle);
1356            @rmdir($cache_dir);
1357    
1358            return true;
1359        }
1360    
1361    
1362    /*======================================================================*\
1363        Function:   _set_error_msg()
1364        Purpose:    set the error message
1365    \*======================================================================*/
1366    
1367        function _set_error_msg($error_msg)
1368        {
1369            $this->_error_msg="smarty error: $error_msg";
1370            return true;
1371        }
1372    
1373    /*======================================================================*\
1374        Function: _syntax_error
1375        Purpose:  display Smarty syntax error
1376    \*======================================================================*/
1377        function _syntax_error($error_msg, $error_type = E_USER_ERROR)
1378        {
1379            trigger_error("Smarty: [in " . $this->_current_file . " line " .
1380                          $this->_current_line_no . "]: syntax error: $error_msg", $error_type);
1381        }
1382  }  }
1383    
1384    /* vim: set expandtab: */
1385    
1386  ?>  ?>

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.5

  ViewVC Help
Powered by ViewVC 1.1.26