include/patTemplate.php
<?php
/*
* You may not change or alter any portion of this comment or credits
* of supporting developers from this source code or any supporting source code
* which is considered copyrighted (c) material of the original comment or credit authors.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
/**
* @copyright {@link http://xoops.org/ XOOPS Project}
* @license {@link http://www.fsf.org/copyleft/gpl.html GNU public license}
* @package
* @since
* @author XOOPS Development Team,
* @author GIJ=CHECKMATE (PEAK Corp. http://www.peak.ne.jp/)
*/
if (!class_exists('PatTemplate')) {
/**
* Variable prefix
* @access public
* @const patTEMPLATE_TAG_START
*/
define('patTEMPLATE_TAG_START', '{');
/**
* Variable sufix
* @const patTEMPLATE_TAG_END
* @access public
*/
define('patTEMPLATE_TAG_END', '}');
/**
* Template type Standard
* @const patTEMPLATE_TYPE_STANDARD
*/
define('patTEMPLATE_TYPE_STANDARD', 'STANDARD');
/**
* Template type OddEven
* @const patTEMPLATE_TYPE_ODDEVEN
*/
define('patTEMPLATE_TYPE_ODDEVEN', 'ODDEVEN');
/**
* Template type Condition
* @const patTEMPLATE_TYPE_CONDITION
*/
define('patTEMPLATE_TYPE_CONDITION', 'CONDITION');
/**
* Template type SimpleCondition
* @const patTEMPLATE_TYPE_SIMPLECONDITION
*/
define('patTEMPLATE_TYPE_SIMPLECONDITION', 'SIMPLECONDITION');
/**
* Easy-to-use but powerful template engine
*
* Features include: several templates in one file, automatic repetitions, global variables,
* alternating lists, conditions, and much more
*
* @package PatTemplate
* @access public
* @author Stephan Schmidt <schst@php-tools.de>
*/
class PatTemplate
{
/**
* Constructor
*
* Create new PatTemplate object
* You can choose between two outputs you want tp generate: html (default) or tex (LaTex).
* When "tex" is used the PatTemplate markings used for variables are changed as LaTex makes use of the default PatTemplate markings.
* You can also change the markings later by calling setTags();
*
* @access public
* @param string $type type of output you want to generate.
*/
public function __construct($type = 'html')
{
// Directory, where Templates are stored
$this->basedir = '';
// counter for template iterations
$this->iteration = array();
// Filenames of the templates
$this->filenames = array();
// HTML/Text of unparsed templates
$this->plain_templates = array();
// HTML/Text of parsed templates
$this->parsed_templates = array();
// Amount and names of all templates
$this->cnt_templates = 0;
$this->templates = array();
// These vars will be set for all added Templates
$this->subtemplates = array();
$this->variables = array();
$this->globals = array();
$this->attributes = array();
$this->whitespace = array();
// Does one of the templates contain other templates
$this->uses_dependencies = false;
$this->dependencies = [];
// Set template tags
$this->setType($type);
}
/**
* Set template type
*
* select a predefined template type
*
* @param string $type predefined template type, like "html" or "tex"
* @access public
*/
public function setType($type = '')
{
switch ($type) {
case 'tex':
$this->setTags('<{', '}>');
break;
case 'html':
default:
$this->setTags(patTEMPLATE_TAG_START, patTEMPLATE_TAG_END);
break;
}
}
/**
* Set template tags
*
* Sets the start and end tags of template variables
*
* @param string $start start tag
* @param string $end end tag
* @access public
*/
public function setTags($start = patTEMPLATE_TAG_START, $end = patTEMPLATE_TAG_END)
{
$this->tag_start = $start;
$this->tag_end = $end;
$this->regex_get_all_vars = '/' . $start . '([^a-z{}]+)' . $end . '/';
}
/**
* Set template directory
*
* Sets the directory where the template are stored.
* By default the engine looks in the directory where the original file is stored.
*
* @param string $basedir directory of the templates
* @access public
*/
public function setBasedir($basedir)
{
$this->basedir = $basedir;
}
/**
* Check if a template exists
*
* @param string $name name of the template
* @return bool
* @access public
*/
public function exists($name)
{
$name = strtoupper($name);
for ($i = 0; $i < $this->cnt_templates; ++$i) {
if ($this->templates[$i] == $name) {
return true;
}
}
return false;
}
/**
* Add a template
*
* Adds a plain text/html to the template engine.
* The file has to be in the directory that has been set using setBaseDir
*
* @param string $name name of the template
* @param string $filename filename of the sourcetemplate
* @access public
* @deprecated 2.4 2001/11/05
* @see setBaseDir(), addTemplates()
*/
public function addTemplate($name, $filename)
{
$this->createTemplate($name, array(
'type' => 'file',
'filename' => $filename
));
// Store the filename
$this->filenames[$name] = $filename;
}
/**
* Adds several templates
*
* Adds several templates to the template engine using an associative array.
* Names of the templates are stored in the keys, filenames are the values.
* The templates have to be in the directory set by setBaseDir().
*
* @param array $templates associative Array with name/filename pairs
* @access public
* @deprecated 2.4 2001/11/05
* @see setBaseDir(), addTemplate()
*/
public function addTemplates($templates)
{
// while (list($name, $file) = each($templates)) {
foreach ($templates as $name => $file) {
$this->addTemplate($name, $file);
}
}
/**
* creates a new template
*
* creates all needed variables
*
* @param string $name name of the template
* @param array $source data regarding the source of the template
* @access private
*/
public function createTemplate($name, $source)
{
$name = strtoupper($name);
// Store the name of the template in index table
$this->templates[$this->cnt_templates] = $name;
$this->cnt_templates++;
// Store the source
$this->source[$name] = $source;
// Init vars for the new Templates
// Store all attributes in Array
$this->attributes[$name] = array(
'loop' => 1,
'visibility' => 'visible',
'unusedvars' => 'strip',
'type' => 'STANDARD'
);
$this->iteration[$name] = 0;
// No vars are set for this template
$this->variables[$name] = array();
// No subtemplates have been specified
$this->cnt_subtemplates[$name] = 0;
$this->varsConverted[$name] = false;
}
/**
* Sets the type of the Template
*
* Template types can be STANDARD, CONDITION or ODDEVEN
* The type of the template can also be set using setAttribute()
*
* @param string $template name of the template
* @param string $type type of the template
* @access private
* @see setAttribute()
*/
public function setTemplateType($template, $type)
{
$template = strtoupper($template);
$this->setAttribute($template, 'type', $type);
}
/**
* Sets the conditionvar of a condtion Template
*
* The type of the template has to be condition
*
* @param string $template name of the template
* @param string $conditionvar name of the conditionvariable
* @access private
* @see setTemplateType()
*/
public function setConditionVar($template, $conditionvar)
{
$template = strtoupper($template);
$conditionvar = strtoupper($conditionvar);
$this->conditionvars[$template] = $conditionvar;
}
/**
* Sets an attribute of a template
*
* supported attributes: visibilty, loop, parse, unusedvars
*
* @param string $template name of the template
* @param string $attribute name of the attribute
* @param mixed $value value of the attribute
* @access public
* @see setAttributes(),getAttribute(), clearAttribute()
*/
public function setAttribute($template, $attribute, $value)
{
$template = strtoupper($template);
$attribute = strtolower($attribute);
$this->attributes[$template][$attribute] = $value;
}
/**
* Sets several attribute of a template
*
* $attributes has to be a assotiative arrays containing attribute/value pairs
* supported attributes: visibilty, loop, parse, unusedvars
*
* @param string $template name of the template
* @param array $attributes attribute/value pairs
* @access public
* @see setAttribute(), getAttribute(), clearAttribute()
* @return bool
*/
public function setAttributes($template, $attributes)
{
if (!is_array($attributes)) {
return false;
}
$template = strtoupper($template);
// while (list($attribute, $value) = each($attributes)) {
foreach ($attributes as $attribute => $value) {
$attribute = strtolower($attribute);
$this->attributes[$template][$attribute] = $value;
}
return true;
}
/**
* Gets an attribute of a template
*
* supported attributes: visibilty, loop, parse, unusedvars
*
* @param string $template name of the template
* @param string $attribute name of the attribute
* @return mixed value of the attribute
* @access public
* @see setAttribute(), setAttributes(), clearAttribute()
*/
public function getAttribute($template, $attribute)
{
$template = strtoupper($template);
$attribute = strtolower($attribute);
return $this->attributes[$template][$attribute];
}
/**
* Clears an attribute of a template
*
* supported attributes: visibilty, loop, parse, unusedvars
*
* @param string $template name of the template
* @param string $attribute name of the attribute
* @access public
* @see setAttribute(), setAttributes(), getAttribute()
*/
public function clearAttribute($template, $attribute)
{
$template = strtoupper($template);
$attribute = strtolower($attribute);
unset($this->attributes[$template][$attribute]);
}
/**
* Adds a subtemplate for a condition or oddeven template
*
* template type has to be condition or oddeven
*
* @param string $template name of the template
* @param string $condition condition for this subtemplate
* @access private
* @see setTemplateType()
*/
public function addSubTemplate($template, $condition)
{
$template = strtoupper($template);
$this->subtemplates[$template][$condition] = '';
$this->subtemplate_conditions[$template][$this->cnt_subtemplates[$template]] = $condition;
$this->cnt_subtemplates[$template]++;
}
/**
* Parses several templates from one patTemplate file
*
* Templates can be seperated using Tags
* The file has to be located in the directory that has been set using setBaseDir.
*
* @param string $file filename
* @access public
* @see setBasedir()
*/
public function readTemplatesFromFile($file)
{
// Tag depth
$this->depth = -1;
// Names, extracted from the Tags
$this->template_names = array();
// All HTML code, that is found between the tags
$this->template_data = array();
// Attributes, extracted from tags
$this->template_types = array();
$this->last_opened = array();
$this->last_keep = array();
$this->whitespace = array();
$this->createParser($file);
$open_tag = array_pop($this->last_opened);
if ($open_tag !== null) {
die("Error in template '" . $file . "': </" . $open_tag . '> still open at end of file.');
}
}
/**
* parse a template file and call the appropriate handlers
*
* @access private
* @param string $fname filename of the template
*/
public function createParser($fname)
{
// Store filename of the first file that has to be opened
// If basedir is set, prepend basedir
$pname = $this->basedir !== '' ? $this->basedir . '/' . $fname : $fname;
// open file for reading
$fp = fopen($pname, 'r');
// couldn't open the file => exit
if (!$fp) {
die("Couldn't open file '" . $fname . "' for reading!");
}
// Read line for line from the template
// current linenumber in file, used for error messages
$lineno = 1;
// read until end of file
while (!feof($fp)) {
// Read one line
$line = fgets($fp, 4096);
// check, wether leading and trailing whitepaces should be stripped
switch ($this->whitespace[count($this->whitespace) - 1]) {
case 'trim':
$line = trim($line);
break;
case 'ltrim':
$line = ltrim($line);
break;
case 'rtrim':
$line = rtrim($line);
break;
}
// ========= [ OPEN TAG ] =========
// check for any <patTemplate:...> Tag by using RegExp
if (preg_match('/<patTemplate:([[:alnum:]]+)[[:space:]]*(.*)>/i', $line, $regs)) {
// Get Tag name and attributes
$tagname = strtolower($regs[1]);
$attributes = $this->parseAttributes($regs[2]);
if ($attributes['keep'] > 0) {
// create new attribute
$newkeep = $attributes['keep'] > 1 ? ' keep="' . ($attributes['keep'] - 1) . '"' : '';
// replace old attribute with new attribute
$newline = str_replace(' keep="' . $attributes['keep'] . '"', $newkeep, $line);
// use this line as data
$this->dataHandler($fname, $newline, $lineno);
// if the tag was not empty keep the closing tag, too
if (substr($regs[2], -1) !== '/') {
$this->last_keep[] = true;
}
} else {
$this->last_keep[] = false;
// handle start Element
$this->startElementHandler($fname, $tagname, $attributes, $line, $lineno);
if (substr($regs[2], -1) === '/') {
$this->endElementHandler($fname, $tagname, $line, $lineno);
} // Store the name of the last opened tag
else {
$this->last_opened[] = $tagname;
}
}
}
// ========= [ CLOSING TAG ] =========
// Check if a closing <patTemplate:...> Tag has been found
elseif (preg_match("/<\/patTemplate:([[:alnum:]]+)>/i", $line, $regs)) {
// Yes => get the tagname
$tagname = strtolower($regs[1]);
$keep = array_pop($this->last_keep);
if (!$keep) {
$last_opened = array_pop($this->last_opened);
if ($last_opened === null) {
die("Error in template '" . $fname . "': no opening tag found for </" . $tagname . '> in line ' . $lineno);
}
if ($tagname != $last_opened) {
die("Error in template '" . $fname . "': closing </" . $tagname . '> does not match opened <' . $last_opened . '> in line ' . $lineno);
}
$this->endElementHandler($fname, $tagname, $line, $lineno);
} else {
$this->dataHandler($fname, $line, $lineno);
}
}
// ========= [ CDATA SECTION ] =========
// No tag found => store the line
else {
$this->dataHandler($fname, $line, $lineno);
}
// goto next line
++$lineno;
}
}
/**
* handle a <patTemplate:...> start tag in template parser
*
* @access private
* @param string $fname name of the file where the tag was found (kind of parser id)
* @param string $tagname name of the start tag that was found
* @param array $attributes all attributes that were found
* @param string $line the complete line containing the tag
* @param integer $lineno lineno in the parse file (can be used for error messages
*/
public function startElementHandler($fname, $tagname, $attributes, $line, $lineno)
{
// check for whitespace attribute
if ($attributes['whitespace']) {
array_push($this->whitespace, strtolower($attributes['whitespace']));
} // use whitepspace mode from last opened template
else {
array_push($this->whitespace, $this->whitespace[count($this->whitespace) - 1]);
}
switch ($tagname) {
// Beginning of a template found
case 'tmpl':
// parse all attributes from a string into an associative array
// Check for name of template, which is a necessary attribute
if (!$tmpl_name = strtoupper($attributes['name'])) {
die("Error in template '" . $fname . "': missing name for template in line " . $lineno);
}
unset($attributes['name']);
// Increment Tag Depth
$this->depth++;
// Start with a blank template
$this->template_data[$this->depth] = '';
// and store the name
$this->template_names[$this->depth] = $tmpl_name;
// Check, if attribute "type" was found
if ($tmpl_type = strtoupper($attributes['type'])) {
$this->template_types[$this->depth] = $tmpl_type;
$attributes['type'] = $tmpl_type;
} // No type found => this is a boring standard template
else {
$attributes['type'] = 'STANDARD';
$this->template_types[$this->depth] = 'STANDARD';
}
// Check for src attribute => external file
if ($attributes['src']) {
// Store the filename of the external file
$filename = $attributes['src'];
// Has the external file to be parsed
if ($attributes[parse] === 'on') {
$this->createParser($filename);
} // No parsing, just take the whole content of the file
else {
// Filename including full path
$external = $this->basedir !== '' ? $this->basedir . '/' . $filename : $filename;
// Open the file and read all the content
if (!$tmp = @implode('', @file($external))) {
die("Couldn't open file '" . $external . "' for reading in template " . $fname . ' line ' . $lineno);
}
$this->template_data[$this->depth] .= $tmp;
}
// Delete the src attribute, it hasn't to be stored
unset($attributes['src']);
} // No external file => the template is part of teh current file
else {
$filename = '[part of ' . $fname . ']';
}
// add the template
$this->addTemplate($this->template_names[$this->depth], $filename);
// Set all remaining attributes
$this->setAttributes($this->template_names[$this->depth], $attributes);
switch ($this->template_types[$this->depth]) {
// Template type is "ODDEVEN", it contains two alternating subtemplates
case 'ODDEVEN':
$this->setConditionVar($this->template_names[$depth], 'PAT_ROW_VAR mod 2');
break;
// Template is a condition Tenplate => it needs a condition var
case 'CONDITION':
// none found => there is an error
if (!$conditionvar = $attributes['conditionvar']) {
die("Error in template '" . $fname . "': missing conditionvar for template in line " . $lineno);
}
// conditionvar was found => store it
$this->setConditionVar($this->template_names[$this->depth], $conditionvar);
break;
// Template is a simple condition Tenplate => it needs required vars
case 'SIMPLECONDITION':
// none found => there is an error
if ($requiredvars = $attributes['requiredvars']) {
$this->setAttribute($this->template_names[$this->depth], 'requiredvars', explode(',', $requiredvars));
} else {
die("Error in template '" . $fname . "': missing requiredvars attribute for simple condition template in line " . $lineno);
}
break;
}
// if the template isn't the root( depth=0 ) template, a placeholder has
// to be put into the parent template
if ($this->depth > 0) {
// Is there a placeholder attribute?
if ($placeholder = strtoupper($attributes['placeholder'])) {
// placeholder="none" found => DO NOT PUT A PLACEHOLDER IN THE PARENT TEMPLATE!
if ($placeholder !== 'NONE') {
$this->template_data[$this->depth - 1] .= $this->tag_start . $placeholder . $this->tag_end;
}
} // No placeholder attribute found => standard placeholder
else {
$this->template_data[$this->depth - 1] .= $this->tag_start . 'TMPL:' . $this->template_names[$this->depth] . $this->tag_end . "\n";
// Tell the parent template, that it has to parse the child template, before parsing
// itself
$this->addDependency($this->template_names[$this->depth - 1], $this->template_names[$this->depth]);
}
}
break;
// Found the beginning of a subtemplate
case 'sub':
// A subtemplate needs to have a "condition" attribute
$condition = $attributes['condition'];
// None found => error
if (isset($condition) == 0) {
die("Error in template '" . $fname . "': missing condition attribute for template in line " . $lineno);
}
// Everything is ok => add the subtemplate and store the condition
$this->addSubTemplate($this->template_names[$this->depth], $condition);
// Store the current condition
$this->template_condition[$this->depth] = $condition;
break;
// Found a link template
case 'link':
$src = strtoupper($attributes['src']);
if (!$src) {
die("Error in template '" . $fname . "': missing src attribute for link in line " . $lineno);
}
// put a placeholder into the current template
if ($this->depth >= 0) {
$this->template_data[$this->depth] .= $this->tag_start . 'TMPL:' . $src . $this->tag_end . "\n";
// Tell the parent template, that it has to parse the child template, before parsing
// itself
$this->addDependency($this->template_names[$this->depth], $src);
}
break;
// No valid Tag found =>
default:
die("Error in template '" . $fname . "': unkown Tag in line " . $lineno);
break;
}
}
/**
* handle a </patTemplate:...> end tag in template parser
*
* @access private
* @param string $fname name of the file where the tag was found (kind of parser id)
* @param string $tagname name of the start tag that was found
* @param string $line the complete line containing the tag
*/
public function endElementHandler($fname, $tagname, $line)
{
array_pop($this->whitespace);
switch ($tagname) {
// End of a template found
case 'tmpl':
// If the current template is a standard template, store all content
// found between <patTemplate> Tags
if ($this->template_types[$this->depth] === 'STANDARD'
|| $this->template_types[$this->depth] === 'SIMPLECONDITION'
) {
$this->setPlainContent($this->template_names[$this->depth], $this->template_data[$this->depth]);
}
// Decrease Tagdepth
$this->depth--;
break;
// End of a subtemplate found
case 'sub':
// Store alle content found between :sub Tags
$this->setPlainContent($this->template_names[$this->depth], $this->template_data[$this->depth], $this->template_condition[$this->depth]);
// clear all Data, to store the data of the next subtemplate
$this->template_data[$this->depth] = '';
break;
// End of a link found
case 'link':
// Just ignore this tag...
break;
// No kown tag found
default:
die("Error in template '" . $fname . "': unkown closing tag in line " . $lineno);
break;
}
}
/**
* handle a CDATA in template parser
*
* @access private
* @param string $fname name of the file where the tag was found (kind of parser id)
* @param string $data all cdata that was found
*/
public function dataHandler($fname, $data)
{
$this->template_data[$this->depth] .= $data;
}
/**
* Adds a variable to a template
*
* Each Template can have an unlimited amount of its own variables
*
* @param string $template name of the template
* @param string $name name of the variables
* @param mixed $value value of the variable
* @access public
* @see addVars(), addRows(), addGlobalVar(), addGlobalVars()
*/
public function addVar($template, $name, $value)
{
$template = strtoupper($template);
$name = strtoupper($name);
if (!is_array($value)) {
$value = (string)$value;
}
// store the value and the name of the variable
$this->variables[$template][$name] = $value;
// if the value is an array, the template has to be repeated
if (is_array($value)) {
// Check, how often the template has to be repeated
if ($this->getAttribute($template, 'loop') < count($value)) {
$this->setAttribute($template, 'loop', count($value));
}
}
}
/**
* Adds several variables to a template
*
* Each Template can have an unlimited amount of its own variables
* $variables has to be an assotiative array containing variable/value pairs
*
* @param string $template name of the template
* @param array $variables assotiative array of the variables
* @param string $prefix prefix for all variable names
* @access public
* @see addVar(), addRows(), addGlobalVar(), addGlobalVars()
* @return bool
*/
public function addVars($template, $variables, $prefix = '')
{
// Are there variables?
if (!is_array($variables)) {
return false;
}
// Add all vars
// while (list($name, $value) = each($variables)) {
foreach ($variables as $name => $value) {
if (!is_int($name)) {
$this->addVar($template, $prefix . $name, $value);
}
}
return true;
}
/**
* Adds several rows of variables to a template
*
* Each Template can have an unlimited amount of its own variables
* Can be used to add a database result as variables to a template
*
* @param string $template name of the template
* @param array $rows array containing assotiative arrays with variable/value pairs
* @param string $prefix prefix for all variable names
* @access public
* @see addVar(), addVars(), addGlobalVar(), addGlobalVars()
*/
public function addRows($template, $rows, $prefix = '')
{
// Store the vars in this array
$newvars = array();
// get amount of rows
$cnt_rows = count($rows);
if ($cnt_rows == 1) {
$this->addVars($template, $rows[0], $prefix);
} else {
for ($i = 0; $i < $cnt_rows; ++$i) {
if (is_array($rows[$i])) {
// Get key and value
// while (list($key, $value) = each($rows[$i])) {
foreach ($rows[$i] as $key => $value) {
// check if the array key is an int value => skip it
if (!is_int($key)) {
// prepend prefix and store the value
$new_vars[$prefix . $key][$i] = $value;
}
}
}
}
// add the vars to the template
$this->addVars($template, $new_vars);
}
}
/**
* Adds a global variable
*
* Global variables are valid in all templates of this object
*
* @param string $name name of the global variable
* @param string $value value of the variable
* @access public
* @see addGlobalVars(), addVar(), addVars(), addRows()
*/
public function addGlobalVar($name, $value)
{
$this->globals[strtoupper($name)] = (string)$value;
}
/**
* Adds several global variables
*
* Global variables are valid in all templates of this object
* $variables is an assotiative array, containing name/value pairs of the variables
*
* @param array $variables array containing the variables
* @param string $prefix prefix for variable names
* @access public
* @see addGlobalVar(), addVar(), addVars(), addRows()
*/
public function addGlobalVars($variables, $prefix = '')
{
// while (list($variable, $value) = each($variables)) {
foreach ($variables as $variable => $value) {
$this->globals[strtoupper($prefix . $variable)] = (string)$value;
}
}
/**
* Creates a dependeny between two templates
*
* The Dependency tells a template, which templates have to be parsed before parsing the current template, because they are its children.
*
* @param string $container the name of the template, that contains the other template
* @param string $child the child of the container
* @access private
*/
public function addDependency($container, $child)
{
$this->dependencies[strtoupper($container)][] = strtoupper($child);
// This template now uses dependencies
$this->uses_dependencies = true;
}
/**
* loads a template
*
* The template has to be defined using addTemplate() or addTemplates()
*
* @param string $name name of the template that has to be loaded
* @access private
* @deprecated 2.4 2001/11/05
* @see addTemplate(), addTemplates();
* @return bool
*/
public function loadTemplate($name)
{
$name = strtoupper($name);
// prepend basedirname, if it exists
$fname = $this->basedir !== '' ? $this->basedir . '/' . $this->source[$name][filename] : $this->source[$name][filename];
if (stristr($fname, '[part')) {
return true;
}
if (!$this->plain_templates[$name] = @implode('', @file($fname))) {
die("Couldn't open template '" . $name . "' (file: '" . $fname . "') for reading.");
}
return false;
}
/**
* sets the content of a template
*
* This function should used, if a template is added using tags instead of defining it by a filename
*
* @param string $template name of the template
* @param string $content the content that has to be set
* @param string $sub condition, for the subtemplate, if any
* @access private
*/
public function setPlainContent($template, $content, $sub = '')
{
$template = strtoupper($template);
// The content has to be set for a subtemplate
if ($sub !== '') {
$this->plain_templates[$template][$sub] = $content;
} // content is meant for a template
else {
$this->plain_templates[$template] = $content;
}
}
/**
* parses a template
*
* Parses a template and stores the parsed content.
* mode can be "w" for write (delete already parsed content) or "a" for append (appends the
* new parsed content to the already parsed content)
*
*
* @param string $template name of the template
* @param string $mode mode for the parsing
* @access public
* @see parseStandardTemplate(), parseIterativeTemplate()
*/
public function parseTemplate($template, $mode = 'w')
{
$template = strtoupper($template);
$this->iteration[$template] = 0;
// The template has to be repeated
if ($this->getAttribute($template, 'loop') > 1) {
$this->parseIterativeTemplate($template, $mode);
} // parse it once
else {
$this->parseStandardTemplate($template, $mode);
}
}
/**
* parses a standard template
*
* Parses a template and stores the parsed content.
* mode can be "w" for write (delete already parsed content) or "a" for append (appends the
* new parsed content to the already parsed content)
*
*
* @param string $name name of the template
* @param string $mode mode for the parsing
* @access private
* @see parseTemplate(), parseIterativeTemplate()
*/
public function parseStandardTemplate($name, $mode = 'w')
{
$name = strtoupper($name);
// get a copy of the plain content
$temp = $this->getTemplateContent($name);
$vars = $this->getVars($name);
$vars[$this->tag_start . 'PAT_ROW_VAR' . $this->tag_end] = 1;
// while (list($tag, $value) = each($vars)) {
foreach ($vars as $tag => $value) {
if (is_array($value)) {
$value = $value[0];
}
$temp = str_replace($tag, $value, $temp);
}
// parse all global vars
$this->parseGlobals($name, $temp);
// parse child templates into this template
$this->parseDependencies($name, $temp, $mode);
// Strip unsused vars
$this->stripUnusedVars($name, $temp);
if ($mode === 'a') {
$this->parsed_templates[$name] .= $temp;
} elseif ($mode === 'w') {
$this->parsed_templates[$name] = $temp;
}
}
/**
* parses an iterative template
*
* Parses a template and stores the parsed content.
* mode can be "w" for write (delete already parsed content) or "a" for append (appends the
* new parsed content to the already parsed content)
*
*
* @param string $name name of the template
* @param string $mode mode for the parsing
* @access private
* @see parseTemplate(), parseStandardTemplate()
*/
public function parseIterativeTemplate($name, $mode)
{
$name = strtoupper($name);
$temp = '';
// repeat it template_loop[$name] times
for ($PAT_ROW_VAR = 0; $PAT_ROW_VAR < $this->getAttribute($name, 'loop'); ++$PAT_ROW_VAR) {
// add the PAT_ROW_VAR variable to the template
$this->variables[$name]['PAT_ROW_VAR'][$PAT_ROW_VAR] = $PAT_ROW_VAR + 1;
$this->iteration[$name] = $PAT_ROW_VAR;
// get the content to be parsed (dependent on PAT_ROW_VAR or conditionvar)
$current = $this->getTemplateContent($name);
$vars = $this->getVars($name);
// while (list($tag, $value) = each($vars)) {
foreach ($vars as $tag => $value) {
$current = str_replace($tag, $value, $current);
}
// and the dependent Templates
$this->parseDependencies($name, $current, $mode);
// append this parsed to the repetition
$temp .= $current;
}
// after parsing repetitions, parse the Global Vars
$this->parseGlobals($name, $temp);
// Strip unsused vars
$this->stripUnusedVars($name, $temp);
if ($mode === 'a') {
$this->parsed_templates[$name] .= $temp;
} elseif ($mode === 'w') {
$this->parsed_templates[$name] = $temp;
}
}
/**
* get variables for a template
* if the templates uses the attribute 'varscope' these vars will be fetched, too
*
* @access private
* @param string $template name of the template
* @return array $vars array containign vars
*/
public function getVars($template)
{
$vars = array();
// parse all vars
if (is_array($this->variables[$template])) {
// Pointer im Array auf 0 setzen
reset($this->variables[$template]);
// while (list($variable, $value) = each($this->variables[$template])) {
foreach ($this->variables[$template] as $variable => $value) {
$tag = $this->tag_start . $variable . $this->tag_end;
// if the variable is an array, use the index
if (is_array($value)) {
$value = $value[$this->iteration[$template]];
}
$vars[$tag] = $value;
}
}
if ($scope = strtoupper($this->getAttribute($template, 'varscope'))) {
$parentVars = $this->getVars($scope);
reset($parentVars);
// while (list($var, $value) = each($parentVars)) {
foreach ($parentVars as $var => $value) {
if (!$vars[$var]) {
$vars[$var] = $value;
}
}
}
reset($vars);
return $vars;
}
/**
* parses the global variables in a template
*
* global variables are valid in all templates
*
* @param string $name name of the template
* @param string &$temp content of the parsed Template
* @access private
* @see parseTemplate(), addGlobalVar(), addGlobalVars()
*/
public function parseGlobals($name, &$temp)
{
$name = strtoupper($name);
// check, if globals exist
if (is_array($this->globals)) {
reset($this->globals);
// while (list($variable, $value) = each($this->globals)) {
foreach ($this->globals as $variable => $value) {
$tag = $this->tag_start . $variable . $this->tag_end;
$temp = str_replace($tag, $value, $temp);
}
}
}
/**
* handles unset variables
*
* either strips, comments, replaces or ignores them, depending on the unusedvars attribute
*
* @param string $name name of the template
* @param string &$template content of the parsed Template
* @access private
* @see setAttribute()
*/
public function stripUnusedVars($name, &$template)
{
switch ($this->getAttribute($name, 'unusedvars')) {
case 'comment':
$template = preg_replace('/(' . $this->tag_start . '[^a-z{}]+' . $this->tag_end . ')/', "<!-- \\1 -->", $template);
break;
case 'strip':
$template = preg_replace('/(' . $this->tag_start . '[^a-z{}]+' . $this->tag_end . ')/', '', $template);
break;
case 'nbsp':
$template = preg_replace('/(' . $this->tag_start . '[^a-z{}]+' . $this->tag_end . ')/', ' ', $template);
break;
case 'ignore':
break;
default:
$template = preg_replace('/(' . $this->tag_start . '[^a-z{}]+' . $this->tag_end . ')/', $this->getAttribute($name, 'unusedvars'), $template);
break;
}
}
/**
* parses dependencies of a template
*
* parses child templates of a template and inserts their content
*
* @param string $name name of the template
* @param string &$temp content of the parsed Template
* @param string $mode
* @access private
* @see addDependency()
*/
public function parseDependencies($name, &$temp, $mode = 'w')
{
$name = strtoupper($name);
if (is_array($this->dependencies[$name])) {
for ($i = 0, $iMax = count($this->dependencies[$name]); $i < $iMax; ++$i) {
$type = $this->getAttribute(strtoupper($this->dependencies[$name][$i]), 'type');
// Templates placeholders have the prefix TMPL:
$tag = $this->tag_start . 'TMPL:' . $this->dependencies[$name][$i] . $this->tag_end;
// Get the parsed child template and replace it
$temp = str_replace($tag, $this->getParsedTemplate($this->dependencies[$name][$i]), $temp);
if (($type == patTEMPLATE_TYPE_CONDITION || $type == patTEMPLATE_TYPE_SIMPLECONDITION)
&& $mode === 'w'
) {
unset($this->parsed_templates[$this->dependencies[$name][$i]]);
}
}
}
}
/**
* returns a parsed Template
*
* If the template already has been parsed, it just returns the parsed template.
* If the template has not been loaded, it will be loaded.
*
* @param string $name name of the template
* @return string $content Content of the parsed template
* @access public
* @see displayParsedTemplate()
*/
public function getParsedTemplate($name = '')
{
$name = strtoupper($name);
// if a name was given, parse only this template
if ($name !== '') {
// check, wther template was disabled
if ($this->getAttribute($name, 'visibility') === 'hidden') {
return false;
}
// check, if the template has already been parsed => just return it
if (!empty($this->parsed_templates[$name])) {
return $this->parsed_templates[$name];
}
// Check, if the template has been loaded, if not, load it
if (empty($this->plain_templates[$name])) {
$this->loadTemplate($name);
}
// Template is loaded, but not parsed then parse it!
$this->parseTemplate($name);
// And return the parsed template
return $this->parsed_templates[$name];
} // No name given
else {
// The template uses dependencies, then start with the root template
if ($this->uses_dependencies) {
return $this->getParsedTemplate($this->templates[0]);
} // Only one template => parse and return it
elseif ($this->cnt_templates == 1) {
return $this->getParsedTemplate($this->templates[0]);
} // No dependencies, but more than one => return all parsed templates in an array
else {
for ($i = 0; $i < $this->cnt_templates; ++$i) {
$arr[$this->templates[$i]] = $this->getParsedTemplate($this->templates[$i]);
}
return $arr;
}
}
}
/**
* displays a parsed Template
*
* If the template has not been loaded, it will be loaded.
*
* @param string $name name of the template
* @access public
* @see getParsedTemplate()
*/
public function displayParsedTemplate($name = '')
{
$name = strtoupper($name);
// if a name was given, parse and display it
if ($name) {
echo $this->getParsedTemplate($name);
} // No name was given, display them all!
else {
// if the template uses dependencies, start with the root template
if ($this->uses_dependencies) {
echo $this->getParsedTemplate($this->templates[0]);
} // Only one template => parse and return it
elseif ($this->cnt_templates == 1) {
echo $this->getParsedTemplate($this->templates[0]);
} // parse and display them all
else {
$templates = $this->getParsedTemplate();
for ($i = 0; $i < $this->cnt_templates; ++$i) {
echo $templates[$this->templates[$i]];
}
}
}
}
/**
* returns an unparsed Template
*
* If the template has not been loaded, it will be loaded.
*
* @param string $name name of the template
* @access private
* @deprecated 2.4 2001/11/05
* @return string $content Unparsed content of the template
* @see getPlainSubTemplate(), displayPlainTemplate()
*/
public function getPlainTemplate($name)
{
$name = strtoupper($name);
// check, wether the template is already loaded
if (empty($this->plain_templates[$name])) {
$this->loadTemplate($name);
}
// return it
return $this->plain_templates[$name];
}
/**
* returns an unparsed Subtemplate
*
* The template of the template has to be set
*
* @param string $name name of the template
* @param string $sub condition for the subtemplate
* @access private
* @deprecated 2.4 2001/11/05
* @return string $content Unparsed content of the template
* @see getPlainTemplate(), displayPlainTemplate()
*/
public function getPlainSubTemplate($name, $sub)
{
$name = strtoupper($name);
return $this->plain_templates[$name][$sub];
}
/**
* displays an unparsed Template
*
* If the template has not been loaded, it will be loaded.
*
* @param string $name name of the template
* @access private
* @deprecated 2.4 2001/11/05
* @see getPlainTemplate(), getPlainSubTemplate()
*/
public function displayPlainTemplate($name)
{
$name = strtoupper($name);
echo $this->getPlainTemplate($name);
}
/**
* clears a parsed Template
*
* parsed Content, variables and the loop attribute are cleared
*
* @param string $name name of the template
* @access public
*/
public function clearTemplate($name)
{
$name = strtoupper($name);
unset($this->parsed_templates[$name], $this->variables[$name]);
$this->clearAttribute($name, 'loop');
}
/**
* clears all templates
*
* @access public
*/
public function clearAllTemplates()
{
for ($i = 0, $iMax = count($this->templates); $i < $iMax; ++$i) {
$this->clearTemplate($this->templates[$i]);
}
}
/**
* parsed attributes from a string
*
* used for parsing <patTemplate> Tags
*
* @param string $string string containing the attributes
* @return array $array assotiative array, containing all attributes
* @access private
*/
public function parseAttributes($string)
{
// Check for trailing slash, if tag was an empty XML Tag
if (substr($string, -1) === '/') {
$string = substr($string, 0, strlen($string) - 1);
}
$pairs = explode(' ', $string);
for ($i = 0, $iMax = count($pairs); $i < $iMax; ++$i) {
$pair = explode('=', trim(str_replace('"', '', $pairs[$i])));
if (count($pair) == 1) {
$pair[1] = 'yes';
}
$attributes[strtolower($pair[0])] = $pair[1];
}
return $attributes;
}
/**
* returns the plain content of a template
*
* return value depends on iteration value
*
* @param string $name name of the template
* @return string $content plain content of the template
* @internal param int $index iteration number
* @access private
*/
public function getTemplateContent($name)
{
$name = strtoupper($name);
$index = $this->iteration[$name];
// Is it a standard, oddeven or condition template
switch ($this->getAttribute($name, 'type')) {
case patTEMPLATE_TYPE_ODDEVEN:
$sub = ($index + 1) % 2 == 0 ? 'even' : 'odd';
return $this->plain_templates[$name][$sub];
break;
case patTEMPLATE_TYPE_CONDITION:
$conditionval = $this->getVar($name, $this->conditionvars[$name]);
// check, if conditionvalue is empty
if (!isset($conditionval) || (is_string($conditionval) && $conditionval === '')
|| $conditionval === false
) {
$conditionval = 'empty';
}
// check if condition was specified, otherwise use default
$condition_found = false;
for ($i = 0; $i < $this->cnt_subtemplates[$name]; ++$i) {
if ($this->subtemplate_conditions[$name][$i] == $conditionval) {
$condition_found = true;
break;
}
}
if (!$condition_found) {
$conditionval = 'default';
}
return $this->plain_templates[$name][$conditionval];
break;
case patTEMPLATE_TYPE_SIMPLECONDITION:
// get required vars
$requiredVars = $this->getAttribute($name, 'requiredvars');
// check, if all are set
for ($i = 0, $iMax = count($requiredVars); $i < $iMax; ++$i) {
if (!$this->getVar($name, $requiredVars[$i])) {
return '';
}
}
return $this->plain_templates[$name];
break;
default:
return $this->plain_templates[$name];
break;
}
}
/**
* get the value of a variable
*
* @param string $template name of the template
* @param string $var name of the variable
* @return mixed $value value of the variable / false if it doesn't exist
* @internal param int $index no of repetition
*/
public function getVar($template, $var)
{
// should the var from a different template be used
if (stristr($var, '.')) {
list($template, $var) = explode('.', $var);
}
$var = strtoupper($var);
$index = $this->iteration[$template];
if ($scope = $this->getAttribute($template, 'varscope')) {
$val = $this->getVar(strtoupper($scope), $var);
} else {
$val = $this->variables[$template][$var];
}
// check, if global var should be used
if (!$val && $this->getAttribute($template, 'useglobals') === 'yes') {
$val = $this->globals[$var];
}
if (is_array($val)) {
$val = $val[$index];
}
return $val;
}
/**
* displays useful information about all templates
*
* returns content, variables, attributes and unused variables
*
* @access public
*/
public function dump()
{
echo "<style type=\"text/css\">\n";
echo ".text {font-family: Verdana, Arial, sans-serif; font-size: 10px; color: #000000}\n";
echo ".mid {font-family: Verdana, Arial, sans-serif; font-size: 12px; color: #000000}\n";
echo ".head {font-family: Verdana, Arial, sans-serif; font-size: 16px; font-weight: bold; color: #000000}\n";
echo "</style>\n";
echo "<table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" bgcolor=\"White\" >\n";
for ($i = 0; $i < $this->cnt_templates; ++$i) {
$name = $this->templates[$i];
// Template name
echo " <tr bgcolor=\"#EEEEEE\">\n";
echo " <td class=\"head\" valign=\"top\">Template</td>\n";
echo ' <td class="head" valign="top">' . $name . "</td>\n";
echo " </tr>\n";
$fname = $this->basedir !== '' ? $this->basedir . '/' . $this->filenames[$name] : $this->filenames[$name];
// Template file
echo " <tr>\n";
echo " <td class=\"mid\" valign=\"top\"><b>Filename</b></td>\n";
echo ' <td class="text" valign="top">' . $fname . "</td>\n";
echo " </tr>\n";
// Template Attributes
echo " <tr>\n";
echo " <td class=\"mid\" valign=\"top\"><b>Attributes</b></td>\n";
echo " <td class=\"text\" valign=\"top\">\n";
echo " <table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";
// Display all Attributes in table
// while (list($key, $value) = each($this->attributes[$name])) {
foreach ($this->attributes[$name] as $key => $value) {
echo " <tr>\n";
echo ' <td class="text"><b>' . $key . "</b></td>\n";
echo " <td class=\"text\"> : </td>\n";
echo ' <td class="text">' . $value . "</td>\n";
echo " </tr>\n";
}
echo " </table>\n";
echo " </td>\n";
echo " </tr>\n";
echo " <tr>\n";
echo " <td class=\"text\" valign=\"top\" colspan=\"2\"> </td>\n";
echo " </tr>\n";
if ($this->cnt_subtemplates[$name] > 0) {
// display template Data
echo " <tr>\n";
echo " <td class=\"mid\" valign=\"top\"><b>Template Data:</b></td>\n";
echo ' <td class="text" valign="top">Amount of subtemplates: ' . $this->cnt_subtemplates[$name] . "</td>\n";
echo " </tr>\n";
// Display all Subtemplates
for ($j = 0; $j < $this->cnt_subtemplates[$name]; ++$j) {
$condition = $this->subtemplate_conditions[$name][$j];
echo " <tr bgcolor=\"#DDDDDD\">\n";
echo " <td class=\"text\"><b>Condition</b></td>\n";
echo ' <td class="text">' . $condition . "</td>\n";
echo " </tr>\n";
echo " <tr>\n";
echo " <td class=\"text\" valign=\"top\"><b>Template Data</b></td>\n";
echo ' <td class="text" valign="top"><pre>' . htmlspecialchars($this->getPlainSubTemplate($name, $condition)) . "</pre></td>\n";
echo " </tr>\n";
unset($matches);
// Check for unset variables
preg_match_all($this->regex_get_all_vars, $this->getPlainSubTemplate($name, $condition), $matches);
// Empty the array that stores the unused Vars
unset($unused);
if (is_array($matches[0]) && count($matches[0]) > 0) {
// Check, wether variable is unused
for ($k = 0, $kMax = count($matches[0]); $k <= $kMax; ++$k) {
if ($matches[1][$k] !== '' && !isset($this->variables[$name][$matches[1][$k]])
&& !isset($this->globals[$matches[1][$k]])
) {
$unused[] = $matches[0][$k];
}
}
}
if (is_array($unused) && count($unused) > 0) {
echo " <tr>\n";
echo " <td class=\"mid\" valign=\"top\"><b>Unused variables</b></td>\n";
echo " <td class=\"text\" valign=\"top\">\n";
echo " <table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";
// Display all Variables in table
for ($k = 0, $kMax = count($unused); $k <= $kMax; ++$k) {
{
echo " <tr>\n";
echo ' <td class="text">' . $unused[$k] . "</td>\n";
echo " </tr>\n";
}
}
echo " </table>\n";
echo " </td>\n";
echo " </tr>\n";
}
echo " <tr>\n";
echo " <td class=\"text\" colspan=\"2\"> </td>\n";
echo " </tr>\n";
}
} else {
// display template Data
echo " <tr>\n";
echo " <td class=\"mid\" valign=\"top\"><b>Template Data:</b></td>\n";
echo " <td class=\"text\" valign=\"top\">\n";
echo ' <pre>' . htmlspecialchars($this->getPlainTemplate($name)) . "</pre>\n";
echo " </td>\n";
echo " </tr>\n";
unset($matches);
// Check for unset variables
preg_match_all($this->regex_get_all_vars, $this->getPlainTemplate($name), $matches);
// Empty the array that stores the unused Vars
unset($unused);
if (is_array($matches[0]) && count($matches[0]) > 0) {
// Check, wether variable is unused
for ($k = 0, $kMax = count($matches[0]); $k < $kMax; ++$k) {
if ($matches[1][$k] !== '' && !isset($this->variables[$name][$matches[1][$k]])
&& !isset($this->globals[$matches[1][$k]])
) {
$unused[] = $matches[0][$k];
}
}
}
if (is_array($unused) && count($unused) > 0) {
echo " <tr>\n";
echo " <td class=\"mid\" valign=\"top\"><b>Unused variables</b></td>\n";
echo " <td class=\"text\" valign=\"top\">\n";
echo " <table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";
// Display all Variables in table
for ($k = 0, $kMax = count($unused); $k <= $kMax; ++$k) {
{
echo " <tr>\n";
echo ' <td class="text">' . $unused[$k] . "</td>\n";
echo " </tr>\n";
}
}
echo " </table>\n";
echo " </td>\n";
echo " </tr>\n";
}
}
// Display Variables
if (is_array($this->variables[$name]) && count($this->variables[$name]) > 0) {
reset($this->variables[$name]);
echo " <tr>\n";
echo " <td class=\"mid\" valign=\"top\"><b>Variables</b></td>\n";
echo " <td class=\"text\" valign=\"top\">\n";
echo " <table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";
// Display all Variables in table
// while (list($key, $value) = each($this->variables[$name])) {
foreach ($this->variables[$name] as $key => $value) {
if (is_array($value)) {
$value = implode(', ', $value);
}
echo " <tr>\n";
echo ' <td class="text"><b>' . $this->tag_start . $key . $this->tag_end . "</b></td>\n";
echo " <td class=\"text\"> => </td>\n";
echo ' <td class="text">' . $value . "</td>\n";
echo " </tr>\n";
}
echo " </table>\n";
echo " </td>\n";
echo " </tr>\n";
}
echo " <tr>\n";
echo " <td class=\"text\" valign=\"top\" colspan=\"2\"> </td>\n";
echo " </tr>\n";
}
echo '</table>';
}
}
}