<?php
/**
* This is a modified version of CDI's FastTemplates
*
* FastTemplates was originally written by some nice guys from
* CDI <cdi@thewebmasters.net>, you can find the sources
* here : http://www.thewebmasters.net/php/
*
* Since it was written back then when we still using PHP3,
* there are many 'old' function that should be replaced
* to gain a faster yet reliable templating system. Which
* I (and you) despradly need.
*
* So, one day (many days actually), I push myself to sit down
* and try to crank it down.
*
* Anyway, InTemplate goes to InArtS Multimedia (http://www.inartsmultimedia.com)
* the place where i get the real money ... :)
*
* @author Eris Ristemena <eristemena@yahoo.com>
* @vesion $Id: class.InTemplate.php,v 0.3 2002/01/10 07:07:39 eris Exp $
*/
class InTemplate
{
/**
* Holds the array of filehandles mFileList[HANDLE] == "fileName"
*
* @var array
*/
var $mFileList = array();
/**
* Holds the array of dynamic blocks,
* and the fileHandles they live in.
*
* @var array
*/
var $mDynamic = array();
/**
* Holds the array of Variable handles.
* mParseVars[HANDLE] == "value"
*
* @var array
*/
var $mParseVars = array();
/**
* We only want to load a template once - when it's used.
* mLoaded[FILEHANDLE] == 1 if loaded undefined if not loaded yet.
*
* @var array
*/
var $mLoaded = array();
/**
* Holds the handle names assigned by a call to parse()
*
* @var array
*/
var $mHandle = array();
/**
* Holds the handle names for selang-seling
*
* @var array
*/
var $mSeling = array();
/**
* Holds path-to-templates
*
* @var string
*/
var $mRoot = "";
/**
* Set to true if this is a WIN32 server
*
* @var boolean
*/
var $mWin32 = false;
/**
* Holds the last error message
*
* @var string
*/
var $mError = "";
/**
* Holds the HANDLE to the last template parsed by parse()
*
* @var string
*/
var $mLast = "";
/**
* Strict template checking.
* If it's set to true then unresolved vars in templates will generate a warning when found.
* For some reasons, i made this default to false.
*
* @var boolean
*/
var $mStrict = false;
/**
* Set the directory which all templates reside
*
* @param string $pathToTemplates path to templates
*/
function InTemplate( $pathToTemplates = "" )
{
if(!empty($pathToTemplates))
{
$this->SetRoot($pathToTemplates);
}
} // end InTemplate
/**
* All templates will be loaded from this "root" directory.
* Can be changed in mid-process by re-calling with a new
* value.
*
* @param string $root root directory
*/
function SetRoot( $root )
{
$trailer = substr($root,-1);
if(!$this->mWin32)
{
if( (ord($trailer)) != 47 )
{
$root = "$root". chr(47);
}
if(is_dir($root))
{
$this->mRoot = $root;
}
else
{
$this->mRoot = "";
$this->Error("Specified ROOT dir [$root] is not a directory");
}
}
else
{
// WIN32 box - no testing
if( (ord($trailer)) != 92 )
{
$root = "$root" . chr(92);
}
$this->mRoot = $root;
}
} // End SetRoot
/**
* A quick check of the template file before reading it.
* This should check if the file is safe to read,
* it means the file really exists and the permission
* allowed us to read it. [UNFINISHED]
*
* @param string $fileName file name for checking
* @return boolean
*/
function IsSafe( $fileName )
{
if(file_exists($fileName))
{
return true;
}
else
{
$this->Error("[$fileName] does not exist",0);
return false;
}
} // end IsSafe
/**
* Grabs a template from the root dir and
* reads it into a (potentially REALLY) big string
*
* @param string $template name of template's file
* @return string $contents contents of the template
*/
function GetTemplate( $template )
{
if (empty($this->mRoot))
{
$this->Error("Cannot open template. Root not valid.",1);
return false;
}
$file_name = "$this->mRoot"."$template";
if ($this->IsSafe($file_name))
{
$contents = implode("",(@file($file_name)));
if( (!$contents) or (empty($contents)) )
{
$this->Error("GetTemplate() failure: [$filename] $php_errormsg",1);
}
return $contents;
}
} // end GetTemplate
/**
* Maps a template filename to a (usually shorter) name.
* This new name is the name that you will use to refer to the templates.
* Let's just call it 'file tag'.
* In InTemplate, this routine will also search any dynamic block which
* resides in the associated file. And defines them using $this->DefineDynamic().
* Cute isn't it ? *_*
*
* @param array $fileList array of template files
* @return boolean
* @access public
*/
function Define( $fileList )
{
while ( list ($FileTag,$FileName) = each ($fileList) )
{
$this->mFileList["$FileTag"] = $FileName;
preg_match_all("/<!-- BEGIN DYNAMIC BLOCK: (.+) -->.*\{(.+)\}.*<!-- END DYNAMIC BLOCK: (.+) -->/sm",$this->GetTemplate($FileName),$matches);
for ($i=0; $i< count($matches[0]); $i++)
{
$this->DefineDynamic($matches[2][$i],$FileTag);
}
}
} // end Define
/**
* A dynamic block lives inside another template file.
* It will be stripped from the template when parsed
* and replaced with the {$Tag}.
*
* @param string $Macro macro's name for dynamic block
* @param string $fileTag file tag where the block resides
* @access public
*/
function DefineDynamic( $Macro, $fileTag )
{
$this->mDynamic["$Macro"] = $fileTag;
} // end DefineDynamic
/**
* Assigns values for var.
*
* @param array $parseVars array(var,value) or just a var (with value in trailer)
* @param string $trailer the value used only if $parseVars is not an array
* @access public
*/
function Assign($parseVars, $trailer="")
{
if(gettype($parseVars) == "array")
{
while ( list ($var,$val) = each ($parseVars) )
{
if (!(empty($var)))
{
// Empty values are allowed
// Empty Keys are NOT
$this->mParseVars["$var"] = $val;
}
}
}
else
{
// Empty values are allowed in non-array context now.
if (!empty($parseVars))
{
$this->mParseVars["$parseVars"] = $trailer;
}
}
} // end Assign
/**
* This routine does the actual {VAR} to VALUE conversion within the template.
* I made a 'crucial' change here, i used str_replace instead of ereg_replace.
* That's why this class should go faster than the old InTemplates.
*
* @param string $template the template befor parsing
* @param array $parseVars array(var,value)
* @return string $template the template after parsing
* @see Parse()
*/
function ParseTemplate( $template, $parseVars )
{
while ( list ($key,$val) = each ($parseVars) )
{
if (!(empty($key)))
{
if(gettype($val) != "string")
{
settype($val,"string");
}
$template = str_replace("\{$key}","$val","$template");
}
}
return $template;
} // end ParseTemplate
/**
* The meat of the whole class. The magic happens here.
*
* @param string $ReturnVar the return variable
* @param array $FileTags file tags
* @param array $lowerVar lower var, for cascading block
* @see Define()
* @see DefineDynamic()
* @see ParseTemplate()
* @access public
*/
function Parse( $ReturnVar, $FileTags, $lowerVar = "" )
{
$append = false;
$this->mLast = $ReturnVar;
$this->mHandle[$ReturnVar] = $FileTags;
// this conditional block is used for cascading block with lower var = $lowerVar
if (!empty($lowerVar))
{
// reset the lower macro
$this->ClearParse($lowerVar);
// if the lower Tag is a dynamic alternating block then reset the index
$lower_tag = $this->mHandle[$lowerVar];
if( (substr($lower_tag,0,1)) == '+' )
{
$lower_tag = substr($lower_tag,1);
$this->mSeling[$lower_tag][index] = 0;
}
}
if (gettype($FileTags) == "array")
{
unset($this->$ReturnVar); // Clear any previous data
while ( list ( $key , $file_tag ) = each ( $FileTags ) )
{
if ( (!isset($this->$file_tag)) || (empty($this->$file_tag)) )
{
$this->mLoaded["$file_tag"] = 1; // file tag $file_tag has been loaded
if(isset($this->mDynamic["$file_tag"]))
{
$this->ParseDynamic($file_tag,$ReturnVar);
}
else
{
$file_name = $this->mFileList["$file_tag"];
$this->$file_tag = $this->GetTemplate($file_name);
}
}
// Array context implies overwrite
$this->$ReturnVar = $this->ParseTemplate($this->$file_tag,$this->mParseVars);
// For recursive calls.
$this->Assign( array( $ReturnVar => $this->$ReturnVar ) );
}
}
else
{
// FileTags is not an array
$file_tag = $FileTags; // look dummy? ya but it has some points
if( (substr($file_tag,0,1)) == '.') // dynamic block
{
// Append this template to a previous ReturnVar
$append = true;
$file_tag = substr($file_tag,1);
}
if( (substr($file_tag,0,1)) == '+' ) // dynamic alternation block
{
// Append this template to a previous ReturnVar
$append = true;
$file_tag = substr($file_tag,1);
// set as alternation (the first time altered) and increment for indexing purpose
$this -> mSeling[$file_tag][index] += 1;
// sorry, this is for cascading puspose only
$this -> mSeling[$file_tag][macro] = $ReturnVar;
}
if ( (!isset($this->$file_tag)) || (empty($this->$file_tag)) )
{
$this->mLoaded["$file_tag"] = 1;
if (isset($this->mDynamic["$file_tag"]) && isset($this->mSeling[$file_tag][index]))
{
$this->ParseDynamic($ReturnVar, $file_tag, 1);
}
elseif (isset($this->mDynamic["$file_tag"]) && !isset($this -> mSeling[$file_tag][index]))
{
$this->ParseDynamic($ReturnVar, $file_tag);
}
else
{
$fileName = $this->mFileList["$file_tag"];
$this->$file_tag = $this->GetTemplate($fileName);
}
}
if($append)
{
if ( isset($this -> mSeling[$file_tag][index]) )
{
($this->mSeling[$file_tag][index] % 2) ?
$macro = $this->mSeling[$file_tag][selang] :
$macro = $this->mSeling[$file_tag][seling];
$this->$ReturnVar .= $this->ParseTemplate($macro,$this->mParseVars);
}
else
{
$this->$ReturnVar .= $this->ParseTemplate($this->$file_tag,$this->mParseVars);
}
}
else
{
$this->$ReturnVar = $this->ParseTemplate($this->$file_tag,$this->mParseVars);
}
// For recursive calls.
$this->assign( array($ReturnVar => $this->$ReturnVar) );
}
return;
} // End Parse
/**
* Parse a dynamic block
*
* @param string $MacroName name of the new var for the replacement of dynamic block
* @param string $Macro name of the parent's tag
* @param string $alt set if alternation mode is used
* @see DefineDynamic()
*/
function ParseDynamic( $MacroName, $Macro, $alt = "" )
{
// The file must already be in memory.
$ParentTag = $this->mDynamic["$Macro"];
if( (!$this->$ParentTag) or (empty($this->$ParentTag)) )
{
$fileName = $this->mFileList[$ParentTag];
$this->$ParentTag = $this->GetTemplate($fileName);
$this->mLoaded[$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
if (!empty($alt))
{
// this is where the alternating does the work ... *ting*
$_newMacro = explode("<!-- ALTERNATING -->", $newMacro);
$this->mSeling[$Macro][selang] = $_newMacro[0];
$this->mSeling[$Macro][seling] = $_newMacro[1];
$this->$Macro = ((($indexSelangSeling % 2) == 0) ? $_newMacro[0] : $_newMacro[1]);
}
else
{
$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;
} // end ParseDynamic
/**
* Print the templates which has been modified.
*
* @param $template
* @access public
*/
function InPrint( $template = "" )
{
if(empty($template))
{
$template = $this->mLast;
}
if( (!(isset($this->$template))) || (empty($this->$template)) )
{
$this->Error("Nothing parsed, nothing printed",0);
return;
}
else
{
if ( !$this->mStrict )
{
// Silently removes any unparsed dynamic blocks
$this->ClearAllDynamic();
// Silently removes any InTemplate Vars that has been resolved
$this->$template = preg_replace("|\{([A-Za-z0-9_]+)}|","",$this->$template);
}
else
{
// Warn about unresolved InTemplate Vars
preg_match_all("/\{([A-Za-z0-9_]+)\}/",$this->$template,$unresolved);
for ($i=0;$i<count($unresolved[0]);$i++)
{
$this->ShowUnknowns($unresolved[1][$i]);
}
}
print $this->$template;
}
return;
} // end InPrint
/**
* Returns the raw data from a parsed handle.
*
* @param string $template name of the templates
* @access public
*/
function Fetch( $template = "" )
{
if(empty($template))
{
$template = $this->mLast;
}
if( (!(isset($this->$template))) || (empty($this->$template)) )
{
$this->Error("Nothing parsed, nothing printed",0);
return "";
}
if ( !$this->mStrict )
{
// Silently removes any unparsed dynamic blocks
$this->ClearAllDynamic();
// Silently removes any InTemplate Vars that has been resolved
$this->$template = preg_replace("|\{([A-Za-z0-9_]+)}|","",$this->$template);
}
return($this->$template);
} // end Fetch
/**
* Strips a DYNAMIC BLOCK from a template.
*
* @param string $Macro
* @return boolean
* @access public
*/
function ClearDynamic( $Macro = "" )
{
if(empty($Macro))
{
return false;
}
else
{
// The file must already be in memory.
$ParentTag = $this->mDynamic["$Macro"];
if( (!$this->$ParentTag) or (empty($this->$ParentTag)) )
{
$fileName = $this->mFileList[$ParentTag];
$this->$ParentTag = $this->GetTemplate($fileName);
$this->mLoaded[$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;
}
} // end ClearDynamic
/**
* Strips all unparsed DYNAMIC BLOCK from the last parsed template.
* Silly ? nope, if you use this routine last before you print the
* final templates.
*
* @access public
*/
function ClearAllDynamic()
{
// strips all dynamic block that hasnt been parsed
$template = $this->mLast;
$this->$template = preg_replace("/<!-- BEGIN DYNAMIC BLOCK: (.+) -->.*\{(.+)\}.*<!-- END DYNAMIC BLOCK: (.+) -->/sm","",$this->$template);
} // end ClearAllDynamic
/**
* Removes a given reference from the list of refs that is built using: $tpl->Assign(KEY = val);
*
* If called with no arguments, it removes all references from the array.
*
* @param string $returnVar clear any parsed variable from Assign()
* @see Assign()
* @see Clear()
* @access public
*/
function ClearParse( $returnVar = "")
{
$this->clear($returnVar);
} // end ClearParse
/**
* General clearance
*
* @param string $returnVar macro, filetag or var
* @see ClearParse()
* @see ClearDefine()
*/
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->mHandle) )
{
$KEY = $key;
unset($this->$KEY);
}
return;
} // end Clear
/**
* Clear all
*
* @access public
*/
function ClearAll()
{
$this->Clear();
$this->clear_assign();
$this->clear_define();
$this->clear_tpl();
return;
} // end ClearAll
/**
* Clear templates
*
* @param $fileHandle
* @access public
*/
function ClearTemplate( $fileHandle = "" )
{
if(empty($this->mLoaded))
{
// Nothing loaded, nothing to clear
return true;
}
if(empty($fileHandle))
{
// Clear ALL fileHandles
while ( list ($key, $val) = each ($this->mLoaded) )
{
unset($this->$key);
}
unset($this->mLoaded);
return true;
}
else
{
if( (gettype($fileHandle)) != "array")
{
if( (isset($this->$fileHandle)) || (!empty($this->$fileHandle)) )
{
unset($this->mLoaded[$fileHandle]);
unset($this->$fileHandle);
return true;
}
}
else
{
while ( list ($Key, $Val) = each ($fileHandle) )
{
unset($this->mLoaded[$Key]);
unset($this->$Key);
}
return true;
}
}
return false;
} // end ClearTemplate
/**
* Clears the internal list that stores data passed to $tpl->define();
*
* @param string $fileTag file tag
* @access public
*/
function ClearDefine( $fileTag = "" )
{
if(empty($fileTag))
{
unset($this->mFileList);
return;
}
if( (gettype($Files)) != "array")
{
unset($this->mFileList[$fileTag]);
return;
}
else
{
while ( list ( $Tag, $Val) = each ($fileTag) )
{
unset($this->mFileList[$Tag]);
}
return;
}
} // end ClearDefine
/**
* Clears all variables set by assign()
*
* @access public
*/
function ClearAssign()
{
if(!(empty($this->mParseVars)))
{
while(list($Ref,$Val) = each ($this->mParseVars) )
{
unset($this->mParseVars["$Ref"]);
}
}
} // end ClearAssign
/**
* Clears all href
*
* @param string $href href
* @class public
*/
function ClearHref( $href )
{
if(!empty($href))
{
if( (gettype($href)) != "array")
{
unset($this->mParseVars[$href]);
return;
}
else
{
while (list ($Ref,$val) = each ($href) )
{
unset($this->mParseVars[$Ref]);
}
return;
}
}
else
{
// Empty - clear them all
$this->clear_assign();
}
return;
} // end ClearHref
/**
* Prints the warnings for unresolved InTemplate variable.
* Used if mStrict is set true
*
* @param string $unkVar the unknownvars
* @access public
*/
function ShowUnknowns( $unkVar )
{
@error_log("[InTemplate] Warning: no value found for variable: $unkVar ",0);
} // end ShowUnknowns
/**
* Prints any error message.
*
* @param string $errorMsg Error messages
* @param boolean $die whether exit or not after prints
*/
function Error( $errorMsg, $die = 0 )
{
$this->mError = $errorMsg;
echo "ERROR: $this->mError <BR> \n";
if ($die == 1)
{
exit;
}
return;
} // end Error
/**
* 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.
*
* @param string $root root directory
*/
function utime ()
{
$time = explode( " ", microtime());
$usec = (double)$time[0];
$sec = (double)$time[1];
return $sec + $usec;
} // end utime
/**
* 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.
*
* @access public
*/
function Strict ()
{
$this->mStrict = true;
} // end Strict
/**
* Silently discards (removes) undefined variable references
* found in templates
*
* @access public
*/
function NoStrict()
{
$this->mStrict = false;
} // end NoStrict
/**
* Return the value of an assigned variable.
*
* @param string $tpl_name template name
* @return boolean
* @access public
*/
function GetAssigned($tpl_name = "")
{
if(empty($tpl_name)) { return false; }
if(isset($this->mParseVars["$tpl_name"]))
{
return ($this->mParseVars["$tpl_name"]);
}
else
{
return false;
}
} // end GetAssigned
/**
* Handle any included file using include()
* I'm sure there are some other better ways to do this
*
* @param string $Macro current macro
* @param string $includeFile included file
* @param array $passedVars passed vars
* @access public
*
*/
function AssignInclude ( $Macro, $includeFile, $passedVars = '' )
{
global $HTTP_POST_VARS, $HTTP_GET_VARS;
ob_start();
if ( is_array($passedVars) )
{
foreach( $passedVars as $k=>$v )
{
$$k = $v;
}
}
include $includeFile;
$fp=ob_get_contents();
ob_end_clean();
$this->assign ( $Macro, $fp );
} // end AssignInclude
} // End class InTemplate
?> |