<?php
/*
	This file contains all necessary to get a certain template.
	Use the function set_vartag('tpl_V_vartag2', '%value%2') to set a vartag*.
		Examples of use:
		* set_vartag('V_title', '$tmp_title');
			-> create a vartag with a value which depends on the value of 
			$tmp_title at the time of *retrieving* (it is thus it is possible to 
			create vars which change through the script )
			
		* set_vartag('V_title', 'Custom title here');
			-> create a vartag with a constant value
	
	Only following functions should be available to the user:
		* get_template: get a template
		* set_vartag: set a vartag
		* get_vartag_value: get the current value of the vartag
	
	----
	vartag*: an identifier which is used in a template, that is to be replaced by a value
		defined in the script. 
		EBNF-notation:
			vartag = '{{' ident '}}'
			ident = ('a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | "_" | "." )
*/

/*
	GLOBAL VARS
	These vars may be changed from outside this document.
*/
/* following variables are used for caching certain parts. Search for best values for each site,
	but only $use_file_cache set to true usually gives best results
*/

$use_tpln_cache = false;		//use cache for nested templates
$use_file_cache = false;		//use cache for read files


/*
	PRIVATE GLOBAL VARS
	Do *not* alter these vars directly, but use corresponding functions instead!
*/
static $auto_vartags = array();




/* @return $fstring
 * @param $tplname: name of template to read and process
 * @param $tpl_t_prefix: prefix for the nested templates in the template
 * @param $max_level_deep: this determines how deep the templatenesting may go; this
	parameter does not need to be specified and is only used with recursive calls:
	execution will be stopped when max_level_deep < 0
 * @desc gets the specified template, reads and processes (recursivity allowed)
	it, and returns the processed string */
function get_template($tplname, $tpl_t_prefix = 'T_', $max_level_deep=5)
{
	if ($max_level_deep < 0) 
	{
		return '<p><b>Maximum nested templates exceeded</b></p>';
	}
	
	/* max nr of loops to do. This determines the max nr of nested tags to get.
	e.g. {{T_tpl1{{V_var1{{V_var2{{V_var3}}}}}}}}. With a max_loop_count of 4 this will be
	completely 'templated', but with a max_loop_count of 3 the outer var (T_tpl1) will
	be omitted and not set
	*/
	$max_loop_count = 10;	
	
	//init vars for outer loop
	$done_all = true;		//see if everything is done in the outerloop (i.e. nested templates and vars)
	$max_all = $max_loop_count;
	
	//read current template and get vars
	$fstring = read_template($tplname);
	$tplreplace = get_template_vars();
	
	//outer loop to allow nested tags ('tags in tags')
	do
	{
		$max_all--;
		
		//while loop to process recursive defined templates ...
		$max_tpl_v = $max_loop_count; /*limit the number of recursive calls 
			(e.g. when circular references occur, this will stop the script before it
			runs out of time*/
		do
		{
			//init vars
			$max_tpl_v--;
			$done_tpl_v = true;
			
			foreach($tplreplace as $tpl_key => $tpl_value)
			{
				//this check is needed so we can check if there is still anything left to process
				if (strpos($fstring, '{{'.$tpl_key.'}}'))
				{
					$fstring = str_replace('{{'.$tpl_key.'}}', get_vartag_value($tpl_key), $fstring);
					$done_tpl_v = false;
					$done_all = false;
				}
			}
		} while ($done_tpl_v == false and $max_tpl_v>0);
		
		
		//search for all nested templates in the current template
		$tplnested = get_nested_templates($fstring, $tpl_t_prefix);
		//replace all nested templates
		foreach($tplnested as $tplnestedkey => $tplnestedvalue)
		{
			if (strpos($fstring, $tplnestedkey))
			{	
				$fstring = str_replace($tplnestedkey,
					get_template($tplnestedvalue, $tpl_t_prefix, $max_level_deep-1),
					$fstring);
				$done_all = $done_all and false;;
			}
		}
	} while ($done_all == false and $max_all >0);
	
	return $fstring;

}


/* @return array containing as key the vartag, and value the string on which to execute
	the function eval() to get the value. e.g. get_template_vars['vartag'] -> 'return "$i"'
 * @desc get all the vartags and the correspondig "values" that are declared to be used in the template
*/
function get_template_vars()
{	
	global $auto_vartags;
	
	return $auto_vartags;
}


/* @ return nothing
 * @ param $vartag_name: name of the vartag (e.g. V_index, V_header, ...);
 * @ param $vartag_value: the value to set; this must be a string that can be evaluated by 
	the function eval. E.g. '$f[$i]["forumid"]' (-> mind the single quotes!)
 * @ desc set a vartag with a certain value which automatically gets updated
	Example of use: set_vartag('V_forumtitle', '$f[$i]["forumtitle"]');
*/
function set_vartag($vartag, $vartag_value)
{
	global $auto_vartags;
	
	$eval = $vartag_value;
	
	if (strpos($eval, '$') === false)
	{
		//$vartag_value is a constant value
		$eval="'".$eval."'";
	}
	else
	{
		//replace all $x with a $GLOBALS['x']
		$eval = preg_replace('/(\$)([a-zA-Z_0-9]{1,})/', '\$GLOBALS["\\2"]', $eval);
	}
	
	$eval = 'return '.$eval.';';
	
	$auto_vartags[$vartag] = $eval;
}


/* @ return a string on which to run the function eval to get the real value
 * @ param $vartag: vartag to get name of; $vartag_name must start with the tpl_v_prefix
 * @ desc gets the update value of a vartag
*/
function get_vartag_value($vartag)
{
	global $auto_vartags, $i;
	
	//check if auto_vartag is defined
	if (isset($auto_vartags[$vartag]))
	{
		return eval($auto_vartags[$vartag]);
	}
	else
	{
		return false;
	}
}


/* @return array containing as key the string to find (format: {{%tpl_t_prefix%tpl_name}})
	and corresponding value the templatename. 
	e.g. get_nested_template["{{T_nested_template}}"] -> nested_template
 * @param $fstring: string to find templates in
 * @param $tpl_t_prefix: prefix for the nested templates
 * @desc searches for occurences of nested templates in a string and store these occurences in an array
*/
function get_nested_templates($fstring, $tpl_t_prefix)
{
	global $use_tpln_cache;
	
	static $nested_tpls = array();
	if ($use_tpln_cache == false)		$nested_tpls = array();
	
	//perl find command to search for {{%$tp_t_prefix%%var_name%}}
	$pat = '/\{\{'.preg_quote($tpl_t_prefix).'[a-zA-Z_0-9]{1,}\}\}/';
	$count = preg_match_all($pat, $fstring, $tpls);
	
	for($i=0; $i<$count; $i++)
	{
		$tpl = $tpls[0][$i];
		$val = substr($tpl, strlen('{{'.$tpl_t_prefix), strlen($tpl) - strlen('{{'.$tpl_t_prefix.'}}'));
		
		//set var if no cache used, or the var needs an update
		if ($use_tpln_cache == false or
			(isset($nested_tpls[$tpl]) == false) or 
			(isset($nested_tpls[$tpl]) and $nested_tpls[$tpl] != $val))
		{
			$nested_tpls[$tpl] = $val;
		}
	}
	
	return $nested_tpls;
}


/* @return string containing file (if filename does not exist, a message will be returned)
 * @param filename file to read
 * @desc read a file to a string (this function always uses a cache for optimized accesspeed;
	this automatically updates the contents of the file if changed in between two calls)
*/
function read_template($filename)
{
	global $use_file_cache;
	
	static $files_cache = array(); //contains filecache
	if ($use_file_cache == false)		$files_cache = array();
	
	$fcontents = false;
	$filename.='.htm';
	
	if (file_exists($filename))
	{
		//clear filecache if file has changed between last update and this moment
		if (isset($files_cache[$filename]) and $files_cache[$filename]["Time"] != filemtime($filename))
		{
			unset($files_cache[$filename]);
		}
		
		//read filename from file, as it is not yet in the cache
		if (isset($files_cache[$filename]) == false)
		{
			if (($fcontents = file_get_contents($filename)) != false)
			{
				//put file in filecache
				$files_cache[$filename] = array("File"=>$fcontents, "Time"=>filemtime($filename));
			}
		}
		//file is in cache, and updated -> load from cache
		else
		{
			$fcontents = $files_cache[$filename]["File"];
		}
	}

	//file doesn't exist -> message
	if ($fcontents === false)		$fcontents = '<p><b>File <i>'.$filename.'</i> not found.</b></p>';
	
	return $fcontents;
}	

?>