--- /dev/null
+<?php\r
+/**\r
+* File MiniTemplator.class.php\r
+* @package MiniTemplator\r
+*/\r
+\r
+/**\r
+* A compact template engine for HTML files.\r
+*\r
+* Requires PHP 4.0.4 or newer.\r
+*\r
+* <pre>\r
+* Template syntax:\r
+*\r
+* Variables:\r
+* ${VariableName}\r
+*\r
+* Blocks:\r
+* <!-- $BeginBlock BlockName -->\r
+* ... block content ...\r
+* <!-- $EndBlock BlockName -->\r
+*\r
+* Include a subtemplate:\r
+* <!-- $Include RelativeFileName -->\r
+* </pre>\r
+*\r
+* <pre>\r
+* General remarks:\r
+* - Variable names and block names are case-insensitive.\r
+* - The same variable may be used multiple times within a template.\r
+* - Blocks can be nested.\r
+* - Multiple blocks with the same name may occur within a template.\r
+* </pre>\r
+*\r
+* <pre>\r
+* Public methods:\r
+* readTemplateFromFile - Reads the template from a file.\r
+* setTemplateString - Assigns a new template string.\r
+* setVariable - Sets a template variable.\r
+* setVariableEsc - Sets a template variable to an escaped string value.\r
+* variableExists - Checks whether a template variable exists.\r
+* addBlock - Adds an instance of a template block.\r
+* blockExists - Checks whether a block exists.\r
+* reset - Clears all variables and blocks.\r
+* generateOutput - Generates the HTML page and writes it to the PHP output stream.\r
+* generateOutputToFile - Generates the HTML page and writes it to a file.\r
+* generateOutputToString - Generates the HTML page and writes it to a string.\r
+* </pre>\r
+*\r
+* Home page: {@link http://www.source-code.biz/MiniTemplator}<br>\r
+* License: This module is released under the GNU/LGPL license ({@link http://www.gnu.org/licenses/lgpl.html}).<br>\r
+* Copyright 2003: Christian d'Heureuse, Inventec Informatik AG, Switzerland. All rights reserved.<br>\r
+* This product is provided "as is" without warranty of any kind.<br>\r
+*\r
+* Version history:<br>\r
+* 2001-10-24 Christian d'Heureuse (chdh): VBasic version created.<br>\r
+* 2002-01-26 Markus Angst: ported to PHP4.<br>\r
+* 2003-04-07 chdh: changes to adjust to Java version.<br>\r
+* 2003-07-08 chdh: Method variableExists added.\r
+* Method setVariable changed to trigger an error when the variable does not exist.<br>\r
+* 2004-04-07 chdh: Parameter isOptional added to method setVariable.\r
+* Licensing changed from GPL to LGPL.<br>\r
+* 2004-04-18 chdh: Method blockExists added.<br>\r
+* 2004-10-28 chdh:<br>\r
+* Method setVariableEsc added.<br>\r
+* Multiple blocks with the same name may now occur within a template.<br>\r
+* No error ("unknown command") is generated any more, if a HTML comment starts with "${".<br>\r
+* 2004-11-06 chdh:<br>\r
+* "$Include" command implemented.<br>\r
+* 2004-11-20 chdh:<br>\r
+* "$Include" command changed so that the command text is not copied to the output file.<br>\r
+*/\r
+\r
+class MiniTemplator {\r
+\r
+//--- public member variables ---------------------------------------------------------------------------------------\r
+\r
+/**\r
+* Base path for relative file names of subtemplates (for the $Include command).\r
+* This path is prepended to the subtemplate file names. It must be set before\r
+* readTemplateFromFile or setTemplateString.\r
+* @access public\r
+*/\r
+var $subtemplateBasePath;\r
+\r
+//--- private member variables --------------------------------------------------------------------------------------\r
+\r
+/**#@+\r
+* @access private\r
+*/\r
+\r
+var $maxNestingLevel = 50; // maximum number of block nestings\r
+var $maxInclTemplateSize = 1000000; // maximum length of template string when including subtemplates\r
+var $template; // Template file data\r
+var $varTab; // variables table, array index is variable no\r
+ // Fields:\r
+ // varName // variable name\r
+ // varValue // variable value\r
+var $varTabCnt; // no of entries used in VarTab\r
+var $varNameToNoMap; // maps variable names to variable numbers\r
+var $varRefTab; // variable references table\r
+ // Contains an entry for each variable reference in the template. Ordered by TemplatePos.\r
+ // Fields:\r
+ // varNo // variable no\r
+ // tPosBegin // template position of begin of variable reference\r
+ // tPosEnd // template position of end of variable reference\r
+ // blockNo // block no of the (innermost) block that contains this variable reference\r
+ // blockVarNo // block variable no. Index into BlockInstTab.BlockVarTab\r
+var $varRefTabCnt; // no of entries used in VarRefTab\r
+var $blockTab; // Blocks table, array index is block no\r
+ // Contains an entry for each block in the template. Ordered by TPosBegin.\r
+ // Fields:\r
+ // blockName // block name\r
+ // nextWithSameName; // block no of next block with same name or -1 (blocks are backward linked in relation to template position)\r
+ // tPosBegin // template position of begin of block\r
+ // tPosContentsBegin // template pos of begin of block contents\r
+ // tPosContentsEnd // template pos of end of block contents\r
+ // tPosEnd // template position of end of block\r
+ // nestingLevel // block nesting level\r
+ // parentBlockNo // block no of parent block\r
+ // definitionIsOpen // true while $BeginBlock processed but no $EndBlock\r
+ // instances // number of instances of this block\r
+ // firstBlockInstNo // block instance no of first instance of this block or -1\r
+ // lastBlockInstNo // block instance no of last instance of this block or -1\r
+ // currBlockInstNo // current block instance no, used during generation of output file\r
+ // blockVarCnt // no of variables in block\r
+ // blockVarNoToVarNoMap // maps block variable numbers to variable numbers\r
+ // firstVarRefNo // variable reference no of first variable of this block or -1\r
+var $blockTabCnt; // no of entries used in BlockTab\r
+var $blockNameToNoMap; // maps block names to block numbers\r
+var $openBlocksTab;\r
+ // During parsing, this table contains the block numbers of the open parent blocks (nested outer blocks).\r
+ // Indexed by the block nesting level.\r
+var $blockInstTab; // block instances table\r
+ // This table contains an entry for each block instance that has been added.\r
+ // Indexed by BlockInstNo.\r
+ // Fields:\r
+ // blockNo // block number\r
+ // instanceLevel // instance level of this block\r
+ // InstanceLevel is an instance counter per block.\r
+ // (In contrast to blockInstNo, which is an instance counter over the instances of all blocks)\r
+ // parentInstLevel // instance level of parent block\r
+ // nextBlockInstNo // pointer to next instance of this block or -1\r
+ // Forward chain for instances of same block.\r
+ // blockVarTab // block instance variables\r
+var $blockInstTabCnt; // no of entries used in BlockInstTab\r
+\r
+var $currentNestingLevel; // Current block nesting level during parsing.\r
+var $templateValid; // true if a valid template is prepared\r
+var $outputMode; // 0 = to PHP output stream, 1 = to file, 2 = to string\r
+var $outputFileHandle; // file handle during writing of output file\r
+var $outputError; // true when an output error occurred\r
+var $outputString; // string buffer for the generated HTML page\r
+\r
+/**#@-*/\r
+\r
+//--- constructor ---------------------------------------------------------------------------------------------------\r
+\r
+/**\r
+* Constructs a MiniTemplator object.\r
+* @access public\r
+*/\r
+function MiniTemplator() {\r
+ $this->templateValid = false; }\r
+\r
+//--- template string handling --------------------------------------------------------------------------------------\r
+\r
+/**\r
+* Reads the template from a file.\r
+* @param string $fileName name of the file that contains the template.\r
+* @return boolean true on success, false on error.\r
+* @access public\r
+*/\r
+function readTemplateFromFile ($fileName) {\r
+ if (!$this->readFileIntoString($fileName,$s)) {\r
+ $this->triggerError ("Error while reading template file " . $fileName . ".");\r
+ return false; }\r
+ if (!$this->setTemplateString($s)) return false;\r
+ return true; }\r
+\r
+/**\r
+* Assigns a new template string.\r
+* @param string $templateString contents of the template file.\r
+* @return boolean true on success, false on error.\r
+* @access public\r
+*/\r
+function setTemplateString ($templateString) {\r
+ $this->templateValid = false;\r
+ $this->template = $templateString;\r
+ if (!$this->parseTemplate()) return false;\r
+ $this->reset();\r
+ $this->templateValid = true;\r
+ return true; }\r
+\r
+/**\r
+* Loads the template string for a subtemplate (used for the $Include command).\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function loadSubtemplate ($subtemplateName, &$s) {\r
+ $subtemplateFileName = $this->combineFileSystemPath($this->subtemplateBasePath,$subtemplateName);\r
+ if (!$this->readFileIntoString($subtemplateFileName,$s)) {\r
+ $this->triggerError ("Error while reading subtemplate file " . $subtemplateFileName . ".");\r
+ return false; }\r
+ return true; }\r
+\r
+//--- template parsing ----------------------------------------------------------------------------------------------\r
+\r
+/**\r
+* Parses the template.\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function parseTemplate() {\r
+ $this->initParsing();\r
+ $this->beginMainBlock();\r
+ if (!$this->parseTemplateCommands()) return false;\r
+ $this->endMainBlock();\r
+ if (!$this->checkBlockDefinitionsComplete()) return false;\r
+ if (!$this->parseTemplateVariables()) return false;\r
+ $this->associateVariablesWithBlocks();\r
+ return true; }\r
+\r
+/**\r
+* @access private\r
+*/\r
+function initParsing() {\r
+ $this->varTab = array();\r
+ $this->varTabCnt = 0;\r
+ $this->varNameToNoMap = array();\r
+ $this->varRefTab = array();\r
+ $this->varRefTabCnt = 0;\r
+ $this->blockTab = array();\r
+ $this->blockTabCnt = 0;\r
+ $this->blockNameToNoMap = array();\r
+ $this->openBlocksTab = array(); }\r
+\r
+/**\r
+* Registers the main block.\r
+* The main block is an implicitly defined block that covers the whole template.\r
+* @access private\r
+*/\r
+function beginMainBlock() {\r
+ $blockNo = 0;\r
+ $this->registerBlock('@@InternalMainBlock@@', $blockNo);\r
+ $bte =& $this->blockTab[$blockNo];\r
+ $bte['tPosBegin'] = 0;\r
+ $bte['tPosContentsBegin'] = 0;\r
+ $bte['nestingLevel'] = 0;\r
+ $bte['parentBlockNo'] = -1;\r
+ $bte['definitionIsOpen'] = true;\r
+ $this->openBlocksTab[0] = $blockNo;\r
+ $this->currentNestingLevel = 1; }\r
+\r
+/**\r
+* Completes the main block registration.\r
+* @access private\r
+*/\r
+function endMainBlock() {\r
+ $bte =& $this->blockTab[0];\r
+ $bte['tPosContentsEnd'] = strlen($this->template);\r
+ $bte['tPosEnd'] = strlen($this->template);\r
+ $bte['definitionIsOpen'] = false;\r
+ $this->currentNestingLevel -= 1; }\r
+\r
+/**\r
+* Parses commands within the template in the format "<!-- $command parameters -->".\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function parseTemplateCommands() {\r
+ $p = 0;\r
+ while (true) {\r
+ $p0 = strpos($this->template,'<!--',$p);\r
+ if ($p0 === false) break;\r
+ $p = strpos($this->template,'-->',$p0);\r
+ if ($p === false) {\r
+ $this->triggerError ("Invalid HTML comment in template at offset $p0.");\r
+ return false; }\r
+ $p += 3;\r
+ $cmdL = substr($this->template,$p0+4,$p-$p0-7);\r
+ if (!$this->processTemplateCommand($cmdL,$p0,$p,$resumeFromStart))\r
+ return false;\r
+ if ($resumeFromStart) $p = $p0; }\r
+ return true; }\r
+\r
+/**\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function processTemplateCommand ($cmdL, $cmdTPosBegin, $cmdTPosEnd, &$resumeFromStart) {\r
+ $resumeFromStart = false;\r
+ $p = 0;\r
+ $cmd = '';\r
+ if (!$this->parseWord($cmdL,$p,$cmd)) return true;\r
+ $parms = substr($cmdL,$p);\r
+ switch (strtoupper($cmd)) {\r
+ case '$BEGINBLOCK':\r
+ if (!$this->processBeginBlockCmd($parms,$cmdTPosBegin,$cmdTPosEnd))\r
+ return false;\r
+ break;\r
+ case '$ENDBLOCK':\r
+ if (!$this->processEndBlockCmd($parms,$cmdTPosBegin,$cmdTPosEnd))\r
+ return false;\r
+ break;\r
+ case '$INCLUDE':\r
+ if (!$this->processincludeCmd($parms,$cmdTPosBegin,$cmdTPosEnd))\r
+ return false;\r
+ $resumeFromStart = true;\r
+ break;\r
+ default:\r
+ if ($cmd{0} == '$' && !(strlen($cmd) >= 2 && $cmd{1} == '{')) {\r
+ $this->triggerError ("Unknown command \"$cmd\" in template at offset $cmdTPosBegin.");\r
+ return false; }}\r
+ return true; }\r
+\r
+/**\r
+* Processes the $BeginBlock command.\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function processBeginBlockCmd ($parms, $cmdTPosBegin, $cmdTPosEnd) {\r
+ $p = 0;\r
+ if (!$this->parseWord($parms,$p,$blockName)) {\r
+ $this->triggerError ("Missing block name in \$BeginBlock command in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ if (trim(substr($parms,$p)) != '') {\r
+ $this->triggerError ("Extra parameter in \$BeginBlock command in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ $this->registerBlock ($blockName, $blockNo);\r
+ $btr =& $this->blockTab[$blockNo];\r
+ $btr['tPosBegin'] = $cmdTPosBegin;\r
+ $btr['tPosContentsBegin'] = $cmdTPosEnd;\r
+ $btr['nestingLevel'] = $this->currentNestingLevel;\r
+ $btr['parentBlockNo'] = $this->openBlocksTab[$this->currentNestingLevel-1];\r
+ $this->openBlocksTab[$this->currentNestingLevel] = $blockNo;\r
+ $this->currentNestingLevel += 1;\r
+ if ($this->currentNestingLevel > $this->maxNestingLevel) {\r
+ $trhis->triggerError ("Block nesting overflow in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ return true; }\r
+\r
+/**\r
+* Processes the $EndBlock command.\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function processEndBlockCmd ($parms, $cmdTPosBegin, $cmdTPosEnd) {\r
+ $p = 0;\r
+ if (!$this->parseWord($parms,$p,$blockName)) {\r
+ $this->triggerError ("Missing block name in \$EndBlock command in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ if (trim(substr($parms,$p)) != '') {\r
+ $this->triggerError ("Extra parameter in \$EndBlock command in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ if (!$this->lookupBlockName($blockName,$blockNo)) {\r
+ $this->triggerError ("Undefined block name \"$blockName\" in \$EndBlock command in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ $this->currentNestingLevel -= 1;\r
+ $btr =& $this->blockTab[$blockNo];\r
+ if (!$btr['definitionIsOpen']) {\r
+ $this->triggerError ("Multiple \$EndBlock command for block \"$blockName\" in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ if ($btr['nestingLevel'] != $this->currentNestingLevel) {\r
+ $this->triggerError ("Block nesting level mismatch at \$EndBlock command for block \"$blockName\" in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ $btr['tPosContentsEnd'] = $cmdTPosBegin;\r
+ $btr['tPosEnd'] = $cmdTPosEnd;\r
+ $btr['definitionIsOpen'] = false;\r
+ return true; }\r
+\r
+/**\r
+* @access private\r
+*/\r
+function registerBlock($blockName, &$blockNo) {\r
+ $blockNo = $this->blockTabCnt++;\r
+ $btr =& $this->blockTab[$blockNo];\r
+ $btr = array();\r
+ $btr['blockName'] = $blockName;\r
+ if (!$this->lookupBlockName($blockName,$btr['nextWithSameName']))\r
+ $btr['nextWithSameName'] = -1;\r
+ $btr['definitionIsOpen'] = true;\r
+ $btr['instances'] = 0;\r
+ $btr['firstBlockInstNo'] = -1;\r
+ $btr['lastBlockInstNo'] = -1;\r
+ $btr['blockVarCnt'] = 0;\r
+ $btr['firstVarRefNo'] = -1;\r
+ $btr['blockVarNoToVarNoMap'] = array();\r
+ $this->blockNameToNoMap[strtoupper($blockName)] = $blockNo; }\r
+\r
+/**\r
+* Checks that all block definitions are closed.\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function checkBlockDefinitionsComplete() {\r
+ for ($blockNo=0; $blockNo < $this->blockTabCnt; $blockNo++) {\r
+ $btr =& $this->blockTab[$blockNo];\r
+ if ($btr['definitionIsOpen']) {\r
+ $this->triggerError ("Missing \$EndBlock command in template for block " . $btr['blockName'] . ".");\r
+ return false; }}\r
+ if ($this->currentNestingLevel != 0) {\r
+ $this->triggerError ("Block nesting level error at end of template.");\r
+ return false; }\r
+ return true; }\r
+\r
+/**\r
+* Processes the $Include command.\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function processIncludeCmd ($parms, $cmdTPosBegin, $cmdTPosEnd) {\r
+ $p = 0;\r
+ if (!$this->parseWordOrQuotedString($parms,$p,$subtemplateName)) {\r
+ $this->triggerError ("Missing or invalid subtemplate name in \$Include command in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ if (trim(substr($parms,$p)) != '') {\r
+ $this->triggerError ("Extra parameter in \$include command in template at offset $cmdTPosBegin.");\r
+ return false; }\r
+ return $this->insertSubtemplate($subtemplateName,$cmdTPosBegin,$cmdTPosEnd); }\r
+\r
+/**\r
+* Processes the $Include command.\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function insertSubtemplate ($subtemplateName, $tPos1, $tPos2) {\r
+ if (strlen($this->template) > $this->maxInclTemplateSize) {\r
+ $this->triggerError ("Subtemplate include aborted because the internal template string is longer than $this->maxInclTemplateSize characters.");\r
+ return false; }\r
+ if (!$this->loadSubtemplate($subtemplateName,$subtemplate)) return false;\r
+ // (Copying the template to insert a subtemplate is a bit slow. In a future implementation of MiniTemplator,\r
+ // a table could be used that contains references to the string fragments.)\r
+ $this->template = substr($this->template,0,$tPos1) . $subtemplate . substr($this->template,$tPos2);\r
+ return true; }\r
+\r
+/**\r
+* Parses variable references within the template in the format "${VarName}".\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function parseTemplateVariables() {\r
+ $p = 0;\r
+ while (true) {\r
+ $p = strpos($this->template, '${', $p);\r
+ if ($p === false) break;\r
+ $p0 = $p;\r
+ $p = strpos($this->template, '}', $p);\r
+ if ($p === false) {\r
+ $this->triggerError ("Invalid variable reference in template at offset $p0.");\r
+ return false; }\r
+ $p += 1;\r
+ $varName = trim(substr($this->template, $p0+2, $p-$p0-3));\r
+ if (strlen($varName) == 0) {\r
+ $this->triggerError ("Empty variable name in template at offset $p0.");\r
+ return false; }\r
+ $this->registerVariableReference ($varName, $p0, $p); }\r
+ return true; }\r
+\r
+/**\r
+* @access private\r
+*/\r
+function registerVariableReference ($varName, $tPosBegin, $tPosEnd) {\r
+ if (!$this->lookupVariableName($varName,$varNo))\r
+ $this->registerVariable($varName,$varNo);\r
+ $varRefNo = $this->varRefTabCnt++;\r
+ $vrtr =& $this->varRefTab[$varRefNo];\r
+ $vrtr = array();\r
+ $vrtr['tPosBegin'] = $tPosBegin;\r
+ $vrtr['tPosEnd'] = $tPosEnd;\r
+ $vrtr['varNo'] = $varNo; }\r
+\r
+/**\r
+* @access private\r
+*/\r
+function registerVariable ($varName, &$varNo) {\r
+ $varNo = $this->varTabCnt++;\r
+ $vtr =& $this->varTab[$varNo];\r
+ $vtr = array();\r
+ $vtr['varName'] = $varName;\r
+ $vtr['varValue'] = '';\r
+ $this->varNameToNoMap[strtoupper($varName)] = $varNo; }\r
+\r
+/**\r
+* Associates variable references with blocks.\r
+* @access private\r
+*/\r
+function associateVariablesWithBlocks() {\r
+ $varRefNo = 0;\r
+ $activeBlockNo = 0;\r
+ $nextBlockNo = 1;\r
+ while ($varRefNo < $this->varRefTabCnt) {\r
+ $vrtr =& $this->varRefTab[$varRefNo];\r
+ $varRefTPos = $vrtr['tPosBegin'];\r
+ $varNo = $vrtr['varNo'];\r
+ if ($varRefTPos >= $this->blockTab[$activeBlockNo]['tPosEnd']) {\r
+ $activeBlockNo = $this->blockTab[$activeBlockNo]['parentBlockNo'];\r
+ continue; }\r
+ if ($nextBlockNo < $this->blockTabCnt) {\r
+ if ($varRefTPos >= $this->blockTab[$nextBlockNo]['tPosBegin']) {\r
+ $activeBlockNo = $nextBlockNo;\r
+ $nextBlockNo += 1;\r
+ continue; }}\r
+ $btr =& $this->blockTab[$activeBlockNo];\r
+ if ($varRefTPos < $btr['tPosBegin'])\r
+ $this->programLogicError(1);\r
+ $blockVarNo = $btr['blockVarCnt']++;\r
+ $btr['blockVarNoToVarNoMap'][$blockVarNo] = $varNo;\r
+ if ($btr['firstVarRefNo'] == -1)\r
+ $btr['firstVarRefNo'] = $varRefNo;\r
+ $vrtr['blockNo'] = $activeBlockNo;\r
+ $vrtr['blockVarNo'] = $blockVarNo;\r
+ $varRefNo += 1; }}\r
+\r
+//--- build up (template variables and blocks) ----------------------------------------------------------------------\r
+\r
+/**\r
+* Clears all variables and blocks.\r
+* This method can be used to produce another HTML page with the same\r
+* template. It is faster than creating another MiniTemplator object,\r
+* because the template does not have to be parsed again.\r
+* All variable values are cleared and all added block instances are deleted.\r
+* @access public\r
+*/\r
+function reset() {\r
+ for ($varNo=0; $varNo<$this->varTabCnt; $varNo++)\r
+ $this->varTab[$varNo]['varValue'] = '';\r
+ for ($blockNo=0; $blockNo<$this->blockTabCnt; $blockNo++) {\r
+ $btr =& $this->blockTab[$blockNo];\r
+ $btr['instances'] = 0;\r
+ $btr['firstBlockInstNo'] = -1;\r
+ $btr['lastBlockInstNo'] = -1; }\r
+ $this->blockInstTab = array();\r
+ $this->blockInstTabCnt = 0; }\r
+\r
+/**\r
+* Sets a template variable.\r
+* For variables that are used in blocks, the variable value\r
+* must be set before {@link addBlock} is called.\r
+* @param string $variableName the name of the variable to be set.\r
+* @param string $variableValue the new value of the variable.\r
+* @param boolean $isOptional Specifies whether an error should be\r
+* generated when the variable does not exist in the template. If\r
+* $isOptional is false and the variable does not exist, an error is\r
+* generated.\r
+* @return boolean true on success, or false on error (e.g. when no\r
+* variable with the specified name exists in the template and\r
+* $isOptional is false).\r
+* @access public\r
+*/\r
+function setVariable ($variableName, $variableValue, $isOptional=false) {\r
+ if (!$this->templateValid) {$this->triggerError ("Template not valid."); return false; }\r
+ if (!$this->lookupVariableName($variableName,$varNo)) {\r
+ if ($isOptional) return true;\r
+ $this->triggerError ("Variable \"$variableName\" not defined in template.");\r
+ return false; }\r
+ $this->varTab[$varNo]['varValue'] = $variableValue;\r
+ return true; }\r
+\r
+/**\r
+* Sets a template variable to an escaped string.\r
+* This method is identical to (@link setVariable), except that\r
+* the characters <, >, &, ' and " of variableValue are\r
+* replaced by their corresponding HTML/XML character entity codes.\r
+* For variables that are used in blocks, the variable value\r
+* must be set before {@link addBlock} is called.\r
+* @param string $variableName the name of the variable to be set.\r
+* @param string $variableValue the new value of the variable. Special HTML/XML characters are escaped.\r
+* @param boolean $isOptional Specifies whether an error should be\r
+* generated when the variable does not exist in the template. If\r
+* $isOptional is false and the variable does not exist, an error is\r
+* generated.\r
+* @return boolean true on success, or false on error (e.g. when no\r
+* variable with the specified name exists in the template and\r
+* $isOptional is false).\r
+* @access public\r
+*/\r
+function setVariableEsc ($variableName, $variableValue, $isOptional=false) {\r
+ return $this->setVariable($variableName,htmlspecialchars($variableValue,ENT_QUOTES),$isOptional); }\r
+\r
+/**\r
+* Checks whether a variable with the specified name exists within the template.\r
+* @param string $variableName the name of the variable.\r
+* @return boolean true if the variable exists, or false when no\r
+* variable with the specified name exists in the template.\r
+* @access public\r
+*/\r
+function variableExists ($variableName) {\r
+ if (!$this->templateValid) {$this->triggerError ("Template not valid."); return false; }\r
+ return $this->lookupVariableName($variableName,$varNo); }\r
+\r
+/**\r
+* Adds an instance of a template block.\r
+* If the block contains variables, these variables must be set\r
+* before the block is added.\r
+* If the block contains subblocks (nested blocks), the subblocks\r
+* must be added before this block is added.\r
+* If multiple blocks exist with the specified name, an instance\r
+* is added for each block occurence.\r
+* @param string blockName the name of the block to be added.\r
+* @return boolean true on success, false on error (e.g. when no\r
+* block with the specified name exists in the template).\r
+* @access public\r
+*/\r
+function addBlock($blockName) {\r
+ if (!$this->templateValid) {$this->triggerError ("Template not valid."); return false; }\r
+ if (!$this->lookupBlockName($blockName,$blockNo)) {\r
+ $this->triggerError ("Block \"$blockName\" not defined in template.");\r
+ return false; }\r
+ while ($blockNo != -1) {\r
+ $this->addBlockByNo($blockNo);\r
+ $blockNo = $this->blockTab[$blockNo]['nextWithSameName']; }\r
+ return true; }\r
+\r
+/**\r
+* @access private\r
+*/\r
+function addBlockByNo ($blockNo) {\r
+ $btr =& $this->blockTab[$blockNo];\r
+ $this->registerBlockInstance ($blockInstNo);\r
+ $bitr =& $this->blockInstTab[$blockInstNo];\r
+ if ($btr['firstBlockInstNo'] == -1)\r
+ $btr['firstBlockInstNo'] = $blockInstNo;\r
+ if ($btr['lastBlockInstNo'] != -1)\r
+ $this->blockInstTab[$btr['lastBlockInstNo']]['nextBlockInstNo'] = $blockInstNo;\r
+ // set forward pointer of chain\r
+ $btr['lastBlockInstNo'] = $blockInstNo;\r
+ $parentBlockNo = $btr['parentBlockNo'];\r
+ $blockVarCnt = $btr['blockVarCnt'];\r
+ $bitr['blockNo'] = $blockNo;\r
+ $bitr['instanceLevel'] = $btr['instances']++;\r
+ if ($parentBlockNo == -1)\r
+ $bitr['parentInstLevel'] = -1;\r
+ else\r
+ $bitr['parentInstLevel'] = $this->blockTab[$parentBlockNo]['instances'];\r
+ $bitr['nextBlockInstNo'] = -1;\r
+ $bitr['blockVarTab'] = array();\r
+ // copy instance variables for this block\r
+ for ($blockVarNo=0; $blockVarNo<$blockVarCnt; $blockVarNo++) {\r
+ $varNo = $btr['blockVarNoToVarNoMap'][$blockVarNo];\r
+ $bitr['blockVarTab'][$blockVarNo] = $this->varTab[$varNo]['varValue']; }}\r
+\r
+/**\r
+* @access private\r
+*/\r
+function registerBlockInstance (&$blockInstNo) {\r
+ $blockInstNo = $this->blockInstTabCnt++; }\r
+\r
+/**\r
+* Checks whether a block with the specified name exists within the template.\r
+* @param string $blockName the name of the block.\r
+* @return boolean true if the block exists, or false when no\r
+* block with the specified name exists in the template.\r
+* @access public\r
+*/\r
+function blockExists ($blockName) {\r
+ if (!$this->templateValid) {$this->triggerError ("Template not valid."); return false; }\r
+ return $this->lookupBlockName($blockName,$blockNo); }\r
+\r
+//--- output generation ---------------------------------------------------------------------------------------------\r
+\r
+/**\r
+* Generates the HTML page and writes it to the PHP output stream.\r
+* @return boolean true on success, false on error.\r
+* @access public\r
+*/\r
+function generateOutput () {\r
+ $this->outputMode = 0;\r
+ if (!$this->generateOutputPage()) return false;\r
+ return true; }\r
+\r
+/**\r
+* Generates the HTML page and writes it to a file.\r
+* @param string $fileName name of the output file.\r
+* @return boolean true on success, false on error.\r
+* @access public\r
+*/\r
+function generateOutputToFile ($fileName) {\r
+ $fh = fopen($fileName,"wb");\r
+ if ($fh === false) return false;\r
+ $this->outputMode = 1;\r
+ $this->outputFileHandle = $fh;\r
+ $ok = $this->generateOutputPage();\r
+ fclose ($fh);\r
+ return $ok; }\r
+\r
+/**\r
+* Generates the HTML page and writes it to a string.\r
+* @param string $outputString variable that receives\r
+* the contents of the generated HTML page.\r
+* @return boolean true on success, false on error.\r
+* @access public\r
+*/\r
+function generateOutputToString (&$outputString) {\r
+ $outputString = "Error";\r
+ $this->outputMode = 2;\r
+ $this->outputString = "";\r
+ if (!$this->generateOutputPage()) return false;\r
+ $outputString = $this->outputString;\r
+ return true; }\r
+\r
+/**\r
+* @access private\r
+* @return boolean true on success, false on error.\r
+*/\r
+function generateOutputPage() {\r
+ if (!$this->templateValid) {$this->triggerError ("Template not valid."); return false; }\r
+ if ($this->blockTab[0]['instances'] == 0)\r
+ $this->addBlockByNo (0); // add main block\r
+ for ($blockNo=0; $blockNo < $this->blockTabCnt; $blockNo++) {\r
+ $btr =& $this->blockTab[$blockNo];\r
+ $btr['currBlockInstNo'] = $btr['firstBlockInstNo']; }\r
+ $this->outputError = false;\r
+ $this->writeBlockInstances (0, -1);\r
+ if ($this->outputError) return false;\r
+ return true; }\r
+\r
+/**\r
+* Writes all instances of a block that are contained within a specific\r
+* parent block instance.\r
+* Called recursively.\r
+* @access private\r
+*/\r
+function writeBlockInstances ($blockNo, $parentInstLevel) {\r
+ $btr =& $this->blockTab[$blockNo];\r
+ while (!$this->outputError) {\r
+ $blockInstNo = $btr['currBlockInstNo'];\r
+ if ($blockInstNo == -1) break;\r
+ $bitr =& $this->blockInstTab[$blockInstNo];\r
+ if ($bitr['parentInstLevel'] < $parentInstLevel)\r
+ $this->programLogicError (2);\r
+ if ($bitr['parentInstLevel'] > $parentInstLevel) break;\r
+ $this->writeBlockInstance ($blockInstNo);\r
+ $btr['currBlockInstNo'] = $bitr['nextBlockInstNo']; }}\r
+\r
+/**\r
+* @access private\r
+*/\r
+function writeBlockInstance($blockInstNo) {\r
+ $bitr =& $this->blockInstTab[$blockInstNo];\r
+ $blockNo = $bitr['blockNo'];\r
+ $btr =& $this->blockTab[$blockNo];\r
+ $tPos = $btr['tPosContentsBegin'];\r
+ $subBlockNo = $blockNo + 1;\r
+ $varRefNo = $btr['firstVarRefNo'];\r
+ while (!$this->outputError) {\r
+ $tPos2 = $btr['tPosContentsEnd'];\r
+ $kind = 0; // assume end-of-block\r
+ if ($varRefNo != -1 && $varRefNo < $this->varRefTabCnt) { // check for variable reference\r
+ $vrtr =& $this->varRefTab[$varRefNo];\r
+ if ($vrtr['tPosBegin'] < $tPos) {\r
+ $varRefNo += 1;\r
+ continue; }\r
+ if ($vrtr['tPosBegin'] < $tPos2) {\r
+ $tPos2 = $vrtr['tPosBegin'];\r
+ $kind = 1; }}\r
+ if ($subBlockNo < $this->blockTabCnt) { // check for subblock\r
+ $subBtr =& $this->blockTab[$subBlockNo];\r
+ if ($subBtr['tPosBegin'] < $tPos) {\r
+ $subBlockNo += 1;\r
+ continue; }\r
+ if ($subBtr['tPosBegin'] < $tPos2) {\r
+ $tPos2 = $subBtr['tPosBegin'];\r
+ $kind = 2; }}\r
+ if ($tPos2 > $tPos)\r
+ $this->writeString (substr($this->template,$tPos,$tPos2-$tPos));\r
+ switch ($kind) {\r
+ case 0: // end of block\r
+ return;\r
+ case 1: // variable\r
+ $vrtr =& $this->varRefTab[$varRefNo];\r
+ if ($vrtr['blockNo'] != $blockNo)\r
+ $this->programLogicError (4);\r
+ $variableValue = $bitr['blockVarTab'][$vrtr['blockVarNo']];\r
+ $this->writeString ($variableValue);\r
+ $tPos = $vrtr['tPosEnd'];\r
+ $varRefNo += 1;\r
+ break;\r
+ case 2: // sub block\r
+ $subBtr =& $this->blockTab[$subBlockNo];\r
+ if ($subBtr['parentBlockNo'] != $blockNo)\r
+ $this->programLogicError (3);\r
+ $this->writeBlockInstances ($subBlockNo, $bitr['instanceLevel']); // recursive call\r
+ $tPos = $subBtr['tPosEnd'];\r
+ $subBlockNo += 1;\r
+ break; }}}\r
+\r
+/**\r
+* @access private\r
+*/\r
+function writeString ($s) {\r
+ if ($this->outputError) return;\r
+ switch ($this->outputMode) {\r
+ case 0: // output to PHP output stream\r
+ if (!print($s))\r
+ $this->outputError = true;\r
+ break;\r
+ case 1: // output to file\r
+ $rc = fwrite($this->outputFileHandle, $s);\r
+ if ($rc === false) $this->outputError = true;\r
+ break;\r
+ case 2: // output to string\r
+ $this->outputString .= $s;\r
+ break; }}\r
+\r
+//--- name lookup routines ------------------------------------------------------------------------------------------\r
+\r
+/**\r
+* Maps variable name to variable number.\r
+* @return boolean true on success, false if the variable is not found.\r
+* @access private\r
+*/\r
+function lookupVariableName ($varName, &$varNo) {\r
+ $x =& $this->varNameToNoMap[strtoupper($varName)];\r
+ if (!isset($x)) return false;\r
+ $varNo = $x;\r
+ return true; }\r
+\r
+/**\r
+* Maps block name to block number.\r
+* If there are multiple blocks with the same name, the block number of the last\r
+* registered block with that name is returned.\r
+* @return boolean true on success, false when the block is not found.\r
+* @access private\r
+*/\r
+function lookupBlockName ($blockName, &$blockNo) {\r
+ $x =& $this->blockNameToNoMap[strtoupper($blockName)];\r
+ if (!isset($x)) return false;\r
+ $blockNo = $x;\r
+ return true; }\r
+\r
+//--- general utility routines -----------------------------------------------------------------------------------------\r
+\r
+/**\r
+* Reads a file into a string.\r
+* @return boolean true on success, false on error.\r
+* @access private\r
+*/\r
+function readFileIntoString ($fileName, &$s) {\r
+ if (function_exists('version_compare') && version_compare(phpversion(),"4.3.0",">=")) {\r
+ $s = file_get_contents($fileName);\r
+ if ($s === false) return false;\r
+ return true; }\r
+ $fh = fopen($fileName,"rb");\r
+ if ($fh === false) return false;\r
+ $fileSize = filesize($fileName);\r
+ if ($fileSize === false) {close ($fh); return false; }\r
+ $s = fread($fh,$fileSize);\r
+ fclose ($fh);\r
+ if (strlen($s) != $fileSize) return false;\r
+ return true; }\r
+\r
+/**\r
+* @access private\r
+* @return boolean true on success, false when the end of the string is reached.\r
+*/\r
+function parseWord ($s, &$p, &$w) {\r
+ $sLen = strlen($s);\r
+ while ($p < $sLen && ord($s{$p}) <= 32) $p++;\r
+ if ($p >= $sLen) return false;\r
+ $p0 = $p;\r
+ while ($p < $sLen && ord($s{$p}) > 32) $p++;\r
+ $w = substr($s, $p0, $p - $p0);\r
+ return true; }\r
+\r
+/**\r
+* @access private\r
+* @return boolean true on success, false on error.\r
+*/\r
+function parseQuotedString ($s, &$p, &$w) {\r
+ $sLen = strlen($s);\r
+ while ($p < $sLen && ord($s{$p}) <= 32) $p++;\r
+ if ($p >= $sLen) return false;\r
+ if (substr($s,$p,1) != '"') return false;\r
+ $p++; $p0 = $p;\r
+ while ($p < $sLen && $s{$p} != '"') $p++;\r
+ if ($p >= $sLen) return false;\r
+ $w = substr($s, $p0, $p - $p0);\r
+ $p++;\r
+ return true; }\r
+\r
+/**\r
+* @access private\r
+* @return boolean true on success, false on error.\r
+*/\r
+function parseWordOrQuotedString ($s, &$p, &$w) {\r
+ $sLen = strlen($s);\r
+ while ($p < $sLen && ord($s{$p}) <= 32) $p++;\r
+ if ($p >= $sLen) return false;\r
+ if (substr($s,$p,1) == '"')\r
+ return $this->parseQuotedString($s,$p,$w);\r
+ else\r
+ return $this->parseWord($s,$p,$w); }\r
+\r
+/**\r
+* Combine two file system paths.\r
+* @access private\r
+*/\r
+function combineFileSystemPath ($path1, $path2) {\r
+ if ($path1 == '' || $path2 == '') return $path2;\r
+ $s = $path1;\r
+ if (substr($s,-1) != '\\' && substr($s,-1) != '/') $s = $s . "/";\r
+ if (substr($path2,0,1) == '\\' || substr($path2,0,1) == '/')\r
+ $s = $s . substr($path2,1);\r
+ else\r
+ $s = $s . $path2;\r
+ return $s; }\r
+\r
+/**\r
+* @access private\r
+*/\r
+function triggerError ($msg) {\r
+ trigger_error ("MiniTemplator error: $msg", E_USER_ERROR); }\r
+\r
+/**\r
+* @access private\r
+*/\r
+function programLogicError ($errorId) {\r
+ die ("MiniTemplator: Program logic error $errorId.\n"); }\r
+\r
+}\r
+?>\r