Deprecated: Assigning the return value of new by reference is deprecated in /home/bluestat/public_html/source/index.php on line 477
ISSO - Blob - ViewGit - Blue Static
<?php
/*=====================================================================*\
|| ###################################################################
|| # Blue Static ISSO Framework
|| # Copyright (c)2005-2009 Blue Static
|| #
|| # This program is free software; you can redistribute it and/or modify
|| # it under the terms of the GNU General Public License as published by
|| # the Free Software Foundation; version 2 of the License.
|| #
|| # This program is distributed in the hope that it will be useful, but
|| # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|| # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|| # more details.
|| #
|| # You should have received a copy of the GNU General Public License along
|| # with this program; if not, write to the Free Software Foundation, Inc.,
|| # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|| ###################################################################
\*=====================================================================*/

/**
 * Database-Driven Template System (template.php)
 *
 * @package	ISSO
 */

require_once(ISSO . '/Functions.php');

/**
 * File-Based Template System
 *
 * This framework merely replaces the template loading functions with
 * file-system based ones. It has an optional caching system in which
 * template data will remain stored in the database as long as the filesystem
 * file is modified. To do this, pass a table name to setDatabaseCache() and make sure
 * there's a DB module that has access to a table with this schema:
 *
 * CREATE TABLE template (filename VARCHAR (250) NOT NULL, template TEXT NOT NULL, timestamp INT NOT NULL);
 *
 * @author		Blue Static
 * @copyright	Copyright (c)2005 - 2009, Blue Static
 * @package		ISSO
 *
 */
class BSTemplate
{
	/**
	 * The name of a function that is called before template parsing of phrases and conditionals occurs
	 * @var	string
	 */
	public static $preParseHook = ':undefined:';

	/**
	 * The database table name for the template cache
	 * @var string
	 */
	public static $dbCacheTable = null;

	/**
	 * The name of the function phrases are fetched with
	 * @var	string
	 */
	public static $langcall = 'gettext';

	/**
	 * The template path pattern; for instance, this could be: ./templates/%s.tpl
	 * @var string
	 */
	public static $templatePath = '%s';

	/**
	 * Array of pre-compiled templates that are stored for optimization
	 * @var	array
	 */
	protected static $cache = array();

	/**
	 * The name of the function phrases are sprintf() parsed with
	 * @var	string
	 */
	public static $langconst = 'sprintf';

	/**
	 * Template variables to populate
	 * @var array
	 */
	public $vars = array();

	/**
	 * Global variables
	 * @var array
	 */
	public static $globalVars = array();

	/**
	 * The file name of the template
	 * @var string
	 */
	protected $filename;

	/**
	 * Template contents
	 * @var string
	 */
	protected $template;

	/**
	 * Takes an array of template names, loads them, and then stores a
	 * parsed version for optimum speed.
	 *
	 * @param	array	List of template names to be cached
	 */
	public static function cache($namearray)
	{
		if (!self::$dbCacheTable)
		{
			return; // there's no point in pre-caching file templates
		}

		$cache = BSApp::$db->query("SELECT * FROM " . self::$dbCacheTable . " WHERE filename IN ('" . implode("', '", $namearray) . "')");
		while ($tpl = $cache->fetchArray())
		{
			self::$cache[$tpl['filename']] = $tpl;
		}
	}

	/**
	 * Fluent interface-compatible constructor
	 */
	public static function fetch()
	{
		$obj = new ReflectionClass(__CLASS__);
		$args = func_get_args();
		return $obj->newInstanceArgs($args);
	}

	/**
	 * Constructor
	 *
	 * @param	string	File name
	 */
	public function __construct($path)
	{
		$this->file = $path;

		// checks to see if the template has been cached
		if (isset(self::$cache[$this->file]))
		{
			if (!self::$dbCacheTable || filemtime(sprintf(self::$templatePath, $this->file)) <= self::$cache[$this->file]['timestamp'])
			{
				$this->template = self::$cache[$this->file]['template'];
				return;
			}
		}

		// it hasn't been cached
		$path = sprintf(self::$templatePath, $this->file);
		if (!is_file($path) || !is_readable($path))
		{
			throw new Exception("Could not load the template $path");
		}
		$this->template = $this->_parseTemplate(file_get_contents($path));
		self::$cache[$this->file]['template'] = $this->template;

		// store the template in the database
		if (self::$dbCacheTable)
		{
			BSApp::$db->query("REPLACE INTO " . self::$dbCacheTable . " SET template = '" . BSApp::$db->escapeString($this->template) . "', timestamp = " . TIMENOW . ", filename = '" . $this->file . "'");
			self::$cache[$this->file]['time'] = TIMENOW;
		}
	}

	/**
	 * Returns the template data
	 *
	 * @return	string	Final template data
	 */
	public function getTemplate()
	{
		return $this->template;
	}

	/**
	 * This function globalizes/extracts the assigned variables and then
	 * returns the output buffer
	 *
	 * @param	string	Unevaluated template
	 *
	 * @return	fluent interface
	 */
	public function evaluate()
	{
		extract($this->vars);
		extract(self::$globalVars);

		ob_start();
		$this->template = str_replace(array('$this->', 'self::'), 'null', $this->template); // don't want internal access coming from a template
		$this->template = '?>' . $this->template;
		$test = eval($this->template);
		$output = ob_get_clean();
		if ($output === false)
		{
			throw new Exception('A parse error was encountered while evaluating the template');
		}

		$this->template = $output;

		return $this;
	}

	/**
	 * Output a template fully compiled to the browser
	 */
	public function flush()
	{
		ob_start();

		if (empty($this->template))
		{
			throw new Exception('There is no output to print');
		}

		$template = $this->template;

		$debugBlock = '';
		if (BSApp::get_debug() && strpos($template, '</body>') !== false)
		{
			$debugBlock .= "\n<div align=\"center\">Executed in " . round(BSFunctions::fetch_microtime_diff('0 ' . $_SERVER['REQUEST_TIME']), 10) . ' seconds</div>';
			$debugBlock .= "\n<br /><div align=\"center\">" . BSApp::get_debug_list() . "</div>";

			if (BSApp::$db)
			{
				$queries = BSApp::$db->getHistory();

				$debugBlock .= "<br />\n" . '<table cellpadding="4" cellspacing="1" border="0" align="center" width="30%" style="background-color: rgb(60, 60, 60); color: white">' . "\n\t" . '<tr><td><strong>Query Debug</strong></td></tr>';

				foreach ($queries as $query)
				{
					$debugBlock .= "\n\t<tr style=\"background-color: rgb(230, 230, 230); color: black\">";
					$debugBlock .= "\n\t\t<td>";
					$debugBlock .= "\n\t\t\t$query[query]\n\n\t\t\t<div style=\"font-size: 9px;\">($query[time])</div>\n<!--\n$query[trace]\n-->\n\t\t</td>\n\t</tr>";
				}

				$debugBlock .= "\n</table>\n\n\n";
			}

			$template = str_replace('</body>', $debugBlock . '</body>', $template);
		}

		print($template);
	}

	/**
	 * A wrapper for all the parsing functions and compiling functins
	 *
	 * @param	string	Unparsed template data
	 *
	 * @return	string	Parsed template data
	 */
	protected function _parseTemplate($template)
	{
		if (function_exists(self::$preParseHook))
		{
			$template = call_user_func(self::$preParseHook, $template, $this);
		}

		$template = $this->_parseTokens($template);
		return $template;
	}

	/**
	 * Parses tokens <% %>
	 *
	 * @param	string	Template data
	 *
	 * @return	string	Parsed template data
	 */
	protected function _parseTokens($template)
	{
		$stack = array();
		$tokens = array();

		for ($i = 0; $i < strlen($template); $i++)
		{
			// opening tag
			if ($template[$i] == '<' && $template[$i + 1] == '%')
			{
				array_push($stack, $i);
			}
			// closing tag
			else if ($template[$i] == '%' && $template[$i + 1] == '>')
			{
				// there's no stack, so it's a bad template
				if (sizeof($stack) == 0)
				{
					throw new Exception('Malformed template data: unexpected closing substitution tag');
				}
				// we're good and nested
				else if (sizeof($stack) == 1)
				{
					$open = array_pop($stack);
					$echo = ($template[$open + 2] == '-' ? 'echo ' : '');
					$replace = '<?php ' . $echo . BSFunctions::substring($template, $open + ($echo ? 3 : 2), $i) . ' ?>';
					$template = substr_replace($template, $replace, $open, ($i + 2) - $open);
				}
				// just pop it off
				else
				{
					array_pop($stack);
				} // end else
			} // end if
		} // end for

		return $template;
	}
}

?>