Skip to content
tpl.inc.php 55.1 KiB
Newer Older
tbrehm's avatar
tbrehm committed
<?php
pedro_morgan's avatar
pedro_morgan committed
* vlibTemplate is a class used to seperate PHP and HTML.
* For instructions on how to use vlibTemplate, see the
* vlibTemplate.html file, located in the 'docs' directory.
*
* @since 07/03/2002
* @author Kelvin Jones <kelvin@kelvinjones.co.uk>
* @package vLIB
* @access public
* @see vlibTemplate.html
*/
tbrehm's avatar
tbrehm committed

/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP version 4.0                                                      |
// +----------------------------------------------------------------------+
// | Copyright (c) 2002 Active Fish Group                                 |
// +----------------------------------------------------------------------+
// | Authors: Kelvin Jones <kelvin@kelvinjones.co.uk>                     |
// +----------------------------------------------------------------------+
//
// $Id: class.tpl.inc.php,v 1.1 2003/07/08 12:31:10 platinum Exp $

pedro_morgan's avatar
pedro_morgan committed
//** check to avoid multiple including of class
tbrehm's avatar
tbrehm committed
if (!defined('vlibTemplateClassLoaded')) {
    define('vlibTemplateClassLoaded', 1);
tbrehm's avatar
tbrehm committed
   	include_once ($conf['classpath'].'/tpl_error.inc.php');
   	include_once ($conf['classpath'].'/tpl_ini.inc.php');

pedro_morgan's avatar
pedro_morgan committed
    class tpl{
    
        /*-----------------------------------------------------------------------------\
        |                                 ATTENTION                                    |
        |  Do not touch the following variables. vlibTemplate will not work otherwise. |
        \-----------------------------------------------------------------------------*/
        private $OPTIONS = array(
                'MAX_INCLUDES'          =>   10,
                'TEMPLATE_DIR'          => null,
                'GLOBAL_VARS'           => null,
                'GLOBAL_CONTEXT_VARS'   => null,
                'LOOP_CONTEXT_VARS'     => null,
                'SET_LOOP_VAR'          => null,
                'DEFAULT_ESCAPE'        => null,
                'STRICT'                => null,
                'CASELESS'              => null,
                'UNKNOWNS'              => null,
                'TIME_PARSE'            => null,
                'ENABLE_PHPINCLUDE'     => null,
                'INCLUDE_PATHS'         => array(),
                'CACHE_DIRECTORY'       => null,
                'CACHE_LIFETIME'        => null,
                'CACHE_EXTENSION'       => null
                );
tbrehm's avatar
tbrehm committed

        /** open and close tags used for escaping */
pedro_morgan's avatar
pedro_morgan committed
        private $ESCAPE_TAGS = array(
                'html'      => array('open' => 'htmlspecialchars('    ,'close'=> ', ENT_QUOTES)'),
                'url'       => array('open' => 'urlencode('           ,'close'=> ')'),
                'rawurl'    => array('open' => 'rawurlencode('        ,'close'=> ')'),
                'sq'        => array('open' => 'addcslashes('         ,'close'=> ", \"'\")"),
                'dq'        => array('open' => 'addcslashes('         ,'close'=> ", '\"')"),
                '1'         => array('open' => 'htmlspecialchars('    ,'close'=> ', ENT_QUOTES)'),
                '0'         => array('open' => ''                     ,'close'=> ''),
                'none'      => array('open' => ''                     ,'close'=> ''),
                'hex'       => array('open' => '$this->_escape_hex('  ,'close'=> ', false)'),
                'hexentity' => array('open' => '$this->_escape_hex('  ,'close'=> ', true)')
                );
    
tbrehm's avatar
tbrehm committed
        /** open and close tags used for formatting */
pedro_morgan's avatar
pedro_morgan committed
        private $FORMAT_TAGS = array(
                'strtoupper' => array('open' => 'strtoupper(',          'close'=> ')'),
                'uc'         => array('open' => 'strtoupper(',          'close'=> ')'),
                'strtolower' => array('open' => 'strtolower(',          'close'=> ')'),
                'lc'         => array('open' => 'strtolower(',          'close'=> ')'),
                'ucfirst'    => array('open' => 'ucfirst(',             'close'=> ')'),
                'lcucfirst'  => array('open' => 'ucfirst(strtolower(',  'close'=> '))'),
                'ucwords'    => array('open' => 'ucwords(',             'close'=> ')'),
                'lcucwords'  => array('open' => 'ucwords(strtolower(',  'close'=> '))')
                );
tbrehm's avatar
tbrehm committed

        /** operators allowed when using extended TMPL_IF syntax */
pedro_morgan's avatar
pedro_morgan committed
        private $allowed_if_ops = array('==','!=','<>','<','>','<=','>=');
    
tbrehm's avatar
tbrehm committed
        /** dbs allowed by vlibTemplate::setDbLoop(). */
pedro_morgan's avatar
pedro_morgan committed
        private $allowed_loop_dbs = array('MYSQL','POSTGRESQL','INFORMIX','INTERBASE','INGRES',
                                        'MSSQL','MSQL','OCI8','ORACLE','OVRIMOS','SYBASE');
    
tbrehm's avatar
tbrehm committed
        /** root directory of vlibTemplate automagically filled in */
pedro_morgan's avatar
pedro_morgan committed
        private $VLIBTEMPLATE_ROOT = null;
    
tbrehm's avatar
tbrehm committed
        /** contains current directory used when doing recursive include */
pedro_morgan's avatar
pedro_morgan committed
        private $_currentincludedir = array();
    
tbrehm's avatar
tbrehm committed
        /** current depth of includes */
pedro_morgan's avatar
pedro_morgan committed
        private $_includedepth = 0;
    
tbrehm's avatar
tbrehm committed
        /** full path to tmpl file */
pedro_morgan's avatar
pedro_morgan committed
        private $_tmplfilename = null;
    
tbrehm's avatar
tbrehm committed
        /** file data before it's parsed */
pedro_morgan's avatar
pedro_morgan committed
        private $_tmplfile = null;
    
tbrehm's avatar
tbrehm committed
        /** parsed version of file, ready for eval()ing */
pedro_morgan's avatar
pedro_morgan committed
        private $_tmplfilep = null;
    
tbrehm's avatar
tbrehm committed
        /** eval()ed version ready for printing or whatever */
pedro_morgan's avatar
pedro_morgan committed
        private $_tmploutput = null;
    
tbrehm's avatar
tbrehm committed
        /** array for variables to be kept */
pedro_morgan's avatar
pedro_morgan committed
        private $_vars = array();
    
tbrehm's avatar
tbrehm committed
        /** array where loop variables are kept */
pedro_morgan's avatar
pedro_morgan committed
        private $_arrvars = array();
tbrehm's avatar
tbrehm committed

        /** array which holds the current namespace during parse */
pedro_morgan's avatar
pedro_morgan committed
        private $_namespace = array();
    
tbrehm's avatar
tbrehm committed
        /** variable is set to true once the template is parsed, to save re-parsing everything */
pedro_morgan's avatar
pedro_morgan committed
        private $_parsed = false;
    
tbrehm's avatar
tbrehm committed
        /** array holds all unknowns vars */
pedro_morgan's avatar
pedro_morgan committed
        private $_unknowns = array();
    
tbrehm's avatar
tbrehm committed
        /** microtime when template parsing began */
pedro_morgan's avatar
pedro_morgan committed
        private $_firstparsetime = null;
    
tbrehm's avatar
tbrehm committed
        /** total time taken to parse template */
pedro_morgan's avatar
pedro_morgan committed
        private $_totalparsetime = null;
    
tbrehm's avatar
tbrehm committed
        /** name of current loop being passed in */
pedro_morgan's avatar
pedro_morgan committed
        private $_currloopname = null;
    
tbrehm's avatar
tbrehm committed
        /** rows with the above loop */
pedro_morgan's avatar
pedro_morgan committed
        private $_currloop = array();
    
tbrehm's avatar
tbrehm committed
        /** define vars to avoid warnings */
pedro_morgan's avatar
pedro_morgan committed
        private $_debug = null;
        private $_cache = null;
tbrehm's avatar
tbrehm committed
        
        /** array which holds the dynamic Includes */
pedro_morgan's avatar
pedro_morgan committed
        private $_dyninclude = array();

        /*-----------------------------------------------------------------------------\
        |                           public functions                                   |
        \-----------------------------------------------------------------------------*/
		    
tbrehm's avatar
tbrehm committed
		
        /**
         * FUNCTION: newTemplate
         *
         * Usually called by the class constructor.
         * Stores the filename in $this->_tmplfilename.
         * Raises an error if the template file is not found.
         *
         * @param string $tmplfile full path to template file
         * @return boolean true
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function newTemplate($tmplfile)
        {
tbrehm's avatar
tbrehm committed
            if (!$tfile = $this->_fileSearch($tmplfile)) vlibTemplateError::raiseError('VT_ERROR_NOFILE',KILL,$tmplfile);

pedro_morgan's avatar
pedro_morgan committed
            //* make sure that any parsing vars are cleared for the new template
tbrehm's avatar
tbrehm committed
            $this->_tmplfile = null;
            $this->_tmplfilep = null;
            $this->_tmploutput = null;
            $this->_parsed = false;
            $this->_unknowns = array();
            $this->_firstparsetime = null;
            $this->_totalparsetime = null;

pedro_morgan's avatar
pedro_morgan committed
            //* reset debug module
tbrehm's avatar
tbrehm committed
            if ($this->_debug) $this->_debugReset();

            $this->_tmplfilename = $tfile;
            return true;
        }

        /**
         * FUNCTION: setVar
         *
         * Sets variables to be used by the template
         * If $k is an array, then it will treat it as an associative array
         * using the keys as variable names and the values as variable values.
         *
         * @param mixed $k key to define variable name
         * @param mixed $v variable to assign to $k
         * @return boolean true/false
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function setVar($k, $v = null)
        {
tbrehm's avatar
tbrehm committed
            if (is_array($k)) {
                foreach($k as $key => $value){
                    $key = ($this->OPTIONS['CASELESS']) ? strtolower(trim($key)) : trim($key);
                    if (preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $key) && $value !== null ) {
                        $this->_vars[$key] = $value;
                    }
                }
pedro_morgan's avatar
pedro_morgan committed
            } else {
tbrehm's avatar
tbrehm committed
                if (preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $k) && $v !== null) {
                    if ($this->OPTIONS['CASELESS']) $k = strtolower($k);
                    $this->_vars[trim($k)] = $v;
pedro_morgan's avatar
pedro_morgan committed
                } else {
tbrehm's avatar
tbrehm committed
                    return false;
                }
            }
            return true;
        }
        
        /**
         * FUNCTION: setInclude
         *
         * Sets dynamic includes to be used by the template
         * If $k is an array, then it will treat it as an associative array
         * using the keys as variable names and the values as variable values.
         *
         * @param mixed $k key to define variable name
         * @param mixed $v variable to assign to $k
         * @return boolean true/false
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function setInclude($k, $v = null) 
        {
tbrehm's avatar
tbrehm committed
        	if(is_array($k)) {
				foreach($k as $key => $val) {
					$this->_dyninclude[$key] = $val;
				}
			} else {
				$this->_dyninclude[$k] = $v;
			}
			return true;
		}

        /**
         * FUNCTION: unsetVar
         *
         * Unsets a variable which has already been set
         * Parse in all vars wanted for deletion in seperate parametres
         *
         * @param string var name to remove use: vlibTemplate::unsetVar(var[, var..])
         * @return boolean true/false returns true unless called with 0 params
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function unsetVar()
        {
tbrehm's avatar
tbrehm committed
            $num_args = func_num_args();
            if ($num_args < 1)  return false;

            for ($i = 0; $i < $num_args; $i++) {
                $var = func_get_arg($i);
                if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
                if (!preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $var)) continue;
                unset($this->_vars[$var]);
            }
            return true;
        }

        /**
         * FUNCTION: getVars
         *
         * Gets all vars currently set in global namespace.
         *
         * @return array
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function getVars()
        {
tbrehm's avatar
tbrehm committed
            if (empty($this->_vars)) return false;
            return $this->_vars;
        }

        /**
         * FUNCTION: getVar
         *
         * Gets a single var from the global namespace
         *
         * @return var
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function getVar($var)
        {
tbrehm's avatar
tbrehm committed
            if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
            if (empty($var) || !isset($this->_vars[$var])) return false;
            return $this->_vars[$var];
        }

        /**
         * FUNCTION: setContextVars
         *
         * sets the GLOBAL_CONTEXT_VARS
         *
         * @return true
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function setContextVars()
        {
tbrehm's avatar
tbrehm committed
            $_phpself = @$GLOBALS['HTTP_SERVER_VARS']['PHP_SELF'];
            $_pathinfo = @$GLOBALS['HTTP_SERVER_VARS']['PATH_INFO'];
            $_request_uri = @$GLOBALS['HTTP_SERVER_VARS']['REQUEST_URI'];
            $_qs   = @$GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING'];

pedro_morgan's avatar
pedro_morgan committed
            //* the following fixes bug of $PHP_SELF on Win32 CGI and IIS.
tbrehm's avatar
tbrehm committed
            $_self = (!empty($_pathinfo)) ? $_pathinfo : $_phpself;
            $_uri  = (!empty($_request_uri)) ? $_request_uri : $_self.'?'.$_qs;

            $this->setvar('__SELF__', $_self);
            $this->setvar('__REQUEST_URI__', $_uri);
            return true;
        }

        /**
         * FUNCTION: setLoop
         *
         * Builds the loop construct for use with <TMPL_LOOP>.
         *
         * @param string $k string to define loop name
         * @param array $v array to assign to $k
         * @return boolean true/false
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function setLoop($k, $v)
        {
tbrehm's avatar
tbrehm committed
            if (is_array($v) && preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $k)) {
                $k = ($this->OPTIONS['CASELESS']) ? strtolower(trim($k)) : trim($k);
                $this->_arrvars[$k] = array();
                if ($this->OPTIONS['SET_LOOP_VAR'] && !empty($v)) $this->setvar($k, 1);
                if (($this->_arrvars[$k] = $this->_arrayBuild($v)) == false) {
                    vlibTemplateError::raiseError('VT_WARNING_INVALID_ARR',WARNING,$k);
                }
            }
            return true;
        }

        /**
         * FUNCTION: setDbLoop [** EXPERIMENTAL **]
         *
         * Function to create a loop from a Db result resource link.
         *
         * @param string $loopname to commit loop. If not set, will use last loopname set using newLoop()
         * @param string $result link to a Db result resource
         * @param string $db_type, type of db that the result resource belongs to.
         * @return boolean true/false
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function setDbLoop($loopname, $result, $db_type = 'MYSQL')
        {
tbrehm's avatar
tbrehm committed
            $db_type = strtoupper($db_type);
            if (!in_array($db_type, $this->allowed_loop_dbs)) {
                vlibTemplateError::raiseError('VT_WARNING_INVALID_LOOP_DB',WARNING, $db_type);
                return false;
            }

            $loop_arr = array();
            switch ($db_type) {

                case 'MYSQL':
                    if (get_resource_type($result) != 'mysql result') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while($r = mysql_fetch_assoc($result)) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'POSTGRESQL':
                    if (get_resource_type($result) != 'pgsql result') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }

                    $nr = (function_exists('pg_num_rows')) ? pg_num_rows($result) : pg_numrows($result);

                    for ($i=0; $i < $nr; $i++) {
                        $loop_arr[] = pg_fetch_array($result, $i, PGSQL_ASSOC);
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'INFORMIX':
                    if (!$result) {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while($r = ifx_fetch_row($result, 'NEXT')) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'INTERBASE':
                    if (get_resource_type($result) != 'interbase result') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while($r = ibase_fetch_row($result)) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'INGRES':
                    if (!$result) {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while($r = ingres_fetch_array(INGRES_ASSOC, $result)) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'MSSQL':
                    if (get_resource_type($result) != 'mssql result') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while($r = mssql_fetch_array($result)) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'MSQL':
                    if (get_resource_type($result) != 'msql result') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while($r = msql_fetch_array($result, MSQL_ASSOC)) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'OCI8':
                    if (get_resource_type($result) != 'oci8 statement') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while(OCIFetchInto($result, &$r, OCI_ASSOC+OCI_RETURN_LOBS)) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'ORACLE':
                    if (get_resource_type($result) != 'oracle Cursor') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while(ora_fetch_into($result, &$r, ORA_FETCHINTO_ASSOC)) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'OVRIMOS':
                    if (!$result) {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }
                    while(ovrimos_fetch_into($result, &$r, 'NEXT')) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed

                case 'SYBASE':
                    if (get_resource_type($result) != 'sybase-db result') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE',WARNING, $db_type);
                        return false;
                    }

                    while($r = sybase_fetch_array($result)) {
                        $loop_arr[] = $r;
                    }
pedro_morgan's avatar
pedro_morgan committed
                    break;
tbrehm's avatar
tbrehm committed
            }
            $this->setLoop($loopname, $loop_arr);
            return true;
        }

        /**
         * FUNCTION: newLoop
         *
         * Sets the name for the curent loop in the 3 step loop process.
         *
         * @param string $name string to define loop name
         * @return boolean true/false
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function newLoop($loopname)
        {
tbrehm's avatar
tbrehm committed
            if (preg_match('/^[a-z_]+[a-z0-9_]*$/i', $loopname)) {
                $this->_currloopname[$loopname] = $loopname;
                $this->_currloop[$loopname] = array();
                return true;
pedro_morgan's avatar
pedro_morgan committed
            } else {
tbrehm's avatar
tbrehm committed
                return false;
            }
        }

        /**
         * FUNCTION: addRow
         *
         * Adds a row to the current loop in the 3 step loop process.
         *
         * @param array $row loop row to add to current loop
         * @param string $loopname loop to which you want to add row, if not set will use last loop set using newLoop().
         * @return boolean true/false
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function addRow($row, $loopname = null)
        {
tbrehm's avatar
tbrehm committed
            if (!$loopname) $loopname = $this->_currloopname[(count($this->_currloopname)-1)];

            if (!isset($this->_currloop[$loopname]) || empty($this->_currloopname)) {
                vlibTemplateError::raiseError('VT_WARNING_LOOP_NOT_SET',WARNING);
                return false;
            }
            if (is_array($row)) {
                $this->_currloop[$loopname][] = $row;
                return true;
pedro_morgan's avatar
pedro_morgan committed
            } else {
tbrehm's avatar
tbrehm committed
                return false;
            }
        }

        /**
         * FUNCTION: addLoop
         *
         * Completes the 3 step loop process. This assigns the rows and resets
         * the variables used.
         *
         * @param string $loopname to commit loop. If not set, will use last loopname set using newLoop()
         * @return boolean true/false
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function addLoop($loopname = null)
        {
tbrehm's avatar
tbrehm committed
            if ($loopname == null) { // add last loop used
                if (!empty($this->_currloop)) {
                    foreach ($this->_currloop as $k => $v) {
                        $this->setLoop($k, $v);
                        unset($this->_currloop[$k]);
                    }
                    $this->_currloopname = array();
                    return true;
pedro_morgan's avatar
pedro_morgan committed
                } else {
tbrehm's avatar
tbrehm committed
                    return false;
                }
pedro_morgan's avatar
pedro_morgan committed
            } elseif (!isset($this->_currloop[$loopname]) || empty($this->_currloopname)) { // newLoop not yet envoked
                    vlibTemplateError::raiseError('VT_WARNING_LOOP_NOT_SET', WARNING);
tbrehm's avatar
tbrehm committed
                    return false;
pedro_morgan's avatar
pedro_morgan committed
            } else { // add a specific loop
tbrehm's avatar
tbrehm committed
                $this->setLoop($loopname, $this->_currloop[$loopname]);
                unset($this->_currloopname[$loopname], $this->_currloop[$loopname]);
            }
            return true;
        }

        /**
         * FUNCTION: unsetLoop
         *
         * Unsets a loop which has already been set.
         * Can only unset top level loops.
         *
         * @param string loop to remove use: vlibTemplate::unsetLoop(loop[, loop..])
         * @return boolean true/false returns true unless called with 0 params
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function unsetLoop()
        {
tbrehm's avatar
tbrehm committed
            $num_args = func_num_args();
            if ($num_args < 1) return false;

            for ($i = 0; $i < $num_args; $i++) {
                $var = func_get_arg($i);
                if ($this->OPTIONS['CASELESS']) $var = strtolower($var);
                if (!preg_match('/^[A-Za-z_]+[A-Za-z0-9_]*$/', $var)) continue;
                unset($this->_arrvars[$var]);
            }
            return true;
        }

        /**
         * FUNCTION: reset
         *
         * Resets the vlibTemplate object. After using vlibTemplate::reset() you must
         * use vlibTemplate::newTemplate(tmpl) to reuse, not passing in the options array.
         *
         * @return boolean true
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function reset()
        {
tbrehm's avatar
tbrehm committed
            $this->clearVars();
            $this->clearLoops();
            $this->_tmplfilename = null;
            $this->_tmplfile = null;
            $this->_tmplfilep = null;
            $this->_tmploutput = null;
            $this->_parsed = false;
            $this->_unknowns = array();
            $this->_firstparsetime = null;
            $this->_totalparsetime = null;
            $this->_currloopname = null;
            $this->_currloop = array();
            return true;
        }

        /**
         * FUNCTION: clearVars
         *
         * Unsets all variables in the template
         *
         * @return boolean true
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function clearVars()
        {
tbrehm's avatar
tbrehm committed
            $this->_vars = array();
            return true;
        }

        /**
         * FUNCTION: clearLoops
         *
         * Unsets all loops in the template
         *
         * @return boolean true
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function clearLoops()
        {
tbrehm's avatar
tbrehm committed
            $this->_arrvars = array();
            $this->_currloopname = null;
            $this->_currloop = array();
            return true;
        }

        /**
         * FUNCTION: clearAll
         *
         * Unsets all variables and loops set using setVar/Loop()
         *
         * @return boolean true
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function clearAll()
        {
tbrehm's avatar
tbrehm committed
            $this->clearVars();
            $this->clearLoops();
            return true;
        }

        /**
         * FUNCTION: unknownsExist
         *
         * Returns true if unknowns were found after parsing.
         * Function MUST be called AFTER one of the parsing functions to have any relevance.
         *
         * @return boolean true/false
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function unknownsExist()
        {
tbrehm's avatar
tbrehm committed
            return (!empty($this->_unknowns));
        }

        /**
         * FUNCTION: unknowns
         *
         * Alias for unknownsExist.
         *
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function unknowns()
        {
tbrehm's avatar
tbrehm committed
            return $this->unknownsExist();
        }

        /**
         * FUNCTION: getUnknowns
         *
         * Returns an array of all unknown vars found when parsing.
         * This function is only relevant after parsing a document.
         *
         * @return array
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function getUnknowns()
        {
tbrehm's avatar
tbrehm committed
            return $this->_unknowns;
        }

        /**
         * FUNCTION: setUnknowns
         *
         * Sets how you want to handle variables that were found in the
         * template but not set in vlibTemplate using vlibTemplate::setVar().
         *
         * @param  string $arg ignore, remove, print, leave or comment
         * @return boolean
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function setUnknowns($arg)
        {
tbrehm's avatar
tbrehm committed
            $arg = strtolower(trim($arg));
            if (preg_match('/^ignore|remove|print|leave|comment$/', $arg)) {
                $this->OPTIONS['UNKNOWNS'] = $arg;
                return true;
            }
            return false;
        }

        /**
         * FUNCTION: setPath
         *
         * function sets the paths to use when including files.
         * Use of this function: vlibTemplate::setPath(string path [, string path, ..]);
         * i.e. if $tmpl is your template object do: $tmpl->setPath('/web/htdocs/templates','/web/htdocs/www');
         * with as many paths as you like.
         * if this function is called without any arguments, it will just delete any previously set paths.
         *
         * @param string path (mulitple)
         * @return bool success
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function setPath()
        {
tbrehm's avatar
tbrehm committed
            $num_args = func_num_args();
            if ($num_args < 1) {
                $this->OPTIONS['INCLUDE_PATHS'] = array();
                return true;
            }
            for ($i = 0; $i < $num_args; $i++) {
                $thispath = func_get_arg($i);
                array_push($this->OPTIONS['INCLUDE_PATHS'], realpath($thispath));
            }
            return true;
        }

        /**
         * FUNCTION: getParseTime
         *
         * After using one of the parse functions, this will allow you
         * access the time taken to parse the template.
         * see OPTION 'TIME_PARSE'.
         *
         * @return float time taken to parse template
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function getParseTime()
        {
tbrehm's avatar
tbrehm committed
            if ($this->OPTIONS['TIME_PARSE'] && $this->_parsed) {
                return $this->_totalparsetime;
            }
            return false;
        }


        /**
         * FUNCTION: fastPrint
         *
         * Identical to pparse() except that it uses output buffering w/ gz compression thus
         * printing the output directly and compressed if poss.
         * Will possibly if parsing a huge template.
         *
         * @access public
         * @return boolean true/false
         */
pedro_morgan's avatar
pedro_morgan committed
        public function fastPrint()
        {
tbrehm's avatar
tbrehm committed
            $ret = $this->_parse('ob_gzhandler');
            print($this->_tmploutput);
            return $ret;
        }


        /**
         * FUNCTION: pparse
         *
         * Calls parse, and then prints out $this->_tmploutput
         *
         * @access public
         * @return boolean true/false
         */
pedro_morgan's avatar
pedro_morgan committed
        public function pparse()
        {
tbrehm's avatar
tbrehm committed
            if (!$this->_parsed) $this->_parse();
            print($this->_tmploutput);
            return true;
        }

        /**
         * FUNCTION: pprint
         *
         * Alias for pparse()
         *
         * @access public
         */
pedro_morgan's avatar
pedro_morgan committed
        public function pprint()
        {
tbrehm's avatar
tbrehm committed
            return $this->pparse();
        }


        /**
         * FUNCTION: grab
         *
         * Returns the parsed output, ready for printing, passing to mail() ...etc.
         * Invokes $this->_parse() if template has not yet been parsed.
         *
         * @access public
         * @return boolean true/false
         */
pedro_morgan's avatar
pedro_morgan committed
        public function grab()
        {
tbrehm's avatar
tbrehm committed
            if (!$this->_parsed) $this->_parse();
            return $this->_tmploutput;
        }

pedro_morgan's avatar
pedro_morgan committed
        /*-----------------------------------------------------------------------------\
        |                           private functions                                  |
        \-----------------------------------------------------------------------------*/
tbrehm's avatar
tbrehm committed

        /**
         * FUNCTION: vlibTemplate
         *
         * vlibTemplate constructor.
         * if $tmplfile has been passed to it, it will send to $this->newTemplate()
         *
         * @param string $tmplfile full path to template file
         * @param array $options see above
         * @return boolean true/false
         * @access private
         */
pedro_morgan's avatar
pedro_morgan committed
        public function __construct($tmplfile = null, $options = null)
        {
tbrehm's avatar
tbrehm committed
            if (is_array($tmplfile) && $options == null) {
                $options = $tmplfile;
                unset($tmplfile);
            }

            $this->VLIBTEMPLATE_ROOT = dirname(realpath(__FILE__));

            if (is_array(vlibIni::vlibTemplate())) {
                foreach (vlibIni::vlibTemplate() as $name => $val) {
                    $this->OPTIONS[$name] = $val;
                }
            }

            if (is_array($options)) {
                foreach($options as $key => $val) {
                    $key = strtoupper($key);
                    if ($key == 'PATH') {
                        $this->setPath($val);
pedro_morgan's avatar
pedro_morgan committed
                    } else {
tbrehm's avatar
tbrehm committed
                        $this->_setOption($key, strtolower($val));
                    }
                }
            }
            if($tmplfile) $this->newTemplate($tmplfile);
            if ($this->OPTIONS['GLOBAL_CONTEXT_VARS']) $this->setContextVars();
            return true;
        }

        /** FUNCTION: _getData
         *
         * function returns the text from the file, or if we're using cache, the text
         * from the cache file. MUST RETURN DATA.
         * @param string tmplfile contains path to template file
         * @param do_eval used for included files. If set then this function must do the eval()'ing.
         * @access private
         * @return mixed data/string or boolean
         */
pedro_morgan's avatar
pedro_morgan committed
        private function _getData ($tmplfile, $do_eval=false)
        {
            //* check the current file depth
tbrehm's avatar
tbrehm committed
            if ($this->_includedepth > $this->OPTIONS['MAX_INCLUDES'] || $tmplfile == false) {
                return;
pedro_morgan's avatar
pedro_morgan committed
            } else {
tbrehm's avatar
tbrehm committed
                if ($this->_debug) array_push ($this->_debugIncludedfiles, $tmplfile);
                if ($do_eval) {
                    array_push($this->_currentincludedir, dirname($tmplfile));
                    $this->_includedepth++;
                }
            }


pedro_morgan's avatar
pedro_morgan committed
            if($this->_cache && $this->_checkCache($tmplfile)) { //* cache exists so lets use it
tbrehm's avatar
tbrehm committed
                $data = fread($fp = fopen($this->_cachefile, 'r'), filesize($this->_cachefile));
                fclose($fp);
pedro_morgan's avatar
pedro_morgan committed
            } else { //* no cache lets parse the file
tbrehm's avatar
tbrehm committed
                $data = fread($fp = fopen($tmplfile, 'r'), filesize($tmplfile));
                fclose($fp);

                $regex = '/(<|<\/|{|{\/|<!--|<!--\/){1}\s*';
                $regex.= 'tmpl_([\w]+)\s*';
                $regex.= '(?:';
                $regex.=    '(?:';
                $regex.=        '(name|format|escape|op|value|file)';
                $regex.=        '\s*=\s*';
                $regex.=    ')?';
                $regex.=    '(?:[\"\'])?';
                $regex.=    '((?<=[\"\'])';
                $regex.=    '[^\"\']*|[a-z0-9_\.]*)';
                $regex.=    '[\"\']?';
                $regex.= ')?\s*';
                $regex.= '(?:';
                $regex.=    '(?:';
                $regex.=        '(name|format|escape|op|value)';
                $regex.=        '\s*=\s*';
                $regex.=    ')';
                $regex.=    '(?:[\"\'])?';
                $regex.=    '((?<=[\"\'])';
                $regex.=    '[^\"\']*|[a-z0-9_\.]*)';
                $regex.=    '[\"\']?';
                $regex.= ')?\s*';
                $regex.= '(?:';
                $regex.=    '(?:';
                $regex.=        '(name|format|escape|op|value)';
                $regex.=        '\s*=\s*';
                $regex.=    ')';
                $regex.=    '(?:[\"\'])?';
                $regex.=    '((?<=[\"\'])';
                $regex.=    '[^\"\']*|[a-z0-9_\.]*)';
                $regex.=    '[\"\']?';
                $regex.= ')?\s*';
                $regex.= '(?:>|\/>|}|-->){1}';
                $regex.= '([\r\n|\n|\r])?/ie';
                $data = preg_replace($regex,"\$this->_parseTag(array('\\0','\\1','\\2','\\3','\\4','\\5','\\6','\\7','\\8','\\9'));",$data);

                if ($this->_cache) { // add cache if need be
                    $this->_createCache($data);
                }
            }

pedro_morgan's avatar
pedro_morgan committed
            //* now we must parse the $data and check for any <tmpl_include>'s
tbrehm's avatar
tbrehm committed
            if ($this->_debug) $this->doDebugWarnings(file($tmplfile), $tmplfile);

            if ($do_eval) {
                $success = @eval('?>'.$data.'<?php return 1;');
                $this->_includedepth--;
                array_pop($this->_currentincludedir);
                return $success;
pedro_morgan's avatar
pedro_morgan committed
            } else {
tbrehm's avatar
tbrehm committed
                return $data;
            }
        }

        /**
         * FUNCTION: _fileSearch
         *
         * Searches for all possible instances of file { $file }
         *
         * @param string $file path of file we're looking for
         * @access private
         * @return mixed fullpath to file or boolean false
         */
pedro_morgan's avatar
pedro_morgan committed
        private function _fileSearch($file) 
        {
tbrehm's avatar
tbrehm committed
            $filename = basename($file);
            $filepath = dirname($file);

pedro_morgan's avatar
pedro_morgan committed
            //* check fullpath first..
tbrehm's avatar
tbrehm committed
            $fullpath = $filepath.'/'.$filename;
            if (is_file($fullpath)) return $fullpath;

pedro_morgan's avatar
pedro_morgan committed
            //* ..then check for relative path for current directory..
tbrehm's avatar
tbrehm committed
            if (!empty($this->_currentincludedir)) {
                $currdir = $this->_currentincludedir[(count($this->_currentincludedir) -1)];
                $relativepath = realpath($currdir.'/'.$filepath.'/'.$filename);
                if (is_file($relativepath)) {
                    array_push ($this->_currentincludedir, dirname($relativepath));
                    return $relativepath;
                }
            }

pedro_morgan's avatar
pedro_morgan committed
            //* ..then check for relative path for all additional given paths..
tbrehm's avatar
tbrehm committed
            if (!empty($this->OPTIONS['INCLUDE_PATHS'])) {
                foreach ($this->OPTIONS['INCLUDE_PATHS'] as $currdir) {
                    $relativepath = realpath($currdir.'/'.$filepath.'/'.$filename);
                    if (is_file($relativepath)) {
                        return $relativepath;
                    }
                }
            }

pedro_morgan's avatar
pedro_morgan committed
            //* ..then check path from TEMPLATE_DIR..
tbrehm's avatar
tbrehm committed
            if (!empty($this->OPTIONS['TEMPLATE_DIR'])) {
                $fullpath = realpath($this->OPTIONS['TEMPLATE_DIR'].'/'.$filepath.'/'.$filename);
                if (is_file($fullpath)) return $fullpath;
            }

pedro_morgan's avatar
pedro_morgan committed
            //* ..then check relative path from executing php script..
tbrehm's avatar
tbrehm committed
            $fullpath = realpath($filepath.'/'.$filename);
            if (is_file($fullpath)) return $fullpath;

pedro_morgan's avatar
pedro_morgan committed
            //* ..then check path from template file.
tbrehm's avatar
tbrehm committed
            if (!empty($this->VLIBTEMPLATE_ROOT)) {
                $fullpath = realpath($this->VLIBTEMPLATE_ROOT.'/'.$filepath.'/'.$filename);
                if (is_file($fullpath)) return $fullpath;
            }