Marius Burkard
2016-04-20 4569cae57f127afd093794310ccd290d2d9fdf36
commit | author | age
e2d6ed 1 <?php
42b689 2 /**
MC 3  * vlibTemplate is a class used to seperate PHP and HTML.
4  * For instructions on how to use vlibTemplate, see the
5  * vlibTemplate.html file, located in the 'docs' directory.
6  *
7  * @since 07/03/2002
8  * @author Kelvin Jones <kelvin@kelvinjones.co.uk>
9  * @package vLIB
10  * @access public
11  * @see vlibTemplate.html
12  */
13
14
e2d6ed 15 /* vim: set expandtab tabstop=4 shiftwidth=4: */
T 16 // +----------------------------------------------------------------------+
17 // | PHP version 4.0                                                      |
18 // +----------------------------------------------------------------------+
19 // | Copyright (c) 2002 Active Fish Group                                 |
20 // +----------------------------------------------------------------------+
21 // | Authors: Kelvin Jones <kelvin@kelvinjones.co.uk>                     |
22 // +----------------------------------------------------------------------+
23 //
24 // $Id: class.tpl.inc.php,v 1.1 2003/07/08 12:31:10 platinum Exp $
25
42b689 26 //** check and avoid multiple loading of class
e2d6ed 27 if (!defined('vlibTemplateClassLoaded')) {
42b689 28
7fe908 29     define('vlibTemplateClassLoaded', 1);
42b689 30     include_once ISPC_CLASS_PATH.'/tpl_error.inc.php';
MC 31     include_once ISPC_CLASS_PATH.'/tpl_ini.inc.php';
e2d6ed 32
42b689 33     class tpl{
7fe908 34
MC 35         /*-----------------------------------------------------------------------------\
42b689 36         |                                 ATTENTION                                    |
MC 37         |  Do not touch the following variables. vlibTemplate will not work otherwise. |
38         \-----------------------------------------------------------------------------*/
39         private $OPTIONS = array(
7fe908 40             'MAX_INCLUDES'          =>   10,
MC 41             'TEMPLATE_DIR'          => null,
42             'GLOBAL_VARS'           => null,
43             'GLOBAL_CONTEXT_VARS'   => null,
44             'LOOP_CONTEXT_VARS'     => null,
45             'SET_LOOP_VAR'          => null,
46             'DEFAULT_ESCAPE'        => null,
47             'STRICT'                => null,
48             'CASELESS'              => null,
49             'UNKNOWNS'              => null,
50             'TIME_PARSE'            => null,
51             'ENABLE_PHPINCLUDE'     => null,
52             'INCLUDE_PATHS'         => array(),
53             'CACHE_DIRECTORY'       => null,
54             'CACHE_LIFETIME'        => null,
55             'CACHE_EXTENSION'       => null
56         );
e2d6ed 57
7fe908 58         /** open and close tags used for escaping */
42b689 59         private $ESCAPE_TAGS = array(
MC 60             'html'      => array('open' => 'htmlspecialchars('    , 'close'=> ', ENT_QUOTES)'),
61             'url'       => array('open' => 'urlencode('           , 'close'=> ')'),
62             'rawurl'    => array('open' => 'rawurlencode('        , 'close'=> ')'),
63             'sq'        => array('open' => 'addcslashes('         , 'close'=> ", \"'\")"),
64             'dq'        => array('open' => 'addcslashes('         , 'close'=> ", '\"')"),
65             '1'         => array('open' => 'htmlspecialchars('    , 'close'=> ', ENT_QUOTES)'),
66             '0'         => array('open' => ''                     , 'close'=> ''),
67             'none'      => array('open' => ''                     , 'close'=> ''),
68             'hex'       => array('open' => '$this->_escape_hex('  , 'close'=> ', false)'),
69             'hexentity' => array('open' => '$this->_escape_hex('  , 'close'=> ', true)')
7fe908 70         );
e2d6ed 71
42b689 72
MC 73
7fe908 74         /** open and close tags used for formatting */
42b689 75         private $FORMAT_TAGS = array(
MC 76             'strtoupper' => array('open' => 'strtoupper(',          'close'=> ')'),
77             'uc'         => array('open' => 'strtoupper(',          'close'=> ')'),
78             'strtolower' => array('open' => 'strtolower(',          'close'=> ')'),
79             'lc'         => array('open' => 'strtolower(',          'close'=> ')'),
80             'ucfirst'    => array('open' => 'ucfirst(',             'close'=> ')'),
81             'lcucfirst'  => array('open' => 'ucfirst(strtolower(',  'close'=> '))'),
82             'ucwords'    => array('open' => 'ucwords(',             'close'=> ')'),
83             'lcucwords'  => array('open' => 'ucwords(strtolower(',  'close'=> '))')
7fe908 84         );
e2d6ed 85
7fe908 86         /** operators allowed when using extended TMPL_IF syntax */
42b689 87         private $allowed_if_ops = array('==', '!=', '<>', '<', '>', '<=', '>=');
MC 88
89
e2d6ed 90
7fe908 91         /** dbs allowed by vlibTemplate::setDbLoop(). */
42b689 92         private $allowed_loop_dbs = array('MYSQL', 'POSTGRESQL', 'INFORMIX', 'INTERBASE', 'INGRES',
7fe908 93             'MSSQL', 'MSQL', 'OCI8', 'ORACLE', 'OVRIMOS', 'SYBASE');
e2d6ed 94
42b689 95
MC 96
7fe908 97         /** root directory of vlibTemplate automagically filled in */
42b689 98         private $VLIBTEMPLATE_ROOT = null;
MC 99
100
e2d6ed 101
7fe908 102         /** contains current directory used when doing recursive include */
42b689 103         private $_currentincludedir = array();
MC 104
105
e2d6ed 106
7fe908 107         /** current depth of includes */
42b689 108         private $_includedepth = 0;
MC 109
110
e2d6ed 111
7fe908 112         /** full path to tmpl file */
42b689 113         private $_tmplfilename = null;
MC 114
115
e2d6ed 116
7fe908 117         /** file data before it's parsed */
42b689 118         private $_tmplfile = null;
MC 119
120
e2d6ed 121
7fe908 122         /** parsed version of file, ready for eval()ing */
42b689 123         private $_tmplfilep = null;
MC 124
125
e2d6ed 126
7fe908 127         /** eval()ed version ready for printing or whatever */
42b689 128         private $_tmploutput = null;
MC 129
130
e2d6ed 131
7fe908 132         /** array for variables to be kept */
42b689 133         private $_vars = array();
MC 134
135
e2d6ed 136
7fe908 137         /** array where loop variables are kept */
42b689 138         private $_arrvars = array();
e2d6ed 139
7fe908 140         /** array which holds the current namespace during parse */
42b689 141         private $_namespace = array();
MC 142
143
e2d6ed 144
7fe908 145         /** variable is set to true once the template is parsed, to save re-parsing everything */
42b689 146         private $_parsed = false;
MC 147
148
e2d6ed 149
7fe908 150         /** array holds all unknowns vars */
42b689 151         private $_unknowns = array();
MC 152
153
e2d6ed 154
7fe908 155         /** microtime when template parsing began */
42b689 156         private $_firstparsetime = null;
MC 157
158
e2d6ed 159
7fe908 160         /** total time taken to parse template */
42b689 161         private $_totalparsetime = null;
MC 162
163
e2d6ed 164
7fe908 165         /** name of current loop being passed in */
42b689 166         private $_currloopname = null;
MC 167
168
e2d6ed 169
7fe908 170         /** rows with the above loop */
42b689 171         private $_currloop = array();
MC 172
173
e2d6ed 174
7fe908 175         /** define vars to avoid warnings */
42b689 176         private $_debug = null;
MC 177         private $_cache = null;
7fe908 178
MC 179
180
181         /** array which holds the dynamic Includes */
42b689 182         private $_dyninclude = array();
MC 183
7fe908 184         /*-----------------------------------------------------------------------------\
42b689 185         |                           public functions                                   |
MC 186         \-----------------------------------------------------------------------------*/
e2d6ed 187
7fe908 188
MC 189
190
191
192         /**
193          * Usually called by the class constructor.
194          * Stores the filename in $this->_tmplfilename.
195          * Raises an error if the template file is not found.
196          * @param string $tmplfile full path to template file
197          * @return boolean true
198          * @access public
199          */
42b689 200         public function newTemplate($tmplfile)
MC 201         {
202             if (!$tfile = $this->_fileSearch($tmplfile)){
203                 vlibTemplateError::raiseError('VT_ERROR_NOFILE', KILL, $tmplfile);
204             }
7fe908 205
42b689 206             //* make sure that any parsing vars are cleared for the new template
7fe908 207             $this->_tmplfile = null;
MC 208             $this->_tmplfilep = null;
209             $this->_tmploutput = null;
210             $this->_parsed = false;
211             $this->_unknowns = array();
212             $this->_firstparsetime = null;
213             $this->_totalparsetime = null;
214
42b689 215             //* reset debug module
MC 216             if ($this->_debug){
217                 $this->_debugReset();
218             }
7fe908 219             $this->_tmplfilename = $tfile;
MC 220             return true;
221         }
222
223         /**
224          * Sets variables to be used by the template
225          * If $k is an array, then it will treat it as an associative array
226          * using the keys as variable names and the values as variable values.
227          * @param mixed $k key to define variable name
228          * @param mixed $v variable to assign to $k
229          * @return boolean true/false
230          * @access public
231          */
42b689 232         public function setVar($k, $v = null)
MC 233         {
7fe908 234             if (is_array($k)) {
MC 235                 foreach($k as $key => $value){
236                     $key = ($this->OPTIONS['CASELESS']) ? strtolower(trim($key)) : trim($key);
237                     if (preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $key) && $value !== null ) {
238                         $this->_vars[$key] = $value;
239                     }
240                 }
42b689 241             } else {
7fe908 242                 if (preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $k) && $v !== null) {
MC 243                     if ($this->OPTIONS['CASELESS']) $k = strtolower($k);
244                     $this->_vars[trim($k)] = $v;
42b689 245                 } else {
7fe908 246                     return false;
MC 247                 }
248             }
249             return true;
250         }
251
252
253
254         /**
255          * Sets dynamic includes to be used by the template
256          * If $k is an array, then it will treat it as an associative array
257          * using the keys as variable names and the values as variable values.
258          * @param mixed $k key to define variable name
259          * @param mixed $v variable to assign to $k
260          * @return boolean true/false
261          * @access public
262          */
42b689 263         public function setInclude($k, $v = null)
MC 264         {
7fe908 265             if(is_array($k)) {
e2d6ed 266                 foreach($k as $key => $val) {
T 267                     $this->_dyninclude[$key] = $val;
268                 }
269             } else {
270                 $this->_dyninclude[$k] = $v;
271             }
272             return true;
273         }
274
7fe908 275         /**
MC 276          * Unsets a variable which has already been set
277          * Parse in all vars wanted for deletion in seperate parametres
278          * @param string var name to remove use: vlibTemplate::unsetVar(var[, var..])
279          * @return boolean true/false returns true unless called with 0 params
280          * @access public
281          */
42b689 282         public function unsetVar()
MC 283         {
7fe908 284             $num_args = func_num_args();
MC 285             if ($num_args < 1)  return false;
e2d6ed 286
7fe908 287             for ($i = 0; $i < $num_args; $i++) {
MC 288                 $var = func_get_arg($i);
289                 if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
290                 if (!preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $var)) continue;
291                 unset($this->_vars[$var]);
292             }
293             return true;
294         }
e2d6ed 295
7fe908 296         /**
MC 297          * Gets all vars currently set in global namespace.
298          * @return array
299          * @access public
300          */
eca44a 301         public function getVars()
MC 302         {
303             return empty($this->_vars) ? false : $this->_vars;
7fe908 304         }
e2d6ed 305
7fe908 306         /**
MC 307          * Gets a single var from the global namespace
308          * @return var
309          * @access public
310          */
42b689 311         public function getVar($var)
MC 312         {
7fe908 313             if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
42b689 314             return (empty($var) || !isset($this->_vars[$var])) ? false : $this->_vars[$var];
7fe908 315         }
e2d6ed 316
7fe908 317         /**
MC 318          * sets the GLOBAL_CONTEXT_VARS
319          * @return true
320          * @access public
321          */
42b689 322         public function setContextVars()
MC 323         {
7fe908 324             $_phpself = @$GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'];
MC 325             $_pathinfo = @$GLOBALS['HTTP_SERVER_VARS']['PATH_INFO'];
326             $_request_uri = @$GLOBALS['HTTP_SERVER_VARS']['REQUEST_URI'];
327             $_qs   = @$GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING'];
328
42b689 329             //* the following fixes bug of $PHP_SELF on Win32 CGI and IIS.
7fe908 330             $_self = (!empty($_pathinfo)) ? $_pathinfo : $_phpself;
MC 331             $_uri  = (!empty($_request_uri)) ? $_request_uri : $_self.'?'.$_qs;
332
333             $this->setvar('__SELF__', $_self);
334             $this->setvar('__REQUEST_URI__', $_uri);
335             return true;
336         }
337
338         /**
339          * Builds the loop construct for use with <TMPL_LOOP>.
340          * @param string $k string to define loop name
341          * @param array $v array to assign to $k
342          * @return boolean true/false
343          * @access public
344          */
42b689 345         public function setLoop($k, $v)
MC 346         {
7fe908 347             if (is_array($v) && preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $k)) {
MC 348                 $k = ($this->OPTIONS['CASELESS']) ? strtolower(trim($k)) : trim($k);
349                 $this->_arrvars[$k] = array();
350                 if ($this->OPTIONS['SET_LOOP_VAR'] && !empty($v)) $this->setvar($k, 1);
351                 if (($this->_arrvars[$k] = $this->_arrayBuild($v)) == false) {
352                     vlibTemplateError::raiseError('VT_WARNING_INVALID_ARR', WARNING, $k);
eca44a 353                 } else {
MC 354                     $this->vars['_'.$k.'_num'] = count($v);
7fe908 355                 }
MC 356             }
357             return true;
358         }
359
360         /**
42b689 361          * [** EXPERIMENTAL **]
7fe908 362          * Function to create a loop from a Db result resource link.
MC 363          * @param string $loopname to commit loop. If not set, will use last loopname set using newLoop()
364          * @param string $result link to a Db result resource
365          * @param string $db_type, type of db that the result resource belongs to.
366          * @return boolean true/false
367          * @access public
368          */
42b689 369         public function setDbLoop($loopname, $result, $db_type = 'MYSQL')
MC 370         {
7fe908 371             /*
42b689 372             $db_type = strtoupper($db_type);
e2d6ed 373             if (!in_array($db_type, $this->allowed_loop_dbs)) {
42b689 374                 vlibTemplateError::raiseError('VT_WARNING_INVALID_LOOP_DB', WARNING, $db_type);
e2d6ed 375                 return false;
T 376             }
377
378             $loop_arr = array();
42b689 379             // TODO: Are all these necessary as were onyl using mysql and possible postgres ? - pedro
e2d6ed 380             switch ($db_type) {
T 381
382                 case 'MYSQL':
383                     if (get_resource_type($result) != 'mysql result') {
42b689 384                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 385                         return false;
T 386                     }
387                     while($r = mysql_fetch_assoc($result)) {
388                         $loop_arr[] = $r;
389                     }
42b689 390                     break;
e2d6ed 391
T 392                 case 'POSTGRESQL':
393                     if (get_resource_type($result) != 'pgsql result') {
42b689 394                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 395                         return false;
T 396                     }
397
398                     $nr = (function_exists('pg_num_rows')) ? pg_num_rows($result) : pg_numrows($result);
399
400                     for ($i=0; $i < $nr; $i++) {
401                         $loop_arr[] = pg_fetch_array($result, $i, PGSQL_ASSOC);
402                     }
42b689 403                     break;
e2d6ed 404
T 405                 case 'INFORMIX':
406                     if (!$result) {
42b689 407                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 408                         return false;
T 409                     }
410                     while($r = ifx_fetch_row($result, 'NEXT')) {
411                         $loop_arr[] = $r;
412                     }
42b689 413                     break;
e2d6ed 414
T 415                 case 'INTERBASE':
416                     if (get_resource_type($result) != 'interbase result') {
42b689 417                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 418                         return false;
T 419                     }
420                     while($r = ibase_fetch_row($result)) {
421                         $loop_arr[] = $r;
422                     }
42b689 423                     break;
e2d6ed 424
T 425                 case 'INGRES':
426                     if (!$result) {
42b689 427                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 428                         return false;
T 429                     }
430                     while($r = ingres_fetch_array(INGRES_ASSOC, $result)) {
431                         $loop_arr[] = $r;
432                     }
42b689 433                     break;
e2d6ed 434
T 435                 case 'MSSQL':
436                     if (get_resource_type($result) != 'mssql result') {
42b689 437                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 438                         return false;
T 439                     }
440                     while($r = mssql_fetch_array($result)) {
441                         $loop_arr[] = $r;
442                     }
42b689 443                     break;
e2d6ed 444
T 445                 case 'MSQL':
446                     if (get_resource_type($result) != 'msql result') {
42b689 447                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 448                         return false;
T 449                     }
450                     while($r = msql_fetch_array($result, MSQL_ASSOC)) {
451                         $loop_arr[] = $r;
452                     }
42b689 453                     break;
e2d6ed 454
T 455                 case 'OCI8':
456                     if (get_resource_type($result) != 'oci8 statement') {
42b689 457                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 458                         return false;
T 459                     }
460                     while(OCIFetchInto($result, &$r, OCI_ASSOC+OCI_RETURN_LOBS)) {
461                         $loop_arr[] = $r;
462                     }
42b689 463                     break;
e2d6ed 464
T 465                 case 'ORACLE':
466                     if (get_resource_type($result) != 'oracle Cursor') {
42b689 467                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 468                         return false;
T 469                     }
470                     while(ora_fetch_into($result, &$r, ORA_FETCHINTO_ASSOC)) {
471                         $loop_arr[] = $r;
472                     }
42b689 473                     break;
e2d6ed 474
T 475                 case 'OVRIMOS':
476                     if (!$result) {
42b689 477                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 478                         return false;
T 479                     }
480                     while(ovrimos_fetch_into($result, &$r, 'NEXT')) {
481                         $loop_arr[] = $r;
482                     }
42b689 483                     break;
e2d6ed 484
T 485                 case 'SYBASE':
486                     if (get_resource_type($result) != 'sybase-db result') {
42b689 487                         vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
e2d6ed 488                         return false;
T 489                     }
490
491                     while($r = sybase_fetch_array($result)) {
492                         $loop_arr[] = $r;
493                     }
42b689 494                     break;
e2d6ed 495             }
T 496             $this->setLoop($loopname, $loop_arr);
497             return true;
b7bf0c 498             */
7fe908 499         }
e2d6ed 500
7fe908 501         /**
MC 502          * Sets the name for the curent loop in the 3 step loop process.
503          * @param string $name string to define loop name
504          * @return boolean true/false
505          * @access public
506          */
42b689 507         public function newLoop($loopname)
MC 508         {
7fe908 509             if (preg_match('/^[a-z_]+[a-z0-9_]*$/i', $loopname)) {
MC 510                 $this->_currloopname[$loopname] = $loopname;
511                 $this->_currloop[$loopname] = array();
512                 return true;
42b689 513             } else {
7fe908 514                 return false;
MC 515             }
516         }
e2d6ed 517
7fe908 518         /**
MC 519          * Adds a row to the current loop in the 3 step loop process.
520          * @param array $row loop row to add to current loop
521          * @param string $loopname loop to which you want to add row, if not set will use last loop set using newLoop().
522          * @return boolean true/false
523          * @access public
524          */
42b689 525         public function addRow($row, $loopname = null)
MC 526         {
7fe908 527             if (!$loopname) $loopname = $this->_currloopname[(count($this->_currloopname)-1)];
MC 528
529             if (!isset($this->_currloop[$loopname]) || empty($this->_currloopname)) {
530                 vlibTemplateError::raiseError('VT_WARNING_LOOP_NOT_SET', WARNING);
531                 return false;
532             }
533             if (is_array($row)) {
534                 $this->_currloop[$loopname][] = $row;
535                 return true;
42b689 536             } else {
7fe908 537                 return false;
MC 538             }
539         }
e2d6ed 540
7fe908 541         /**
MC 542          * Completes the 3 step loop process. This assigns the rows and resets
543          * the variables used.
544          * @param string $loopname to commit loop. If not set, will use last loopname set using newLoop()
545          * @return boolean true/false
546          * @access public
547          */
42b689 548         public function addLoop($loopname = null)
MC 549         {
7fe908 550             if ($loopname == null) { // add last loop used
MC 551                 if (!empty($this->_currloop)) {
552                     foreach ($this->_currloop as $k => $v) {
553                         $this->setLoop($k, $v);
554                         unset($this->_currloop[$k]);
555                     }
556                     $this->_currloopname = array();
557                     return true;
42b689 558                 } else {
7fe908 559                     return false;
MC 560                 }
42b689 561             } elseif (!isset($this->_currloop[$loopname]) || empty($this->_currloopname)) { // newLoop not yet envoked
7fe908 562                 vlibTemplateError::raiseError('VT_WARNING_LOOP_NOT_SET', WARNING);
MC 563                 return false;
42b689 564             } else { // add a specific loop
7fe908 565                 $this->setLoop($loopname, $this->_currloop[$loopname]);
MC 566                 unset($this->_currloopname[$loopname], $this->_currloop[$loopname]);
567             }
568             return true;
569         }
e2d6ed 570
7fe908 571         /**
MC 572          * Unsets a loop which has already been set.
573          * Can only unset top level loops.
574          * @param string loop to remove use: vlibTemplate::unsetLoop(loop[, loop..])
575          * @return boolean true/false returns true unless called with 0 params
576          * @access public
577          */
42b689 578         public function unsetLoop()
MC 579         {
7fe908 580             $num_args = func_num_args();
MC 581             if ($num_args < 1) return false;
e2d6ed 582
7fe908 583             for ($i = 0; $i < $num_args; $i++) {
MC 584                 $var = func_get_arg($i);
585                 if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
586                 if (!preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $var)) continue;
587                 unset($this->_arrvars[$var]);
588             }
589             return true;
590         }
591
592         /**
593          * Resets the vlibTemplate object. After using vlibTemplate::reset() you must
594          * use vlibTemplate::newTemplate(tmpl) to reuse, not passing in the options array.
595          * @return boolean true
596          * @access public
597          */
42b689 598         public function reset()
MC 599         {
7fe908 600             $this->clearVars();
MC 601             $this->clearLoops();
602             $this->_tmplfilename = null;
603             $this->_tmplfile = null;
604             $this->_tmplfilep = null;
605             $this->_tmploutput = null;
606             $this->_parsed = false;
607             $this->_unknowns = array();
608             $this->_firstparsetime = null;
609             $this->_totalparsetime = null;
610             $this->_currloopname = null;
611             $this->_currloop = array();
612             return true;
613         }
614
615         /**
616          * Unsets all variables in the template
617          * @return boolean true
618          * @access public
619          */
42b689 620         public function clearVars()
MC 621         {
7fe908 622             $this->_vars = array();
MC 623             return true;
624         }
625
626         /**
627          * Unsets all loops in the template
628          * @return boolean true
629          * @access public
630          */
42b689 631         public function clearLoops()
MC 632         {
7fe908 633             $this->_arrvars = array();
MC 634             $this->_currloopname = null;
635             $this->_currloop = array();
636             return true;
637         }
638
639         /**
640          * Unsets all variables and loops set using setVar/Loop()
641          * @return boolean true
642          * @access public
643          */
42b689 644         public function clearAll()
MC 645         {
7fe908 646             $this->clearVars();
MC 647             $this->clearLoops();
648             return true;
649         }
650
651         /**
652          * Returns true if unknowns were found after parsing.
653          * Function MUST be called AFTER one of the parsing functions to have any relevance.
654          * @return boolean true/false
655          * @access public
656          */
42b689 657         public function unknownsExist()
MC 658         {
7fe908 659             return !empty($this->_unknowns);
MC 660         }
661
662         /**
663          * Alias for unknownsExist.
664          * @access public
665          */
42b689 666         public function unknowns()
MC 667         {
7fe908 668             return $this->unknownsExist();
MC 669         }
670
671         /**
672          * Returns an array of all unknown vars found when parsing.
673          * This function is only relevant after parsing a document.
674          * @return array
675          * @access public
676          */
42b689 677         public function getUnknowns()
MC 678         {
7fe908 679             return $this->_unknowns;
MC 680         }
681
682         /**
683          * Sets how you want to handle variables that were found in the
684          * template but not set in vlibTemplate using vlibTemplate::setVar().
685          * @param  string $arg ignore, remove, print, leave or comment
686          * @return boolean
687          * @access public
688          */
42b689 689         public function setUnknowns($arg)
MC 690         {
7fe908 691             $arg = strtolower(trim($arg));
MC 692             if (preg_match('/^ignore|remove|print|leave|comment$/', $arg)) {
693                 $this->OPTIONS['UNKNOWNS'] = $arg;
694                 return true;
695             }
696             return false;
697         }
698
699         /**
700          * function sets the paths to use when including files.
701          * Use of this function: vlibTemplate::setPath(string path [, string path, ..]);
702          * i.e. if $tmpl is your template object do: $tmpl->setPath('/web/htdocs/templates','/web/htdocs/www');
703          * with as many paths as you like.
704          * if this function is called without any arguments, it will just delete any previously set paths.
705          *
706          * @param string path (mulitple)
707          * @return bool success
708          * @access public
709          */
42b689 710         public function setPath()
MC 711         {
7fe908 712             $num_args = func_num_args();
MC 713             if ($num_args < 1) {
714                 $this->OPTIONS['INCLUDE_PATHS'] = array();
715                 return true;
716             }
717             for ($i = 0; $i < $num_args; $i++) {
718                 $thispath = func_get_arg($i);
719                 array_push($this->OPTIONS['INCLUDE_PATHS'], realpath($thispath));
720             }
721             return true;
722         }
723
724         /**
725          * After using one of the parse functions, this will allow you
726          * access the time taken to parse the template.
727          * see OPTION 'TIME_PARSE'.
728          *
729          * @return float time taken to parse template
730          * @access public
731          */
42b689 732         public function getParseTime()
MC 733         {
7fe908 734             if ($this->OPTIONS['TIME_PARSE'] && $this->_parsed) {
MC 735                 return $this->_totalparsetime;
736             }
737             return false;
738         }
739
740
741         /**
742          * Identical to pparse() except that it uses output buffering w/ gz compression thus
743          * printing the output directly and compressed if poss.
744          * Will possibly if parsing a huge template.
745          *
746          * @access public
747          * @return boolean true/false
748          */
42b689 749         public function fastPrint()
MC 750         {
7fe908 751             $ret = $this->_parse('ob_gzhandler');
MC 752             print($this->_tmploutput);
753             return $ret;
754         }
755
756
757         /**
758          * Calls parse, and then prints out $this->_tmploutput
759          * @access public
760          * @return boolean true/false
761          */
42b689 762         public function pparse()
MC 763         {
7fe908 764             if (!$this->_parsed) $this->_parse();
MC 765             print($this->_tmploutput);
766             return true;
767         }
768
769         /**
770          * Alias for pparse()
771          * @access public
772          */
42b689 773         public function pprint()
MC 774         {
7fe908 775             return $this->pparse();
MC 776         }
777
778
779         /**
780          * Returns the parsed output, ready for printing, passing to mail() ...etc.
781          * Invokes $this->_parse() if template has not yet been parsed.
782          *
783          * @access public
784          * @return boolean true/false
785          */
42b689 786         public function grab()
MC 787         {
7fe908 788             if (!$this->_parsed) $this->_parse();
MC 789             return $this->_tmploutput;
790         }
791
792         /*-----------------------------------------------------------------------------\
42b689 793         |                           private functions                                  |
MC 794         \-----------------------------------------------------------------------------*/
e2d6ed 795
7fe908 796         /**
MC 797          * vlibTemplate constructor.
798          * if $tmplfile has been passed to it, it will send to $this->newTemplate()
799          * @param string $tmplfile full path to template file
800          * @param array $options see above
801          * @return boolean true/false
802          * @access private
803          */
42b689 804         public function __construct($tmplfile = null, $options = null)
MC 805         {
7fe908 806             if (is_array($tmplfile) && $options == null) {
MC 807                 $options = $tmplfile;
808                 unset($tmplfile);
809             }
e2d6ed 810
7fe908 811             $this->VLIBTEMPLATE_ROOT = dirname(realpath(__FILE__));
e2d6ed 812
7fe908 813             if (is_array(vlibIni::vlibTemplate())) {
MC 814                 foreach (vlibIni::vlibTemplate() as $name => $val) {
815                     $this->OPTIONS[$name] = $val;
816                 }
817             }
e2d6ed 818
7fe908 819             if (is_array($options)) {
MC 820                 foreach($options as $key => $val) {
821                     $key = strtoupper($key);
822                     if ($key == 'PATH') {
823                         $this->setPath($val);
42b689 824                     } else {
7fe908 825                         $this->_setOption($key, strtolower($val));
MC 826                     }
827                 }
828             }
829             if($tmplfile) $this->newTemplate($tmplfile);
830             if ($this->OPTIONS['GLOBAL_CONTEXT_VARS']) $this->setContextVars();
831             return true;
832         }
e2d6ed 833
42b689 834         /**
7fe908 835          * function returns the text from the file, or if we're using cache, the text
MC 836          * from the cache file. MUST RETURN DATA.
837          * @param string tmplfile contains path to template file
838          * @param do_eval used for included files. If set then this function must do the eval()'ing.
839          * @access private
840          * @return mixed data/string or boolean
841          */
216ea1 842         private function _getData ($tmplfile, $do_eval=false, $tmpl_from_string = false)
42b689 843         {
MC 844             //* check the current file depth
7fe908 845             if ($this->_includedepth > $this->OPTIONS['MAX_INCLUDES'] || $tmplfile == false) {
MC 846                 return;
42b689 847             } else {
MC 848                 if ($this->_debug){
216ea1 849                     if($tmpl_from_string) array_push($this->_debugIncludedfiles, 'String: ' . substr($tmplfile, 0, 25) . '...');
MB 850                     else array_push($this->_debugIncludedfiles, $tmplfile);
42b689 851                 }
7fe908 852                 if ($do_eval) {
216ea1 853                     if($tmpl_from_string == true) array_push($this->_currentincludedir, end($this->_currentincludedir));
MB 854                     else array_push($this->_currentincludedir, dirname($tmplfile));
7fe908 855                     $this->_includedepth++;
MC 856                 }
857             }
e2d6ed 858
7fe908 859
216ea1 860             if($this->_cache && $this->_checkCache($tmplfile, $tmpl_from_string)) { //* cache exists so lets use it
67d99a 861                 $data = file_get_contents($this->_cachefile);
42b689 862             } else { //* no cache lets parse the file
216ea1 863                 if($tmpl_from_string == true) {
MB 864                     $data = $tmplfile;
865                 } else {
67d99a 866                     $data = file_get_contents($tmplfile);
216ea1 867                 }
7fe908 868
MC 869                 $regex = '/(<|<\/|{|{\/|<!--|<!--\/){1}\s*';
870                 $regex.= 'tmpl_([\w]+)\s*';
45a8b5 871                 $regex.= '((?:(?:';
7fe908 872                 $regex.=    '(?:';
MC 873                 $regex.=        '(name|format|escape|op|value|file)';
874                 $regex.=        '\s*=\s*';
875                 $regex.=    ')?';
876                 $regex.=    '(?:[\"\'])?';
877                 $regex.=    '((?<=[\"\'])';
878                 $regex.=    '[^\"\']*|[a-z0-9_\.]*)';
879                 $regex.=    '[\"\']?';
45a8b5 880                 $regex.= ')?\s*)*?)';
7fe908 881                 $regex.= '(?:>|\/>|}|-->){1}';
615a0a 882                 $regex.= '/i';
7fe908 883                 $data = preg_replace_callback($regex, array($this, '_parseTag'), $data);
e2d6ed 884
7fe908 885                 if ($this->_cache) { // add cache if need be
MC 886                     $this->_createCache($data);
887                 }
888             }
e2d6ed 889
42b689 890             //* now we must parse the $data and check for any <tmpl_include>'s
216ea1 891             if ($this->_debug && $tmpl_from_string == false) $this->doDebugWarnings(file($tmplfile), $tmplfile);
e2d6ed 892
7fe908 893             if ($do_eval) {
MC 894                 $success = @eval('?>'.$data.'<?php return 1;');
895                 $this->_includedepth--;
896                 array_pop($this->_currentincludedir);
897                 return $success;
42b689 898             } else {
7fe908 899                 return $data;
MC 900             }
901         }
e2d6ed 902
7fe908 903         /**
MC 904          * Searches for all possible instances of file { $file }
905          * @param string $file path of file we're looking for
906          * @access private
907          * @return mixed fullpath to file or boolean false
908          */
42b689 909         private function _fileSearch($file)
MC 910         {
911
7fe908 912             $filename = basename($file);
MC 913             $filepath = dirname($file);
914
42b689 915             if(isset($_SESSION['s']['module']['name']) && isset($_SESSION['s']['theme'])) {
MC 916                 if(is_file(ISPC_THEMES_PATH.'/'.$_SESSION['s']['theme'].'/templates/'.$_SESSION['s']['module']['name'].'/'.$filename)) {
917                     return ISPC_THEMES_PATH.'/'.$_SESSION['s']['theme'].'/templates/'.$_SESSION['s']['module']['name'].'/'.$filename;
918                 }
c8c7ed 919             }
e2d6ed 920
42b689 921             //* check fullpath first..
MC 922             $fullpath = $filepath.'/'.$filename;
923             if (is_file($fullpath)) return $fullpath;
924
925             //* ..then check for relative path for current directory..
7fe908 926             if (!empty($this->_currentincludedir)) {
MC 927                 $currdir = $this->_currentincludedir[(count($this->_currentincludedir) -1)];
928                 $relativepath = realpath($currdir.'/'.$filepath.'/'.$filename);
929                 if (is_file($relativepath)) {
930                     array_push($this->_currentincludedir, dirname($relativepath));
931                     return $relativepath;
932                 }
933             }
e2d6ed 934
42b689 935             //* ..then check for relative path for all additional given paths..
7fe908 936             if (!empty($this->OPTIONS['INCLUDE_PATHS'])) {
MC 937                 foreach ($this->OPTIONS['INCLUDE_PATHS'] as $currdir) {
938                     $relativepath = realpath($currdir.'/'.$filepath.'/'.$filename);
939                     if (is_file($relativepath)) {
940                         return $relativepath;
941                     }
942                 }
943             }
e2d6ed 944
42b689 945             //* ..then check path from TEMPLATE_DIR..
7fe908 946             if (!empty($this->OPTIONS['TEMPLATE_DIR'])) {
MC 947                 $fullpath = realpath($this->OPTIONS['TEMPLATE_DIR'].'/'.$filepath.'/'.$filename);
42b689 948                 if (is_file($fullpath)) return $fullpath;
7fe908 949             }
e2d6ed 950
42b689 951             //* ..then check relative path from executing php script..
7fe908 952             $fullpath = realpath($filepath.'/'.$filename);
42b689 953             if (is_file($fullpath)) return $fullpath;
e2d6ed 954
42b689 955             //* ..then check path from template file.
7fe908 956             if (!empty($this->VLIBTEMPLATE_ROOT)) {
MC 957                 $fullpath = realpath($this->VLIBTEMPLATE_ROOT.'/'.$filepath.'/'.$filename);
42b689 958                 if (is_file($fullpath)) return $fullpath;
7fe908 959             }
e2d6ed 960
7fe908 961             return false; // uh oh, file not found
MC 962         }
e2d6ed 963
7fe908 964         /**
MC 965          * Modifies the array $arr to add Template variables, __FIRST__, __LAST__ ..etc
966          * if $this->OPTIONS['LOOP_CONTEXT_VARS'] is true.
967          * Used by $this->setloop().
968          * @param array $arr
969          * @return array new look array
970          * @access private
971          */
42b689 972         private function _arrayBuild($arr)
MC 973         {
7fe908 974             if (is_array($arr) && !empty($arr)) {
MC 975                 $arr = array_values($arr); // to prevent problems w/ non sequential arrays
976                 for ($i = 0; $i < count($arr); $i++) {
977                     if(!is_array($arr[$i]))  return false;
978                     foreach ($arr[$i] as $k => $v) {
979                         unset($arr[$i][$k]);
980                         if ($this->OPTIONS['CASELESS']) $k = strtolower($k);
981                         if (preg_match('/^[0-9]+$/', $k)) $k = '_'.$k;
e2d6ed 982
7fe908 983                         if (is_array($v)) {
MC 984                             if (($arr[$i][$k] = $this->_arrayBuild($v)) == false) return false;
42b689 985                         } else { // reinsert the var
7fe908 986                             $arr[$i][$k] = $v;
MC 987                         }
988                     }
989                     if ($this->OPTIONS['LOOP_CONTEXT_VARS']) {
990                         if ($i == 0) $arr[$i]['__FIRST__'] = true;
991                         if (($i + 1) == count($arr)) $arr[$i]['__LAST__'] = true;
992                         if ($i != 0 && (($i + 1) < count($arr))) $arr[$i]['__INNER__'] = true;
993                         if (is_int(($i+1) / 2))  $arr[$i]['__EVEN__'] = true;
994                         if (!is_int(($i+1) / 2))  $arr[$i]['__ODD__'] = true;
995                         $arr[$i]['__ROWNUM__'] = ($i + 1);
996                     }
997                 }
998                 return $arr;
42b689 999             } elseif (empty($arr)) {
7fe908 1000                 return true;
MC 1001             }
1002         }
e2d6ed 1003
7fe908 1004         /**
MC 1005          * returns a string used for parsing in tmpl_if statements.
1006          * @param string $varname
1007          * @param string $value
1008          * @param string $op
1009          * @param string $namespace current namespace
1010          * @access private
1011          * @return string used for eval'ing
1012          */
42b689 1013         private function _parseIf($varname, $value = null, $op = null, $namespace = null, $format = null)
MC 1014         {
7fe908 1015             if (isset($namespace)) $namespace = substr($namespace, 0, -1);
MC 1016             $comp_str = ''; // used for extended if statements
e2d6ed 1017
7fe908 1018             // work out what to put on the end id value="whatever" is used
MC 1019             if (isset($value)) {
e2d6ed 1020
7fe908 1021                 // add the correct operator depending on whether it's been specified or not
MC 1022                 if (!empty($op)) {
1023                     if (in_array($op, $this->allowed_if_ops)) {
1024                         $comp_str .= $op;
42b689 1025                     } else {
7fe908 1026                         vlibTemplateError::raiseError('VT_WARNING_INVALID_IF_OP', WARNING, $op);
MC 1027                     }
42b689 1028                 } else {
7fe908 1029                     $comp_str .= '==';
MC 1030                 }
e2d6ed 1031
7fe908 1032                 // now we add the value, if it's numeric, then we leave the quotes off
MC 1033                 if (is_numeric($value)) {
1034                     $comp_str .= $value;
42b689 1035                 } else {
7fe908 1036                     $comp_str .= '\''.$value.'\'';
MC 1037                 }
1038             }
e2d6ed 1039
7fe908 1040             if (count($this->_namespace) == 0 || $namespace == 'global') return '$this->_vars[\''.$varname.'\']'.$comp_str;
MC 1041             $retstr = '$this->_arrvars';
1042             $numnamespaces = count($this->_namespace);
1043             for ($i=0; $i < $numnamespaces; $i++) {
1044                 if ($this->_namespace[$i] == $namespace || (($i + 1) == $numnamespaces && !empty($namespace))) {
1045                     $retstr .= "['".$namespace."'][\$_".$i."]";
1046                     break 1;
42b689 1047                 } else {
7fe908 1048                     $retstr .= "['".$this->_namespace[$i]."'][\$_".$i."]";
MC 1049                 }
1050             }
1051             if ($this->OPTIONS['GLOBAL_VARS'] && empty($namespace)) {
8133de 1052                 $retstr = '(('.$retstr.'[\''.$varname.'\'] !== null) ? '.$retstr.'[\''.$varname.'\'] : $this->_vars[\''.$varname.'\'])';
MC 1053                 if(isset($format) && isset($value) && $format == 'version') {
164349 1054                     return 'version_compare(' . $retstr . ', \'' . $value . '\', \'' . (!empty($op) ? $op : '==') . '\')';
8133de 1055                 } else {
MC 1056                     return $retstr.$comp_str;
1057                 }
7fe908 1058             }
MC 1059             else {
8133de 1060                 if(isset($format) && isset($value) && $format == 'version') {
164349 1061                     return 'version_compare(' . $retstr."['".$varname."']" . ', \'' . $value . '\', \'' . (!empty($op) ? $op : '==') . '\')';
8133de 1062                 } else {
MC 1063                     return $retstr."['".$varname."']".$comp_str;
1064                 }
7fe908 1065             }
MC 1066         }
e2d6ed 1067
216ea1 1068         /**
MB 1069          * returns a string containing hook data
1070          * @param string $type
1071          * @param string $name
1072          * @return string hook data
1073          */
1074         private function _parseHook ($name)
1075         {
1076             global $app;
1077             
1078             $namespace = '';
1079             if(strpos($name, ':') !== false) list($namespace, $name) = explode(':', $name, 2);
1080             
1081             $result = $app->plugins->raiseAction('on_template_content_hook', array(
1082                 'name' => $name,
1083                 'namespace' => $namespace,
1084                 'vars' => $this->_vars
1085             ), true);
1086             if(!$result) $result = '';
1087             else $result = $this->_getData($result, false, true);
1088             
1089             return $result;
1090         }
e2d6ed 1091
7fe908 1092         /**
MC 1093          * returns a string used for parsing in tmpl_loop statements.
1094          * @param string $varname
1095          * @access private
1096          * @return string used for eval'ing
1097          */
42b689 1098         private function _parseLoop ($varname)
MC 1099         {
7fe908 1100             array_push($this->_namespace, $varname);
MC 1101             $tempvar = count($this->_namespace) - 1;
1102             $retstr = "for (\$_".$tempvar."=0 ; \$_".$tempvar." < count(\$this->_arrvars";
1103             for ($i=0; $i < count($this->_namespace); $i++) {
1104                 $retstr .= "['".$this->_namespace[$i]."']";
1105                 if ($this->_namespace[$i] != $varname) $retstr .= "[\$_".$i."]";
1106             }
1107             return $retstr."); \$_".$tempvar."++) {";
1108         }
1109
1110         /**
1111          * returns a string used for parsing in tmpl_var statements.
1112          * @param string $wholetag
1113          * @param string $tag
1114          * @param string $varname
1115          * @param string $escape
1116          * @param string $format
1117          * @param string $namespace
1118          * @access private
1119          * @return string used for eval'ing
1120          */
42b689 1121         private function _parseVar ($wholetag, $tag, $varname, $escape, $format, $namespace)
MC 1122         {
7fe908 1123             if (!empty($namespace)) $namespace = substr($namespace, 0, -1);
MC 1124             $wholetag = stripslashes($wholetag);
1125
1126             if (count($this->_namespace) == 0 || $namespace == 'global') {
1127                 $var1 = '$this->_vars[\''.$varname.'\']';
42b689 1128             } else {
7fe908 1129                 $var1build = "\$this->_arrvars";
MC 1130                 $numnamespaces = count($this->_namespace);
1131                 for ($i=0; $i < $numnamespaces; $i++) {
1132                     if ($this->_namespace[$i] == $namespace || (($i + 1) == $numnamespaces && !empty($namespace))) {
1133                         $var1build .= "['".$namespace."'][\$_".$i."]";
1134                         break 1;
42b689 1135                     } else {
7fe908 1136                         $var1build .= "['".$this->_namespace[$i]."'][\$_".$i."]";
MC 1137                     }
1138                 }
1139                 $var1 = $var1build . "['$varname']";
1140                 if ($this->OPTIONS['GLOBAL_VARS'] && empty($namespace)) {
1141                     $var2 = '$this->_vars[\''.$varname.'\']';
1142                 }
1143             }
1144
1145             $beforevar = '';
1146             $aftervar  = '';
1147             if (!empty($escape)&& isset($this->ESCAPE_TAGS[$escape])) {
1148                 $beforevar .= $this->ESCAPE_TAGS[$escape]['open'];
1149                 $aftervar   = $this->ESCAPE_TAGS[$escape]['close'] . $aftervar;
1150             }
1151
1152             if (!empty($format)&& isset($this->FORMAT_TAGS[$format])) {
1153                 $beforevar .= $this->FORMAT_TAGS[$format]['open'];
1154                 $aftervar   = $this->FORMAT_TAGS[$format]['close'] . $aftervar;
1155             }
1156
42b689 1157             //* build return values
7fe908 1158             $retstr  = 'if ('.$var1.' !== null) { ';
MC 1159             $retstr .= 'print('.$beforevar.$var1.$aftervar.'); ';
1160             $retstr .= '}';
1161
1162             if (@$var2) {
1163                 $retstr .= ' elseif ('.$var2.' !== null) { ';
1164                 $retstr .= 'print('.$beforevar.$var2.$aftervar.'); ';
1165                 $retstr .= '}';
1166             }
1167
1168             switch (strtolower($this->OPTIONS['UNKNOWNS'])) {
1169             case 'comment':
1170                 $comment = addcslashes('<!-- unknown variable '.preg_replace('/<!--|-->/', '', $wholetag).'//-->', '"');
1171                 $retstr .= ' else { print("'.$comment.'"); $this->_setUnknown("'.$varname.'"); }';
1172                 return $retstr;
42b689 1173
7fe908 1174             case 'leave':
MC 1175                 $retstr .= ' else { print("'.addcslashes($wholetag, '"').'"); $this->_setUnknown("'.$varname.'"); }';
1176                 return $retstr;
42b689 1177
7fe908 1178             case 'print':
MC 1179                 $retstr .= ' else { print("'.htmlspecialchars($wholetag, ENT_QUOTES).'"); $this->_setUnknown("'.$varname.'"); }';
1180                 return $retstr;
1181
1182             case 'ignore':
1183                 return $retstr;
42b689 1184
7fe908 1185             case 'remove':
MC 1186             default:
1187                 $retstr .= ' else { $this->_setUnknown("'.$varname.'"); }';
1188                 return $retstr;
42b689 1189
7fe908 1190             }
MC 1191         }
1192
1193         /**
1194          * takes values from preg_replace in $this->_intparse() and determines
1195          * the replace string.
1196          *
1197          * @param array $args array of all matches found by preg_replace
1198          * @access private
1199          * @return string replace values
1200          */
42b689 1201         private function _parseTag ($args)
MC 1202         {
7fe908 1203             $wholetag = $args[0];
MC 1204             $openclose = $args[1];
1205             $tag = strtolower($args[2]);
45a8b5 1206             
MC 1207             if ($tag == 'else') return '<?php } else { ?>';
7fe908 1208             if ($tag == 'tmpl_include') return $wholetag; // ignore tmpl_include tags
e2d6ed 1209
7fe908 1210             if (preg_match("/^<\/|{\/|<!--\/$/s", $openclose) || preg_match("/^end[if|loop|unless|comment]$/", $tag)) {
MC 1211                 if ($tag == 'loop' || $tag == 'endloop') array_pop($this->_namespace);
1212                 if ($tag == 'comment' || $tag == 'endcomment') {
45a8b5 1213                     return '<?php */ ?>';
42b689 1214                 } else {
45a8b5 1215                     return '<?php } ?>';
7fe908 1216                 }
MC 1217             }
e2d6ed 1218
7fe908 1219             // arrange attributes
45a8b5 1220             $tmp_atts = $args[3];
MC 1221             $atts = preg_split('/\s+/', $tmp_atts);
1222             foreach($atts as $att) {
1223                 $regex =    '/(?:';
1224                 $regex.=        '(name|format|escape|op|value|file)';
1225                 $regex.=        '\s*=\s*';
1226                 $regex.=    ')?';
1227                 $regex.=    '(?:[\"\'])?';
1228                 $regex.=    '((?<=[\"\'])';
1229                 $regex.=    '[^\"\']*|[a-z0-9_\.]*)';
1230                 $regex.=    '[\"\']?/';
1231                 if(preg_match($regex, $att, $match)) {
1232                     $key = (empty($match[1])) ? 'name' : strtolower($match[1]);
1233                     if ($key == 'name' && preg_match('/^(php)?include$/', $tag)) $key = 'file';
1234                     $$key = $match[2];
1235                 }
7fe908 1236             }
e2d6ed 1237
7fe908 1238             $var = ($this->OPTIONS['CASELESS']) ? strtolower($name) : $name;
e2d6ed 1239
7fe908 1240             if ($this->_debug && !empty($var)) {
MC 1241                 if (preg_match("/^global\.([A-Za-z_]+[_A-Za-z0-9]*)$/", $var, $matches)) $var2 = $matches[1];
1242                 if (empty($this->_debugTemplatevars[$tag])) $this->_debugTemplatevars[$tag] = array();
1243                 if (!isset($var2)) $var2 = $var;
1244                 if (!in_array($var2, $this->_debugTemplatevars[$tag])) array_push($this->_debugTemplatevars[$tag], $var2);
1245             }
e2d6ed 1246
7fe908 1247             if (preg_match("/^([A-Za-z_]+[_A-Za-z0-9]*(\.)+)?([A-Za-z_]+[_A-Za-z0-9]*)$/", $var, $matches)) {
MC 1248                 $var = $matches[3];
1249                 $namespace = $matches[1];
1250             }
e2d6ed 1251
T 1252
42b689 1253             //* return correct string (tag dependent)
7fe908 1254             switch ($tag) {
MC 1255             case 'var':
1256                 if (empty($escape) && (!empty($this->OPTIONS['DEFAULT_ESCAPE']) && strtolower($this->OPTIONS['DEFAULT_ESCAPE']) != 'none')) {
1257                     $escape = strtolower($this->OPTIONS['DEFAULT_ESCAPE']);
1258                 }
42b689 1259                 return '<?php '.$this->_parseVar ($wholetag, $tag, $var, @$escape, @$format, @$namespace)." ?>\n";
e2d6ed 1260
7fe908 1261             case 'if':
45a8b5 1262                 return '<?php if ('. $this->_parseIf($var, @$value, @$op, @$namespace, @$format) .') { ?>';
e2d6ed 1263
7fe908 1264             case 'unless':
45a8b5 1265                 return '<?php if (!'. $this->_parseIf($var, @$value, @$op, @$namespace, @$format) .') { ?>';
e2d6ed 1266
7fe908 1267             case 'elseif':
45a8b5 1268                 return '<?php } elseif ('. $this->_parseIf($var, @$value, @$op, @$namespace, @$format) .') { ?>';
e2d6ed 1269
7fe908 1270             case 'loop':
45a8b5 1271                 return '<?php '. $this->_parseLoop($var) .'?>';
e2d6ed 1272
7fe908 1273             case 'comment':
MC 1274                 if (empty($var)) { // full open/close style comment
45a8b5 1275                     return '<?php /* ?>';
42b689 1276                 } else { // just ignore tag if it was a one line comment
7fe908 1277                     return;
MC 1278                 }
e2d6ed 1279
7fe908 1280             case 'phpinclude':
MC 1281                 if ($this->OPTIONS['ENABLE_PHPINCLUDE']) {
45a8b5 1282                     return '<?php include(\''.$file.'\'); ?>';
7fe908 1283                 }
216ea1 1284             
MB 1285             case 'hook':
1286                 return $this->_parseHook(@$var);
1287             
7fe908 1288             case 'include':
MC 1289                 return '<?php $this->_getData($this->_fileSearch(\''.$file.'\'), 1); ?>';
e2d6ed 1290
7fe908 1291             case 'dyninclude':
MC 1292                 return '<?php $this->_getData($this->_fileSearch($this->_dyninclude[\''.$name.'\']), 1); ?>';
e2d6ed 1293
7fe908 1294             default:
MC 1295                 if ($this->OPTIONS['STRICT']) vlibTemplateError::raiseError('VT_ERROR_INVALID_TAG', KILL, htmlspecialchars($wholetag, ENT_QUOTES));
1296                 break;
1297             }
e2d6ed 1298
7fe908 1299         }
e2d6ed 1300
7fe908 1301         /**
MC 1302          * Parses $this->_tmplfile into correct format for eval() to work
1303          * Called by $this->_parse(), or $this->fastPrint, this replaces all <tmpl_*> references
1304          * with their correct php representation, i.e. <tmpl_var title> becomes $this->vars['title']
1305          * Sets final parsed file to $this->_tmplfilep.
1306          *
1307          * @access private
1308          * @return boolean true/false
1309          */
42b689 1310         private function _intParse ()
MC 1311         {
7fe908 1312             //$mqrt = get_magic_quotes_runtime();
MC 1313             //set_magic_quotes_runtime(0);
1314             $this->_tmplfilep = '?>'.$this->_getData($this->_tmplfilename).'<?php return true;';
1315             //set_magic_quotes_runtime($mqrt);
1316             return true;
1317         }
e2d6ed 1318
7fe908 1319         /**
MC 1320          * Calls _intParse, and eval()s $this->tmplfilep
1321          * and outputs the results to $this->tmploutput
1322          *
1323          * @param bool compress whether to compress contents
1324          * @access private
1325          * @return boolean true/false
1326          */
42b689 1327         private function _parse ($compress = '')
MC 1328         {
7fe908 1329             if (!$this->_parsed) {
MC 1330                 if ($this->OPTIONS['TIME_PARSE']) $this->_firstparsetime = $this->_getMicroTime();
1331
1332                 $this->_intParse();
1333                 $this->_parsed = true;
1334
1335                 if ($this->OPTIONS['TIME_PARSE']) $this->_totalparsetime = ($this->_getMicroTime() - $this->_firstparsetime);
1336                 if ($this->OPTIONS['TIME_PARSE'] && $this->OPTIONS['GLOBAL_CONTEXT_VARS']) $this->setVar('__PARSE_TIME__', $this->getParseTime());
1337             }
1338
e2d6ed 1339             // ob_start($compress);
7fe908 1340             ob_start();
e2d6ed 1341
7fe908 1342             array_push($this->_currentincludedir, dirname($this->_tmplfilename));
MC 1343             $this->_includedepth++;
1344             $success = @eval($this->_tmplfilep);
1345             $this->_includedepth--;
1346             array_pop($this->_currentincludedir);
e2d6ed 1347
7fe908 1348             if ($this->_debug) $this->doDebug();
MC 1349             if (!$success) vlibTemplateError::raiseError('VT_ERROR_PARSE', FATAL);
1350             $this->_tmploutput .= ob_get_contents();
1351             ob_end_clean();
1352             return true;
1353         }
e2d6ed 1354
7fe908 1355         /**
MC 1356          * Sets one or more of the boolean options 1/0, that control certain actions in the template.
1357          * Use of this function:
1358          * either: vlibTemplate::_setOptions(string option_name, bool option_val [, string option_name, bool option_val ..]);
1359          * or      vlibTemplate::_setOptions(array);
1360          *          with an associative array where the key is the option_name
1361          *          and the value is the option_value.
1362          *
1363          * @param mixed (mulitple)
1364          * @return bool true/false
1365          * @access private
1366          */
42b689 1367         private function _setOption()
MC 1368         {
7fe908 1369             $numargs = func_num_args();
MC 1370             if ($numargs < 1) {
1371                 vlibTemplateError::raiseError('VT_ERROR_WRONG_NO_PARAMS', null, '_setOption()');
1372                 return false;
1373             }
e2d6ed 1374
7fe908 1375             if ($numargs == 1) {
MC 1376                 $options = func_get_arg(1);
1377                 if (is_array($options)) {
1378                     foreach ($options as $k => $v) {
1379                         if ($v != null) {
1380                             if(in_array($k, array_keys($this->OPTIONS))) $this->OPTIONS[$k] = $v;
42b689 1381                         } else {
7fe908 1382                             continue;
MC 1383                         }
1384                     }
42b689 1385                 } else {
7fe908 1386                     vlibTemplateError::raiseError('VT_ERROR_WRONG_NO_PARAMS', null, '_setOption()');
MC 1387                     return false;
1388                 }
42b689 1389             }elseif (is_int($numargs / 2)) {
7fe908 1390                 for ($i = 0; $i < $numargs; $i=($i+2)) {
MC 1391                     $k  = func_get_arg($i);
1392                     $v = func_get_arg(($i+1));
1393                     if ($v != null) {
1394                         if(in_array($k, array_keys($this->OPTIONS))) $this->OPTIONS[$k] = $v;
1395                     }
1396                 }
42b689 1397             } else {
7fe908 1398                 vlibTemplateError::raiseError('VT_ERROR_WRONG_NO_PARAMS', null, '_setOption()');
MC 1399                 return false;
1400             }
1401             return true;
1402         }
1403
1404         /**
1405          * Used during parsing, this function sets an unknown var checking to see if it
1406          * has been previously set.
1407          * @param string var
1408          * @access private
1409          */
42b689 1410         private function _setUnknown($var)
MC 1411         {
7fe908 1412             if (!in_array($var, $this->_unknowns)) array_push($this->_unknowns, $var);
MC 1413         }
1414
1415         /**
1416          * Returns microtime as a float number
1417          * @return float microtime
1418          * @access private
1419          */
42b689 1420         private function _getMicrotime()
MC 1421         {
1422             list($msec, $sec) = explode(' ', microtime());
7fe908 1423             return (float)$msec + (float)$sec;
MC 1424         }
1425
1426         /**
1427          * Returns str encoded to hex code.
1428          * @param string str to be encoded
1429          * @param bool true/false specify whether to use hex_entity
1430          * @return string encoded in hex
1431          * @access private
1432          */
42b689 1433         private  function _escape_hex($str = '', $entity = false) {
7fe908 1434             $prestr = $entity ? '&#x' : '%';
MC 1435             $poststr= $entity ? ';' : '';
1436             for ($i=0; $i < strlen($str); $i++) {
1437                 $return .= $prestr.bin2hex($str[$i]).$poststr;
1438             }
1439             return $return;
1440         }
1441
1442         /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
e2d6ed 1443     The following functions have no use and are included just so that if the user
T 1444     is making use of vlibTemplateCache functions, this doesn't crash when changed to
1445     vlibTemplate if the user is quickly bypassing the vlibTemplateCache class.
1446     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
7fe908 1447         function clearCache()        {vlibTemplateError::raiseError('VT_WARNING_NOT_CACHE_OBJ', WARNING, 'clearCache()');}
e2d6ed 1448
7fe908 1449         function recache()           {vlibTemplateError::raiseError('VT_WARNING_NOT_CACHE_OBJ', WARNING, 'recache()');}
MC 1450
1451         function setCacheLifeTime()  {vlibTemplateError::raiseError('VT_WARNING_NOT_CACHE_OBJ', WARNING, 'setCacheLifeTime()');}
1452
1453         function setCacheExtension() {vlibTemplateError::raiseError('VT_WARNING_NOT_CACHE_OBJ', WARNING, 'setCacheExtension()');}
1454
42b689 1455     } // << end class Def
7fe908 1456
42b689 1457     //include_once (ISPC_CLASS_PATH.'/vlibTemplate/debug.php');
MC 1458     include_once ISPC_CLASS_PATH.'/tpl_cache.inc.php';
e2d6ed 1459
T 1460 } // << end if(!defined())..
7fe908 1461 ?>