public function parseString(PHPTAL_Dom_DocumentBuilder $builder, $src, $filename = '<string>') { try { $builder->setEncoding($this->input_encoding); $this->_file = $filename; $this->_line = 1; $state = self::ST_ROOT; $mark = 0; $len = strlen($src); $quoteStyle = '"'; $tagname = ""; $attribute = ""; $attributes = array(); $customDoctype = false; $builder->setSource($this->_file, $this->_line); $builder->onDocumentStart(); $i = 0; // remove BOM (UTF-8 byte order mark)... if (substr($src, 0, 3) === self::BOM_STR) { $i = 3; } for (; $i < $len; $i++) { $c = $src[$i]; // Change to substr($src, $i, 1); if you want to use mb_string.func_overload if ($c === "\n") { $builder->setSource($this->_file, ++$this->_line); } switch ($state) { case self::ST_ROOT: if ($c === '<') { $mark = $i; // mark tag start $state = self::ST_LT; } elseif (!self::isWhiteChar($c)) { $this->raiseError("Characters found before beginning of the document! (wrap document in < tal:block > to avoid this error)"); } break; case self::ST_TEXT: if ($c === '<') { if ($mark != $i) { $builder->onElementData($this->sanitizeEscapedText($this->checkEncoding(substr($src, $mark, $i - $mark)))); } $mark = $i; $state = self::ST_LT; } break; case self::ST_LT: if ($c === '/') { $mark = $i + 1; $state = self::ST_TAG_CLOSE; } elseif ($c === '?' and strtolower(substr($src, $i, 5)) === '?xml ') { $state = self::ST_XMLDEC; } elseif ($c === '?') { $state = self::ST_PREPROC; } elseif ($c === '!' and substr($src, $i, 3) === '!--') { $state = self::ST_COMMENT; } elseif ($c === '!' and substr($src, $i, 8) === '![CDATA[') { $state = self::ST_CDATA; $mark = $i + 8; // past opening tag } elseif ($c === '!' and strtoupper(substr($src, $i, 8)) === '!DOCTYPE') { $state = self::ST_DOCTYPE; } elseif (self::isWhiteChar($c)) { $state = self::ST_TEXT; } else { $mark = $i; // mark node name start $attributes = array(); $attribute = ""; $state = self::ST_TAG_NAME; } break; case self::ST_TAG_NAME: if (self::isWhiteChar($c) || $c === '/' || $c === '>') { $tagname = substr($src, $mark, $i - $mark); if (!$this->isValidQName($tagname)) { $this->raiseError("Invalid tag name '{$tagname}'"); } if ($c === '/') { $state = self::ST_TAG_SINGLE; } elseif ($c === '>') { $mark = $i + 1; // mark text start $state = self::ST_TEXT; $builder->onElementStart($tagname, $attributes); } else { $state = self::ST_TAG_ATTRIBUTES; } } break; case self::ST_TAG_CLOSE: if ($c === '>') { $tagname = rtrim(substr($src, $mark, $i - $mark)); $builder->onElementClose($tagname); $mark = $i + 1; // mark text start $state = self::ST_TEXT; } break; case self::ST_TAG_SINGLE: if ($c !== '>') { $this->raiseError("Expected '/>', but found '/{$c}' inside tag < {$tagname} >"); } $mark = $i + 1; // mark text start $state = self::ST_TEXT; $builder->onElementStart($tagname, $attributes); $builder->onElementClose($tagname); break; case self::ST_TAG_BETWEEN_ATTRIBUTE: case self::ST_TAG_ATTRIBUTES: if ($c === '>') { $mark = $i + 1; // mark text start $state = self::ST_TEXT; $builder->onElementStart($tagname, $attributes); } elseif ($c === '/') { $state = self::ST_TAG_SINGLE; } elseif (self::isWhiteChar($c)) { $state = self::ST_TAG_ATTRIBUTES; } elseif ($state === self::ST_TAG_ATTRIBUTES && $this->isValidQName($c)) { $mark = $i; // mark attribute key start $state = self::ST_ATTR_KEY; } else { $this->raiseError("Unexpected character '{$c}' between attributes of < {$tagname} >"); } break; case self::ST_COMMENT: if ($c === '>' && $i > $mark + 4 && substr($src, $i - 2, 2) === '--') { if (preg_match('/^-|--|-$/', substr($src, $mark + 4, $i - $mark + 1 - 7))) { $this->raiseError("Ill-formed comment. XML comments are not allowed to contain '--' or start/end with '-': " . substr($src, $mark + 4, $i - $mark + 1 - 7)); } $builder->onComment($this->checkEncoding(substr($src, $mark + 4, $i - $mark + 1 - 7))); $mark = $i + 1; // mark text start $state = self::ST_TEXT; } break; case self::ST_CDATA: if ($c === '>' and substr($src, $i - 2, 2) === ']]') { $builder->onCDATASection($this->checkEncoding(substr($src, $mark, $i - $mark - 2))); $mark = $i + 1; // mark text start $state = self::ST_TEXT; } break; case self::ST_XMLDEC: if ($c === '?' && substr($src, $i, 2) === '?>') { $builder->onXmlDecl($this->checkEncoding(substr($src, $mark, $i - $mark + 2))); $i++; // skip '>' $mark = $i + 1; // mark text start $state = self::ST_TEXT; } break; case self::ST_DOCTYPE: if ($c === '[') { $customDoctype = true; } elseif ($customDoctype && $c === '>' && substr($src, $i - 1, 2) === ']>') { $customDoctype = false; $builder->onDocType($this->checkEncoding(substr($src, $mark, $i - $mark + 1))); $mark = $i + 1; // mark text start $state = self::ST_TEXT; } elseif (!$customDoctype && $c === '>') { $customDoctype = false; $builder->onDocType($this->checkEncoding(substr($src, $mark, $i - $mark + 1))); $mark = $i + 1; // mark text start $state = self::ST_TEXT; } break; case self::ST_PREPROC: if ($c === '>' and substr($src, $i - 1, 1) === '?') { $builder->onProcessingInstruction($this->checkEncoding(substr($src, $mark, $i - $mark + 1))); $mark = $i + 1; // mark text start $state = self::ST_TEXT; } break; case self::ST_ATTR_KEY: if ($c === '=' || self::isWhiteChar($c)) { $attribute = substr($src, $mark, $i - $mark); if (!$this->isValidQName($attribute)) { $this->raiseError("Invalid attribute name '{$attribute}' in < {$tagname} >"); } if (isset($attributes[$attribute])) { $this->raiseError("Attribute {$attribute} in < {$tagname} > is defined more than once"); } if ($c === '=') { $state = self::ST_ATTR_VALUE; } else { /* white char */ $state = self::ST_ATTR_EQ; } } elseif ($c === '/' || $c === '>') { $attribute = substr($src, $mark, $i - $mark); if (!$this->isValidQName($attribute)) { $this->raiseError("Invalid attribute name '{$attribute}'"); } $this->raiseError("Attribute {$attribute} does not have value (found end of tag instead of '=')"); } break; case self::ST_ATTR_EQ: if ($c === '=') { $state = self::ST_ATTR_VALUE; } elseif (!self::isWhiteChar($c)) { $this->raiseError("Attribute {$attribute} in < {$tagname} > does not have value (found character '{$c}' instead of '=')"); } break; case self::ST_ATTR_VALUE: if (self::isWhiteChar($c)) { } elseif ($c === '"' or $c === '\'') { $quoteStyle = $c; $state = self::ST_ATTR_QUOTE; $mark = $i + 1; // mark attribute real value start } else { $this->raiseError("Value of attribute {$attribute} in < {$tagname} > is not in quotes (found character '{$c}' instead of quote)"); } break; case self::ST_ATTR_QUOTE: if ($c === $quoteStyle) { $attributes[$attribute] = $this->sanitizeEscapedText($this->checkEncoding(substr($src, $mark, $i - $mark))); // PHPTAL's code generator assumes input is escaped for double-quoted strings. Single-quoted attributes need to be converted. // FIXME: it should be escaped at later stage. $attributes[$attribute] = str_replace('"', """, $attributes[$attribute]); $state = self::ST_TAG_BETWEEN_ATTRIBUTE; } break; } } if ($state === self::ST_TEXT) { if ($i > $mark) { $text = substr($src, $mark, $i - $mark); if (!ctype_space($text)) { $this->raiseError("Characters found after end of the root element (wrap document in < tal:block > to avoid this error)"); } } } else { if ($state === self::ST_ROOT) { $msg = "Document does not have any tags"; } else { $msg = "Finished document in unexpected state: " . self::$state_names[$state] . " is not finished"; } $this->raiseError($msg); } $builder->onDocumentEnd(); } catch (PHPTAL_TemplateException $e) { $e->hintSrcPosition($this->_file, $this->_line); throw $e; } return $builder; }
public function __construct() { $this->result = ''; parent::__construct(); }
/** * parse currently set template * @return string (compiled PHP code) */ protected function parse() { self::setIncludePath(); require_once 'PHPTAL/Dom/DocumentBuilder.php'; require_once 'PHPTAL/Php/CodeGenerator.php'; self::restoreIncludePath(); // instantiate the PHPTAL source parser $parser = new PHPTAL_Dom_SaxXmlParser($this->_encoding); $builder = new PHPTAL_Dom_DocumentBuilder(); $builder->stripComments($this->_stripComments); $data = $this->_source->getData(); $realpath = $this->_source->getRealPath(); if ($this->_prefilter) { $data = $this->_prefilter->filter($data); } $tree = $parser->parseString($builder, $data, $realpath)->getResult(); $generator = new PHPTAL_Php_CodeGenerator($this->getFunctionName(), $this->_source->getRealPath(), $this->_encoding, $this->_outputMode, $this->getCodePath()); $result = $generator->generateCode($tree); return $result; }