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