Source for file Temply.class.php
Documentation is available at Temply.class.php
* This class does all the parsing stuff. It looks for possible tags inside
* of a given template string, using the TagHandler to identify valid tags.
* The output of all valid tags is then inserted into the template string,
* See http://www.oliver-matuschin.de for more information and version udpates.
* @author Oliver Matuschin <mail@oliver-matuschin.de>
* @copyright Copyright (c) 2010 Oliver Matuschin
* @license LGPL http://www.gnu.de/documents/lgpl.de.html
// TODO Implement "isValidParameter" as handler method.
* All constants needed to define a tag.
* The default syntax for a single tag is
* [ tagname : attrname = attrvalue ... / ]
* The default syntax for a block tag is
* [ tagname : attrname = attrvalue ... ] block content [ /tagname ]
const PARSER_BEGIN_TAG = '[';
const PARSER_END_TAG = ']';
const PARSER_DIVIDER_CHAR = ':';
const PARSER_CLOSE_CHAR = '/';
* All possible parser states.
const PARSER_STATE_SEARCHTAG = 0;
const PARSER_STATE_READ_TAGNAME = 1;
const PARSER_STATE_SEARCHDIVIDER = 2;
const PARSER_STATE_SEARCHATTR = 3;
const PARSER_STATE_READ_ATTRNAME = 4;
const PARSER_STATE_SEARCHASSIGNMENT = 5;
const PARSER_STATE_SEARCHVALUE = 6;
const PARSER_STATE_READVALUE = 7;
const PARSER_STATE_SEARCHENDOFTAG = 8;
const PARSER_STATE_RESET = 9;
* The constructor of Temply. If no TagHandler is
* provided, or if the TagHandler is null, an instance of
* the EmptyTagHandler will be used for tag processing.
* @param ITagHandler [optional] The TagHandler.
public function __construct(ITagHandler $tagHandler= null) {
* Sets the TagHandler to be used by Temply.
* @param ITagHandler The TagHandler.
* Returns the current TagHandler.
* @return ITagHandler The current TagHandler.
* Parses the content of the provided string, using
* the current TagHandler for tag processing.
* @param string The content to parse.
* @return string The parsed content.
public function parse($content) {
// Buffer for the parsed content
// Initial state of the parser
$currentState = self::PARSER_STATE_SEARCHTAG;
// Temporary buffers for the parser
$attributeNameBuffer = '';
$attributeValueBuffer = '';
$attributeQuoted = false;
for($i= 0; $i< $len; $i++ ) {
&& $currentState != self::PARSER_STATE_SEARCHTAG
&& $currentState != self::PARSER_STATE_RESET
&& ($char == self::PARSER_CLOSE_CHAR || $char == self::PARSER_END_TAG)) {
if(($char == self::PARSER_CLOSE_CHAR
&& !$currentTag->isCloseTag
&& !$currentTag->isTagClosed)
|| $char == self::PARSER_END_TAG) {
if(strlen($tagNameBuffer) != 0
if(count($tagQueue) == 0) {
if($currentState == self::PARSER_STATE_SEARCHASSIGNMENT
|| $currentState == self::PARSER_STATE_READ_ATTRNAME) {
$currentTag->tagAttribs['default'] = $this->tagHandler->preprocessAttribute('default', $attributeNameBuffer, $attributeQuoted);
$currentTag->tagAttribs['%'. $paramCount] = $this->tagHandler->preprocessAttribute('%'. $paramCount, $attributeNameBuffer, $attributeQuoted);
else if($currentState == self::PARSER_STATE_READVALUE)
$currentTag->tagAttribs[$attributeNameBuffer] = $this->tagHandler->preprocessAttribute($attributeNameBuffer, $attributeValueBuffer, $attributeQuoted);
$currentState = self::PARSER_STATE_SEARCHENDOFTAG;
if($char == self::PARSER_CLOSE_CHAR) {
$currentTag->isTagClosed = true;
if($char == self::PARSER_CLOSE_CHAR
&& strlen($tagNameBuffer) == 0) {
$currentTag->isCloseTag = true;
$currentState = self::PARSER_STATE_RESET;
// TODO check for wrong characters inside tagnames / attributenames
case self::PARSER_STATE_SEARCHTAG:
if($char == self::PARSER_BEGIN_TAG) {
$currentTag->tagStartPos = $i;
$currentState = self::PARSER_STATE_READ_TAGNAME;
$attributeQuoted = false;
else if(count($tagQueue) == 0) {
case self::PARSER_STATE_READ_TAGNAME:
|| $char == self::PARSER_DIVIDER_CHAR) {
if(!$currentTag->isCloseTag) {
$currentState = self::PARSER_STATE_SEARCHDIVIDER;
else if($char != self::PARSER_DIVIDER_CHAR) {
$currentState = self::PARSER_STATE_SEARCHENDOFTAG;
$currentState = self::PARSER_STATE_RESET;
$currentState = self::PARSER_STATE_RESET;
else $tagNameBuffer .= $char;
case self::PARSER_STATE_SEARCHDIVIDER:
if($char == self::PARSER_DIVIDER_CHAR)
$currentState = self::PARSER_STATE_SEARCHATTR;
$currentState = self::PARSER_STATE_RESET;
case self::PARSER_STATE_SEARCHASSIGNMENT:
if($char == '=') $currentState = self::PARSER_STATE_SEARCHVALUE;
if(count($tagQueue) == 0) {
$currentTag->tagAttribs['default'] = $this->tagHandler->preprocessAttribute('default', $attributeNameBuffer, $attributeQuoted);
$currentTag->tagAttribs['%'. $paramCount] = $this->tagHandler->preprocessAttribute('%'. $paramCount, $attributeNameBuffer, $attributeQuoted);
$currentState = self::PARSER_STATE_SEARCHATTR;
case self::PARSER_STATE_SEARCHATTR:
case self::PARSER_STATE_SEARCHVALUE:
if($currentState == self::PARSER_STATE_SEARCHATTR) {
$attributeNameBuffer = '';
$currentState = self::PARSER_STATE_READ_ATTRNAME;
$currentState = self::PARSER_STATE_READVALUE;
$attributeValueBuffer = '';
$attributeQuoted = false;
case self::PARSER_STATE_READ_ATTRNAME:
case self::PARSER_STATE_READVALUE:
if($char == "\\") $escaped = true;
else if($char == '"') $quoted = false;
else if($currentState == self::PARSER_STATE_READVALUE)
$attributeValueBuffer .= $char;
else $attributeNameBuffer .= $char;
$tmpLen = strlen($attributeNameBuffer);
if($currentState == self::PARSER_STATE_READVALUE)
$tmpLen = strlen($attributeValueBuffer);
|| ($char == '=' && $currentState == self::PARSER_STATE_READ_ATTRNAME)) {
if($currentState == self::PARSER_STATE_READVALUE) {
$currentState = self::PARSER_STATE_SEARCHATTR;
if(count($tagQueue) == 0)
$currentTag->tagAttribs[$attributeNameBuffer] = $this->tagHandler->preprocessAttribute($attributeNameBuffer, $attributeValueBuffer, $attributeQuoted);
$currentState = self::PARSER_STATE_SEARCHASSIGNMENT;
else if($char == '"' && $tmpLen == 0) {
else if($attributeQuoted) {
$currentState = self::PARSER_STATE_RESET;
else if($currentState == self::PARSER_STATE_READVALUE) $attributeValueBuffer .= $char;
else $attributeNameBuffer .= $char;
if($char == 't') $char = "\t";
else if($char == 'n') $char = "\n";
else if($char == 'r') $char = "\r";
$attributeValueBuffer .= $char;
case self::PARSER_STATE_SEARCHENDOFTAG:
else if($char != self::PARSER_END_TAG) {
$currentState = self::PARSER_STATE_RESET;
$currentState = self::PARSER_STATE_SEARCHTAG;
$currentTag->tagName = $tagNameBuffer;
$currentTag->tagEndPos = $i;
if($currentTag->isTagClosed) {
if(count($tagQueue) == 0)
$buffer .= $this->tagHandler->processSingleTag( $tagNameBuffer,
$currentTag->tagAttribs);
else if($currentTag->isCloseTag) {
|| $tagQueue[count($tagQueue)- 1]->tagName != $currentTag->tagName) {
$currentState = self::PARSER_STATE_RESET;
// TODO Add ability to handle special tags (for example "IF-ENDIF")
if($openingTag->tagName != $currentTag->tagName) {
$tagQueue[] = $openingTag;
else if(count($tagQueue) == 0) {
$buffer .= $this->tagHandler->processBlockTag( $openingTag->tagName,
substr($content, $openingTag->tagEndPos+ 1, $currentTag->tagStartPos - $openingTag->tagEndPos- 1));
$tagQueue[] = $currentTag;
case self::PARSER_STATE_RESET:
if(count($tagQueue) == 0)
$buffer .= substr($content, $currentTag->tagStartPos, $i- $currentTag->tagStartPos+ 1);
$currentState = self::PARSER_STATE_SEARCHTAG;
&& ($currentState != self::PARSER_STATE_SEARCHTAG
|| count($tagQueue) != 0)) {
if(count($tagQueue) != 0)
$currentTag = $tagQueue[0];
$buffer .= substr($content, $currentTag->tagStartPos, $i- $currentTag->tagStartPos+ 1);
* Checks whether the given character is visible or not.
* @param string The character to check.
* @return boolean True if the character is invisible, false otherwise.
return ($char == ' ' || $char == "\t" || $char == "\n" || $char == "\r");
* Private dataholder class for internal use by Temply.
|