Skip to content
tpl.inc.php 45.1 KiB
Newer Older
tbrehm's avatar
tbrehm committed
<?php
/**
 * 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 $

//** check and avoid multiple loading of class
tbrehm's avatar
tbrehm committed
if (!defined('vlibTemplateClassLoaded')) {
	define('vlibTemplateClassLoaded', 1);
	include_once ISPC_CLASS_PATH.'/tpl_error.inc.php';
	include_once ISPC_CLASS_PATH.'/tpl_ini.inc.php';

		/*-----------------------------------------------------------------------------\
        |                                 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
		);

		/** open and close tags used for escaping */
		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)')
		/** open and close tags used for formatting */
		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'=> '))')
		);

		/** operators allowed when using extended TMPL_IF syntax */
		private $allowed_if_ops = array('==', '!=', '<>', '<', '>', '<=', '>=');



		/** dbs allowed by vlibTemplate::setDbLoop(). */
		private $allowed_loop_dbs = array('MYSQL', 'POSTGRESQL', 'INFORMIX', 'INTERBASE', 'INGRES',
			'MSSQL', 'MSQL', 'OCI8', 'ORACLE', 'OVRIMOS', 'SYBASE');

		/** root directory of vlibTemplate automagically filled in */
		private $VLIBTEMPLATE_ROOT = null;



		/** contains current directory used when doing recursive include */
		private $_currentincludedir = array();



		/** current depth of includes */
		private $_includedepth = 0;



		/** full path to tmpl file */
		private $_tmplfilename = null;



		/** file data before it's parsed */
		private $_tmplfile = null;



		/** parsed version of file, ready for eval()ing */
		private $_tmplfilep = null;



		/** eval()ed version ready for printing or whatever */
		private $_tmploutput = null;



		/** array for variables to be kept */
		private $_vars = array();



		/** array where loop variables are kept */
		private $_arrvars = array();

		/** array which holds the current namespace during parse */
		private $_namespace = array();



		/** variable is set to true once the template is parsed, to save re-parsing everything */
		private $_parsed = false;



		/** array holds all unknowns vars */
		private $_unknowns = array();



		/** microtime when template parsing began */
		private $_firstparsetime = null;



		/** total time taken to parse template */
		private $_totalparsetime = null;



		/** name of current loop being passed in */
		private $_currloopname = null;



		/** rows with the above loop */
		private $_currloop = array();
		/** define vars to avoid warnings */
		private $_debug = null;
		private $_cache = null;
		/** array which holds the dynamic Includes */
		private $_dyninclude = array();
		/*-----------------------------------------------------------------------------\
        |                           public functions                                   |
        \-----------------------------------------------------------------------------*/





		/**
		 * 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
		 */
		public function newTemplate($tmplfile)
		{
			if (!$tfile = $this->_fileSearch($tmplfile)){
				vlibTemplateError::raiseError('VT_ERROR_NOFILE', KILL, $tmplfile);
			}
			//* make sure that any parsing vars are cleared for the new template
			$this->_tmplfile = null;
			$this->_tmplfilep = null;
			$this->_tmploutput = null;
			$this->_parsed = false;
			$this->_unknowns = array();
			$this->_firstparsetime = null;
			$this->_totalparsetime = null;

			//* reset debug module
			if ($this->_debug){
				$this->_debugReset();
			}
			$this->_tmplfilename = $tfile;
			return true;
		}

		/**
		 * 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
		 */
		public function setVar($k, $v = null)
		{
			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;
					}
				}
				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;
					return false;
				}
			}
			return true;
		}



		/**
		 * 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
		 */
		public function setInclude($k, $v = null)
		{
			if(is_array($k)) {
tbrehm's avatar
tbrehm committed
				foreach($k as $key => $val) {
					$this->_dyninclude[$key] = $val;
				}
			} else {
				$this->_dyninclude[$k] = $v;
			}
			return true;
		}

		/**
		 * 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
		 */
		public function unsetVar()
		{
			$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;
		}

		/**
		 * Gets all vars currently set in global namespace.
		 * @return array
		 * @access public
		 */
		public function getVars()
		{
			return empty($this->_vars) ? false : $this->_vars;
		}

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

		/**
		 * sets the GLOBAL_CONTEXT_VARS
		 * @return true
		 * @access public
		 */
		public function setContextVars()
		{
			$_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'];

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

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

		/**
		 * 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
		 */
		public function setLoop($k, $v)
		{
			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);
		 * [** 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
		 */
		public function setDbLoop($loopname, $result, $db_type = 'MYSQL')
		{
			$db_type = strtoupper($db_type);
tbrehm's avatar
tbrehm committed
            if (!in_array($db_type, $this->allowed_loop_dbs)) {
                vlibTemplateError::raiseError('VT_WARNING_INVALID_LOOP_DB', WARNING, $db_type);
tbrehm's avatar
tbrehm committed
                return false;
            }

            $loop_arr = array();
            // TODO: Are all these necessary as were onyl using mysql and possible postgres ? - pedro
tbrehm's avatar
tbrehm committed
            switch ($db_type) {

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

                case 'POSTGRESQL':
                    if (get_resource_type($result) != 'pgsql result') {
                        vlibTemplateError::raiseError('VT_WARNING_INVALID_RESOURCE', WARNING, $db_type);
tbrehm's avatar
tbrehm committed
                        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);
                    }
tbrehm's avatar
tbrehm committed

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

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

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

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

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

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

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

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

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

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

		/**
		 * 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
		 */
		public function newLoop($loopname)
		{
			if (preg_match('/^[a-z_]+[a-z0-9_]*$/i', $loopname)) {
				$this->_currloopname[$loopname] = $loopname;
				$this->_currloop[$loopname] = array();
				return true;
				return false;
			}
		}

		/**
		 * 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
		 */
		public function addRow($row, $loopname = null)
		{
			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;
				return false;
			}
		}

		/**
		 * 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
		 */
		public function addLoop($loopname = null)
		{
			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;
			} elseif (!isset($this->_currloop[$loopname]) || empty($this->_currloopname)) { // newLoop not yet envoked
				vlibTemplateError::raiseError('VT_WARNING_LOOP_NOT_SET', WARNING);
				return false;
			} else { // add a specific loop
				$this->setLoop($loopname, $this->_currloop[$loopname]);
				unset($this->_currloopname[$loopname], $this->_currloop[$loopname]);
			}
			return true;
		}

		/**
		 * 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
		 */
		public function unsetLoop()
		{
			$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;
		}

		/**
		 * 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
		 */
		public function reset()
		{
			$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;
		}

		/**
		 * Unsets all variables in the template
		 * @return boolean true
		 * @access public
		 */
		public function clearVars()
		{
			$this->_vars = array();
			return true;
		}

		/**
		 * Unsets all loops in the template
		 * @return boolean true
		 * @access public
		 */
		public function clearLoops()
		{
			$this->_arrvars = array();
			$this->_currloopname = null;
			$this->_currloop = array();
			return true;
		}

		/**
		 * Unsets all variables and loops set using setVar/Loop()
		 * @return boolean true
		 * @access public
		 */
		public function clearAll()
		{
			$this->clearVars();
			$this->clearLoops();
			return true;
		}

		/**
		 * 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
		 */
		public function unknownsExist()
		{
			return !empty($this->_unknowns);
		}

		/**
		 * Alias for unknownsExist.
		 * @access public
		 */
		public function unknowns()
		{
			return $this->unknownsExist();
		}

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

		/**
		 * 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
		 */
		public function setUnknowns($arg)
		{
			$arg = strtolower(trim($arg));
			if (preg_match('/^ignore|remove|print|leave|comment$/', $arg)) {
				$this->OPTIONS['UNKNOWNS'] = $arg;
				return true;
			}
			return false;
		}

		/**
		 * 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
		 */
		public function setPath()
		{
			$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;
		}

		/**
		 * 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
		 */
		public function getParseTime()
		{
			if ($this->OPTIONS['TIME_PARSE'] && $this->_parsed) {
				return $this->_totalparsetime;
			}
			return false;
		}


		/**
		 * 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
		 */
		public function fastPrint()
		{
			$ret = $this->_parse('ob_gzhandler');
			print($this->_tmploutput);
			return $ret;
		}


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

		/**
		 * Alias for pparse()
		 * @access public
		 */
		public function pprint()
		{
			return $this->pparse();
		}


		/**
		 * 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
		 */
		public function grab()
		{
			if (!$this->_parsed) $this->_parse();
			return $this->_tmploutput;
		}

		/*-----------------------------------------------------------------------------\
        |                           private functions                                  |
        \-----------------------------------------------------------------------------*/
		/**
		 * 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
		 */
		public function __construct($tmplfile = null, $options = null)
		{
			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);
						$this->_setOption($key, strtolower($val));
					}
				}
			}
			if($tmplfile) $this->newTemplate($tmplfile);
			if ($this->OPTIONS['GLOBAL_CONTEXT_VARS']) $this->setContextVars();
			return true;
		}
		 * 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
		 */
		private function _getData ($tmplfile, $do_eval=false, $tmpl_from_string = false)
		{
			//* check the current file depth
			if ($this->_includedepth > $this->OPTIONS['MAX_INCLUDES'] || $tmplfile == false) {
				return;
			} else {
				if ($this->_debug){
					if($tmpl_from_string) array_push($this->_debugIncludedfiles, 'String: ' . substr($tmplfile, 0, 25) . '...');
					else array_push($this->_debugIncludedfiles, $tmplfile);
				if ($do_eval) {
					if($tmpl_from_string == true) array_push($this->_currentincludedir, end($this->_currentincludedir));
					else array_push($this->_currentincludedir, dirname($tmplfile));
			if($this->_cache && $this->_checkCache($tmplfile, $tmpl_from_string)) { //* cache exists so lets use it
				$data = file_get_contents($this->_cachefile);
			} else { //* no cache lets parse the file
				if($tmpl_from_string == true) {
					$data = $tmplfile;
				} else {
					$data = file_get_contents($tmplfile);

				$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.= '(?:>|\/>|}|-->){1}';
				$regex.= '/i';
				$data = preg_replace_callback($regex, array($this, '_parseTag'), $data);
				if ($this->_cache) { // add cache if need be
					$this->_createCache($data);
				}
			}
			//* now we must parse the $data and check for any <tmpl_include>'s
			if ($this->_debug && $tmpl_from_string == false) $this->doDebugWarnings(file($tmplfile), $tmplfile);
			if ($do_eval) {
				$success = @eval('?>'.$data.'<?php return 1;');
				$this->_includedepth--;
				array_pop($this->_currentincludedir);
				return $success;
		/**
		 * 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
		 */
		private function _fileSearch($file)
		{

			$filename = basename($file);
			$filepath = dirname($file);

			if(isset($_SESSION['s']['module']['name']) && isset($_SESSION['s']['theme'])) {
				if(is_file(ISPC_THEMES_PATH.'/'.$_SESSION['s']['theme'].'/templates/'.$_SESSION['s']['module']['name'].'/'.$filename)) {
					return ISPC_THEMES_PATH.'/'.$_SESSION['s']['theme'].'/templates/'.$_SESSION['s']['module']['name'].'/'.$filename;
				}
			//* check fullpath first..
			$fullpath = $filepath.'/'.$filename;
			if (is_file($fullpath)) return $fullpath;

			//* ..then check for relative path for current directory..
			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;
				}
			}
			//* ..then check for relative path for all additional given paths..
			if (!empty($this->OPTIONS['INCLUDE_PATHS'])) {
				foreach ($this->OPTIONS['INCLUDE_PATHS'] as $currdir) {
					$relativepath = realpath($currdir.'/'.$filepath.'/'.$filename);
					if (is_file($relativepath)) {
						return $relativepath;
					}
				}
			}
			//* ..then check path from TEMPLATE_DIR..
			if (!empty($this->OPTIONS['TEMPLATE_DIR'])) {
				$fullpath = realpath($this->OPTIONS['TEMPLATE_DIR'].'/'.$filepath.'/'.$filename);
				if (is_file($fullpath)) return $fullpath;
			//* ..then check relative path from executing php script..
			$fullpath = realpath($filepath.'/'.$filename);
			if (is_file($fullpath)) return $fullpath;
			//* ..then check path from template file.
			if (!empty($this->VLIBTEMPLATE_ROOT)) {
				$fullpath = realpath($this->VLIBTEMPLATE_ROOT.'/'.$filepath.'/'.$filename);
				if (is_file($fullpath)) return $fullpath;
			return false; // uh oh, file not found
		}
		/**
		 * Modifies the array $arr to add Template variables, __FIRST__, __LAST__ ..etc
		 * if $this->OPTIONS['LOOP_CONTEXT_VARS'] is true.
		 * Used by $this->setloop().
		 * @param array $arr
		 * @return array new look array
		 * @access private
		 */
		private function _arrayBuild($arr)
		{
			if (is_array($arr) && !empty($arr)) {
				$arr = array_values($arr); // to prevent problems w/ non sequential arrays
				for ($i = 0; $i < count($arr); $i++) {
					if(!is_array($arr[$i]))  return false;
					foreach ($arr[$i] as $k => $v) {
						unset($arr[$i][$k]);
						if ($this->OPTIONS['CASELESS']) $k = strtolower($k);
						if (preg_match('/^[0-9]+$/', $k)) $k = '_'.$k;

						if (is_array($v)) {
							if (($arr[$i][$k] = $this->_arrayBuild($v)) == false) return false;
						} else { // reinsert the var
							$arr[$i][$k] = $v;
						}
					}
					if ($this->OPTIONS['LOOP_CONTEXT_VARS']) {
						if ($i == 0) $arr[$i]['__FIRST__'] = true;
						if (($i + 1) == count($arr)) $arr[$i]['__LAST__'] = true;
						if ($i != 0 && (($i + 1) < count($arr))) $arr[$i]['__INNER__'] = true;
						if (is_int(($i+1) / 2))  $arr[$i]['__EVEN__'] = true;
						if (!is_int(($i+1) / 2))  $arr[$i]['__ODD__'] = true;
						$arr[$i]['__ROWNUM__'] = ($i + 1);
					}
				}
				return $arr;
			} elseif (empty($arr)) {