<?

/*
	CVS Revision. 1.1.0
*/

class FastTemplate {

	var $FILELIST	=	array();	//	Holds the array of filehandles
									//	FILELIST[HANDLE] == "fileName"

	var $DYNAMIC	=	array();	//	Holds the array of dynamic
									//	blocks, and the fileHandles they
									//	live in.

	var $PARSEVARS	=	array();	//	Holds the array of Variable
									//	handles.
									//	PARSEVARS[HANDLE] == "value"

	var	$LOADED		=	array();	//	We only want to load a template
									//	once - when it's used.
									//	LOADED[FILEHANDLE] == 1 if loaded
									//	undefined if not loaded yet.

	var	$HANDLE		=	array();	//	Holds the handle names assigned
									//	by a call to parse()

	var	$ROOT		=	"";			//	Holds path-to-templates

	var $WIN32		=	false;		//	Set to true if this is a WIN32 server

	var $ERROR		=	"";			//	Holds the last error message

	var $LAST		=	"";			//	Holds the HANDLE to the last
									//	template parsed by parse()

	var $STRICT		=	true;		//	Strict template checking.
									//	Unresolved vars in templates will
									//	generate a warning when found.

//	************************************************************

	function FastTemplate ($pathToTemplates = "")
	{
		global $php_errormsg;

		if(!empty($pathToTemplates))
		{
			$this->set_root($pathToTemplates);
		}

	}	// end (new) FastTemplate ()


//	************************************************************
//	All templates will be loaded from this "root" directory
//	Can be changed in mid-process by re-calling with a new
//	value.

	function set_root ($root)
	{
		$trailer = substr($root,-1);

		if(!$this->WIN32)
		{
			if( (ord($trailer)) != 47 )
			{
				$root = "$root". chr(47);
			}

			if(is_dir($root))
			{
				$this->ROOT = $root;
			}
			else
			{
				$this->ROOT = "";
				$this->error("Specified ROOT dir [$root] is not a directory");
			}
		}
		else
		{
			// WIN32 box - no testing
			if( (ord($trailer)) != 92 )
			{
				$root = "$root" . chr(92);
			}
			$this->ROOT = $root;
		}

	}	// End set_root()


//  **************************************************************
//  Calculates current microtime
//	I throw this into all my classes for benchmarking purposes
//	It's not used by anything in this class and can be removed
//	if you don't need it.


	function utime ()
	{
		$time = explode( " ", microtime());
		$usec = (double)$time[0];
		$sec = (double)$time[1];
		return $sec + $usec;
    }

//  **************************************************************
//	Strict template checking, if true sends warnings to STDOUT when
//	parsing a template with undefined variable references
//	Used for tracking down bugs-n-such. Use no_strict() to disable.

	function strict ()
	{
		$this->STRICT = true;
	}

//	************************************************************
//	Silently discards (removes) undefined variable references
//	found in templates

	function no_strict ()
	{
		$this->STRICT = false;
	}

//	************************************************************
//	A quick check of the template file before reading it.
//	This is -not- a reliable check, mostly due to inconsistencies
//	in the way PHP determines if a file is readable.

	function is_safe ($filename)
	{
		if(!file_exists($filename))
		{
			$this->error("[$filename] does not exist",0);
			return false;
		}
		return true;
	}

//	************************************************************
//	Grabs a template from the root dir and 
//	reads it into a (potentially REALLY) big string

	function get_template ($template)
	{
		if(empty($this->ROOT))
		{
			$this->error("Cannot open template. Root not valid.",1);
			return false;
		}

		$filename	=	"$this->ROOT"."$template";
		$contents = implode("",(@file($filename)));
		if( (!$contents) or (empty($contents)) )
		{
			$this->error("get_template() failure: [$filename] $php_errormsg",1);
		}

		return $contents;

	} // end get_template

//	************************************************************
//	Prints the warnings for unresolved variable references
//	in template files. Used if STRICT is true

	function show_unknowns ($Line)
	{
		$unknown = array();
		if (ereg("({[A-Z0-9_]+})",$Line,$unknown))
		{
			$UnkVar = $unknown[1];
			if(!(empty($UnkVar)))
			{
				@error_log("[FastTemplate] Warning: no value found for variable: $UnkVar ",0);
			}
		}
	}	// end show_unknowns()

//	************************************************************
//	This routine get's called by parse() and does the actual
//	{VAR} to VALUE conversion within the template.

	function parse_template ($template, $tpl_array)
	{
		while ( list ($key,$val) = each ($tpl_array) )
		{
			if (!(empty($key)))
			{
				if(gettype($val) != "string")
				{
					settype($val,"string");
				}

				//	php4 doesn't like '{$' combinations.
				$key = '{'."$key".'}';
				$template = ereg_replace("$key","$val","$template");
				//$template = str_replace("$key","$val","$template");
			}
		}

		if(!$this->STRICT)
		{
			// Silently remove anything not already found

			$template = ereg_replace("{([A-Z0-9_]+)}","",$template);
		}
		else
		{
			// Warn about unresolved template variables
			if (ereg("({[A-Z0-9_]+})",$template))
			{
				$unknown = split("\n",$template);
				while (list ($Element,$Line) = each($unknown) )
				{
					$UnkVar = $Line;
					if(!(empty($UnkVar)))
					{
						$this->show_unknowns($UnkVar);
					}
				}
			}
		}
		return $template;

	}	// end parse_template();

//	************************************************************
//	The meat of the whole class. The magic happens here.

	function parse ( $ReturnVar, $FileTags )
	{
		$append = false;
		$this->LAST = $ReturnVar;
		$this->HANDLE[$ReturnVar] = 1;

		if (gettype($FileTags) == "array")
		{
			unset($this->$ReturnVar);	// Clear any previous data

			while ( list ( $key , $val ) = each ( $FileTags ) )
			{
				if ( (!isset($this->$val)) || (empty($this->$val)) )
				{
					$this->LOADED["$val"] = 1;
					if(isset($this->DYNAMIC["$val"]))
					{
						$this->parse_dynamic($val,$ReturnVar);
					}
					else
					{
						$fileName = $this->FILELIST[$val];
						$this->$val = $this->get_template($fileName);
					}
				}

				//	Array context implies overwrite

				$this->$ReturnVar = $this->parse_template($this->$val,$this->PARSEVARS);

				//	For recursive calls.

				$this->assign( array( $ReturnVar => $this->$ReturnVar ) );

			}
		}	// end if FileTags is array()
		else
		{
			// FileTags is not an array

			$val = $FileTags;

			if( (substr($val,0,1)) == '.' )
			{
				// Append this template to a previous ReturnVar

				$append = true;
				$val = substr($val,1);
			}

			if ( (!isset($this->$val)) || (empty($this->$val)) )
			{
					$this->LOADED["$val"] = 1;
					if(isset($this->DYNAMIC["$val"]))
					{
						$this->parse_dynamic($val,$ReturnVar);
					}
					else
					{
						$fileName = $this->FILELIST[$val];
						$this->$val = $this->get_template($fileName);
					}
			}

			if($append)
			{
				$this->$ReturnVar .= $this->parse_template($this->$val,$this->PARSEVARS);
			}
			else
			{
				$this->$ReturnVar = $this->parse_template($this->$val,$this->PARSEVARS);
			}

			//	For recursive calls.

			$this->assign(array( $ReturnVar => $this->$ReturnVar) );

		}
		return;
	}	//	End parse()


//	************************************************************

	function FastPrint ( $template = "" )
	{
		if(empty($template))
		{
			$template = $this->LAST;
		}

		if( (!(isset($this->$template))) || (empty($this->$template)) )
		{
			$this->error("Nothing parsed, nothing printed",0);
			return;
		}
		else
		{
			print $this->$template;
		}
		return;
	}

//	************************************************************

	function fetch ( $template = "" )
	{
		if(empty($template))
		{
			$template = $this->LAST;
		}
		if( (!(isset($this->$template))) || (empty($this->$template)) )
		{
			$this->error("Nothing parsed, nothing printed",0);
			return "";
		}

		return($this->$template);
	}


//	************************************************************

	function define_dynamic ($Macro, $ParentName)
	{
		//	A dynamic block lives inside another template file.
		//	It will be stripped from the template when parsed
		//	and replaced with the {$Tag}.

		$this->DYNAMIC["$Macro"] = $ParentName;
		return true;
	}

//	************************************************************

	function parse_dynamic ($Macro,$MacroName)
	{
		// The file must already be in memory.

		$ParentTag = $this->DYNAMIC["$Macro"];
		if( (!$this->$ParentTag) or (empty($this->$ParentTag)) )
		{
			$fileName = $this->FILELIST[$ParentTag];
			$this->$ParentTag = $this->get_template($fileName);
			$this->LOADED[$ParentTag] = 1;
		}
		if($this->$ParentTag)
		{
			$template = $this->$ParentTag;
			$DataArray = split("\n",$template);
			$newMacro = "";
			$newParent = "";
			$outside = true;
			$start = false;
			$end = false;
			while ( list ($lineNum,$lineData) = each ($DataArray) )
			{
				$lineTest = trim($lineData);
				if("<!-- BEGIN DYNAMIC BLOCK: $Macro -->" == "$lineTest" )
				{
					$start = true;
					$end = false;
					$outside = false;
				}
				if("<!-- END DYNAMIC BLOCK: $Macro -->" == "$lineTest" )
				{
					$start = false;
					$end = true;
					$outside = true;
				}
				if( (!$outside) and (!$start) and (!$end) )
				{
					$newMacro .= "$lineData\n"; // Restore linebreaks
				}
				if( ($outside) and (!$start) and (!$end) )
				{
					$newParent .= "$lineData\n"; // Restore linebreaks
				}
				if($end)
				{
					$newParent .= '{'."$MacroName}\n";
				}
				// Next line please
				if($end) { $end = false; }
				if($start) { $start = false; }
			}	// end While

			$this->$Macro = $newMacro;
			$this->$ParentTag = $newParent;
			return true;

		}	// $ParentTag NOT loaded - MAJOR oopsie
		else
		{
			@error_log("ParentTag: [$ParentTag] not loaded!",0);
			$this->error("ParentTag: [$ParentTag] not loaded!",0);
		}
		return false;
	}

//	************************************************************
//	Strips a DYNAMIC BLOCK from a template.

	function clear_dynamic ($Macro="")
	{
		if(empty($Macro)) { return false; }

		// The file must already be in memory.

		$ParentTag = $this->DYNAMIC["$Macro"];

		if( (!$this->$ParentTag) or (empty($this->$ParentTag)) )
		{
			$fileName = $this->FILELIST[$ParentTag];
			$this->$ParentTag = $this->get_template($fileName);
			$this->LOADED[$ParentTag] = 1;
		}

		if($this->$ParentTag)
		{
			$template = $this->$ParentTag;
			$DataArray = split("\n",$template);
			$newParent = "";
			$outside = true;
			$start = false;
			$end = false;
			while ( list ($lineNum,$lineData) = each ($DataArray) )
			{
				$lineTest = trim($lineData);
				if("<!-- BEGIN DYNAMIC BLOCK: $Macro -->" == "$lineTest" )
				{
					$start = true;
					$end = false;
					$outside = false;
				}
				if("<!-- END DYNAMIC BLOCK: $Macro -->" == "$lineTest" )
				{
					$start = false;
					$end = true;
					$outside = true;
				}
				if( ($outside) and (!$start) and (!$end) )
				{
					$newParent .= "$lineData\n"; // Restore linebreaks
				}
				// Next line please
				if($end) { $end = false; }
				if($start) { $start = false; }
			}	// end While

			$this->$ParentTag = $newParent;
			return true;

		}	// $ParentTag NOT loaded - MAJOR oopsie
		else
		{
			@error_log("ParentTag: [$ParentTag] not loaded!",0);
			$this->error("ParentTag: [$ParentTag] not loaded!",0);
		}
		return false;
	}


//	************************************************************

	function define ($fileList)
	{
		while ( list ($FileTag,$FileName) = each ($fileList) )
		{
			$this->FILELIST[$FileTag] = $FileName;
		}
		return true;
	}

//	************************************************************

	function clear_parse ( $ReturnVar = "")
	{
		$this->clear($ReturnVar);
	}

//	************************************************************

	function clear ( $ReturnVar = "" )
	{
		// Clears out hash created by call to parse()

		if(!empty($ReturnVar))
		{
			if( (gettype($ReturnVar)) != "array")
			{
				unset($this->$ReturnVar);
				return;
			}
			else
			{
				while ( list ($key,$val) = each ($ReturnVar) )
				{
					unset($this->$val);
				}
				return;
			}
		}

		// Empty - clear all of them

		while ( list ( $key,$val) = each ($this->HANDLE) )
		{
			$KEY = $key;
			unset($this->$KEY);
		}
		return;

	}	//	end clear()

//	************************************************************

	function clear_all ()
	{
		$this->clear();
		$this->clear_assign();
		$this->clear_define();
		$this->clear_tpl();

		return;

	}	//	end clear_all

//	************************************************************

	function clear_tpl ($fileHandle = "")
	{
		if(empty($this->LOADED))
		{
			// Nothing loaded, nothing to clear

			return true;
		}
		if(empty($fileHandle))
		{
			// Clear ALL fileHandles

			while ( list ($key, $val) = each ($this->LOADED) )
			{
				unset($this->$key);
			}
			unset($this->LOADED);

			return true;
		}
		else
		{
			if( (gettype($fileHandle)) != "array")
			{
				if( (isset($this->$fileHandle)) || (!empty($this->$fileHandle)) )
				{
					unset($this->LOADED[$fileHandle]);
					unset($this->$fileHandle);
					return true;
				}
			}
			else
			{
				while ( list ($Key, $Val) = each ($fileHandle) )
				{
					unset($this->LOADED[$Key]);
					unset($this->$Key);
				}
				return true;
			}
		}

		return false;

	}	// end clear_tpl

//	************************************************************

	function clear_define ( $FileTag = "" )
	{
		if(empty($FileTag))
		{
			unset($this->FILELIST);
			return;
		}

		if( (gettype($Files)) != "array")
		{
			unset($this->FILELIST[$FileTag]);
			return;
		}
		else
		{
			while ( list ( $Tag, $Val) = each ($FileTag) )
			{
				unset($this->FILELIST[$Tag]);
			}
			return;
		}
	}

//	************************************************************
//	Aliased function - used for compatibility with CGI::FastTemplate
	//function clear_parse ()
	//{
	//	$this->clear_assign();
	//}

//	************************************************************
//	Clears all variables set by assign()

	function clear_assign ()
	{
		if(!(empty($this->PARSEVARS)))
		{
			while(list($Ref,$Val) = each ($this->PARSEVARS) )
			{
				unset($this->PARSEVARS["$Ref"]);
			}
		}
	}

//	************************************************************

	function clear_href ($href)
	{
		if(!empty($href))
		{
			if( (gettype($href)) != "array")
			{
				unset($this->PARSEVARS[$href]);
				return;
			}
			else
			{
				while (list ($Ref,$val) = each ($href) )
				{
					unset($this->PARSEVARS[$Ref]);
				}
				return;
			}
		}
		else
		{
			// Empty - clear them all

			$this->clear_assign();
		}
		return;
	}

//	************************************************************

	function assign ($tpl_array, $trailer="")
	{
		if(gettype($tpl_array) == "array")
		{
			while ( list ($key,$val) = each ($tpl_array) )
			{
				if (!(empty($key)))
				{
					//	Empty values are allowed
					//	Empty Keys are NOT

					$this->PARSEVARS["$key"] = $val;
				}
			}
		}
		else
		{
			// Empty values are allowed in non-array context now.
			if (!empty($tpl_array))
			{
				$this->PARSEVARS["$tpl_array"] = $trailer;
			}
		}
	}

//	************************************************************
//	Return the value of an assigned variable.
//	Christian Brandel cbrandel@gmx.de

	function get_assigned($tpl_name = "")
	{
		if(empty($tpl_name)) { return false; }
		if(isset($this->PARSEVARS["$tpl_name"]))
		{
			return ($this->PARSEVARS["$tpl_name"]);
		}
		else
		{
			return false;
        }
	}

//	************************************************************

	function error ($errorMsg, $die = 0)
	{
		$this->ERROR = $errorMsg;
		echo "ERROR: $this->ERROR <BR> \n";
		if ($die == 1)
		{
			exit;
		}

		return;

	} // end error()


//	************************************************************



//	************************************************************

} // End class.FastTemplate.php3

?>
