Библиотека для cis, online, cms1
This commit is contained in:
commit
3c2e614d87
269 changed files with 39854 additions and 0 deletions
237
PHPTAL/Dom/Defs.php
Normal file
237
PHPTAL/Dom/Defs.php
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
//
|
||||
// Copyright (c) 2004-2005 Laurent Bedubourg
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
//
|
||||
|
||||
// From http://dev.zope.org/Wikis/DevSite/Projects/ZPT/TAL%20Specification%201.4
|
||||
//
|
||||
// Order of Operations
|
||||
//
|
||||
// When there is only one TAL statement per element, the order in which
|
||||
// they are executed is simple. Starting with the root element, each
|
||||
// element's statements are executed, then each of its child elements is
|
||||
// visited, in order, to do the same.
|
||||
//
|
||||
// Any combination of statements may appear on the same elements, except
|
||||
// that the content and replace statements may not appear together.
|
||||
//
|
||||
// When an element has multiple statements, they are executed in this
|
||||
// order:
|
||||
//
|
||||
// * define
|
||||
// * condition
|
||||
// * repeat
|
||||
// * content or replace
|
||||
// * attributes
|
||||
// * omit-tag
|
||||
//
|
||||
// Since the on-error statement is only invoked when an error occurs, it
|
||||
// does not appear in the list.
|
||||
//
|
||||
// The reasoning behind this ordering goes like this: You often want to set
|
||||
// up variables for use in other statements, so define comes first. The
|
||||
// very next thing to do is decide whether this element will be included at
|
||||
// all, so condition is next; since the condition may depend on variables
|
||||
// you just set, it comes after define. It is valuable be able to replace
|
||||
// various parts of an element with different values on each iteration of a
|
||||
// repeat, so repeat is next. It makes no sense to replace attributes and
|
||||
// then throw them away, so attributes is last. The remaining statements
|
||||
// clash, because they each replace or edit the statement element.
|
||||
//
|
||||
// If you want to override this ordering, you must do so by enclosing the
|
||||
// element in another element, possibly div or span, and placing some of
|
||||
// the statements on this new element.
|
||||
//
|
||||
|
||||
require_once PHPTAL_DIR.'PHPTAL/Namespace.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Namespace/TAL.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Namespace/METAL.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Namespace/I18N.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Namespace/PHPTAL.php';
|
||||
|
||||
/**
|
||||
* PHPTAL constants.
|
||||
*
|
||||
* This is a pseudo singleton class, a user may decide to provide
|
||||
* his own singleton instance which will then be used by PHPTAL.
|
||||
*
|
||||
* This behaviour is mainly usefull to remove builtin namespaces
|
||||
* and provide custom ones.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Dom_Defs
|
||||
{
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$_instance == null){
|
||||
self::$_instance = new PHPTAL_Dom_Defs();
|
||||
}
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
public static function setInstance(PHPTAL_Dom_Defs $instance)
|
||||
{
|
||||
self::$_instance = $instance;
|
||||
}
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->_dictionary = array();
|
||||
$this->_namespaces = array();
|
||||
$this->_xmlns = array();
|
||||
}
|
||||
|
||||
public function isEmptyTag($tagName)
|
||||
{
|
||||
return in_array(strtolower($tagName), self::$XHTML_EMPTY_TAGS);
|
||||
}
|
||||
|
||||
public function xmlnsToLocalName($xmlns)
|
||||
{
|
||||
return $this->_xmlns[$xmlns];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute is an xhtml boolean attribute.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isBooleanAttribute($att)
|
||||
{
|
||||
return in_array($att, self::$XHTML_BOOLEAN_ATTRIBUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute is in the phptal dictionnary.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPhpTalAttribute($att)
|
||||
{
|
||||
return array_key_exists(strtolower($att), $this->_dictionary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute is a valid phptal attribute or an unknown
|
||||
* attribute.
|
||||
*
|
||||
* Examples of valid attributes: tal:content, metal:use-slot
|
||||
* Examples of invalid attributes: tal:unknown, metal:content
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValidAttribute($att)
|
||||
{
|
||||
if (preg_match('/^(.*):(.*)$/', $att, $m)) {
|
||||
list (,$ns,$sub) = $m;
|
||||
if (array_key_exists(strtolower($ns), $this->_namespaces)
|
||||
&& !$this->isPhpTalAttribute($att)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute is a phptal handled xml namespace
|
||||
* declaration.
|
||||
*
|
||||
* Examples of handled xmlns: xmlns:tal, xmlns:metal
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isHandledXmlNs($att, $value)
|
||||
{
|
||||
$att = strtolower($att);
|
||||
return substr($att, 0, 6) == 'xmlns:'
|
||||
&& array_key_exists($value, $this->_xmlns);
|
||||
}
|
||||
|
||||
public function getNamespaceAttribute($attName)
|
||||
{
|
||||
return $this->_dictionary[strtolower($attName)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a PHPTAL_Namespace and its attribute into PHPTAL.
|
||||
*/
|
||||
public function registerNamespace(PHPTAL_Namespace $ns)
|
||||
{
|
||||
$nsname = strtolower($ns->name);
|
||||
$this->_namespaces[$nsname] = $ns;
|
||||
$this->_xmlns[$ns->xmlns] = $nsname;
|
||||
foreach ($ns->getAttributes() as $name => $attribute){
|
||||
$key = $nsname.':'.strtolower($name);
|
||||
$this->_dictionary[$key] = $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
private static $_instance = null;
|
||||
private $_dictionary;
|
||||
private $_namespaces;
|
||||
private $_xmlns;
|
||||
|
||||
/**
|
||||
* This array contains XHTML tags that must be echoed in a <tag/> form
|
||||
* instead of the <tag></tag> form.
|
||||
*
|
||||
* In fact, some browsers does not support the later form so PHPTAL
|
||||
* ensure these tags are correctly echoed.
|
||||
*/
|
||||
private static $XHTML_EMPTY_TAGS = array(
|
||||
'area',
|
||||
'base',
|
||||
'basefont',
|
||||
'br',
|
||||
'col',
|
||||
'frame',
|
||||
'hr',
|
||||
'img',
|
||||
'input',
|
||||
'isindex',
|
||||
'link',
|
||||
'meta',
|
||||
'param',
|
||||
);
|
||||
|
||||
/**
|
||||
* This array contains XHTML boolean attributes, their value is self
|
||||
* contained (ie: they are present or not).
|
||||
*/
|
||||
private static $XHTML_BOOLEAN_ATTRIBUTES = array(
|
||||
'checked',
|
||||
'compact',
|
||||
'declare',
|
||||
'defer',
|
||||
'disabled',
|
||||
'ismap',
|
||||
'multiple',
|
||||
'noresize',
|
||||
'noshade',
|
||||
'nowrap',
|
||||
'readonly',
|
||||
'selected',
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
254
PHPTAL/Dom/Node.php
Normal file
254
PHPTAL/Dom/Node.php
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
//
|
||||
// Copyright (c) 2004-2005 Laurent Bedubourg
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
//
|
||||
|
||||
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Php/CodeWriter.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
|
||||
|
||||
/**
|
||||
* Document node abstract class.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
abstract class PHPTAL_Dom_Node
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function setSource($file, $line)
|
||||
{
|
||||
$this->_file = $file;
|
||||
$this->_line = $line;
|
||||
}
|
||||
|
||||
public function getSourceLine()
|
||||
{
|
||||
return $this->_line;
|
||||
}
|
||||
|
||||
public function getSourceFile()
|
||||
{
|
||||
return $this->_file;
|
||||
}
|
||||
|
||||
private $_file;
|
||||
private $_line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Node container.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Dom_Tree extends PHPTAL_Dom_Node
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->_children = array();
|
||||
}
|
||||
|
||||
public function addChild(PHPTAL_Dom_Node $node)
|
||||
{
|
||||
array_push($this->_children, $node);
|
||||
}
|
||||
|
||||
public function &getChildren()
|
||||
{
|
||||
return $this->_children;
|
||||
}
|
||||
|
||||
public function clearChildren()
|
||||
{
|
||||
$this->_children = array();
|
||||
}
|
||||
|
||||
protected $_children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document Tag representation.
|
||||
*
|
||||
* This is the main class used by PHPTAL because TAL is a Template Attribute
|
||||
* Language, other Node kinds are (usefull) toys.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Dom_Element extends PHPTAL_Dom_Tree
|
||||
{
|
||||
private $name;
|
||||
public $attributes = array();
|
||||
|
||||
public function __construct($name, $attributes)
|
||||
{
|
||||
if (!preg_match('/^[a-z_:][a-z0-9._:\x80-\xff-]*$/i',$name)) throw new PHPTAL_Exception("Invalid element name '$name'");
|
||||
parent::__construct();
|
||||
$this->name = $name;
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
public function setXmlnsState(PHPTAL_Dom_XmlnsState $state)
|
||||
{
|
||||
$this->_xmlns = $state;
|
||||
$this->xmlns = $state;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getXmlnsState()
|
||||
{
|
||||
return $this->_xmlns;
|
||||
}
|
||||
|
||||
/** Returns true if the element contains specified PHPTAL attribute. */
|
||||
public function hasAttribute($name)
|
||||
{
|
||||
$ns = $this->getNodePrefix();
|
||||
foreach ($this->attributes as $key=>$value){
|
||||
if ($this->_xmlns->unAliasAttribute($key) == $name){
|
||||
return true;
|
||||
}
|
||||
if ($ns && $this->_xmlns->unAliasAttribute("$ns:$key") == $name){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the value of specified PHPTAL attribute. */
|
||||
public function getAttribute($name)
|
||||
{
|
||||
$ns = $this->getNodePrefix();
|
||||
|
||||
foreach ($this->attributes as $key=>$value){
|
||||
if ($this->_xmlns->unAliasAttribute($key) == $name){
|
||||
return $value;
|
||||
}
|
||||
if ($ns && $this->_xmlns->unAliasAttribute("$ns:$key") == $name){
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this element or one of its PHPTAL attributes has some
|
||||
* content to print (an empty text node child does not count).
|
||||
*/
|
||||
public function hasRealContent()
|
||||
{
|
||||
if (count($this->_children) == 0)
|
||||
return false;
|
||||
|
||||
if (count($this->_children) == 1){
|
||||
$child = $this->_children[0];
|
||||
if ($child instanceOf PHPTAL_Dom_Text && $child->getValue() == ''){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getNodePrefix()
|
||||
{
|
||||
$result = false;
|
||||
if (preg_match('/^(.*?):block$/', $this->name, $m)){
|
||||
list(,$result) = $m;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function hasContent()
|
||||
{
|
||||
return count($this->_children) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* XMLNS aliases propagated from parent nodes and defined by this node
|
||||
* attributes.
|
||||
*/
|
||||
protected $_xmlns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @package phptal.dom
|
||||
*/
|
||||
class PHPTAL_Dom_ValueNode extends PHPTAL_Dom_Node
|
||||
{
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->_value = $data;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->_value;
|
||||
}
|
||||
|
||||
private $_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document text data representation.
|
||||
* @package phptal.dom
|
||||
*/
|
||||
class PHPTAL_Dom_Text extends PHPTAL_Dom_ValueNode{}
|
||||
|
||||
/**
|
||||
* Preprocessor, etc... representation.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Dom_Specific extends PHPTAL_Dom_ValueNode {}
|
||||
|
||||
/**
|
||||
* Comment nodes.
|
||||
* @package phptal.dom
|
||||
*/
|
||||
class PHPTAL_Dom_Comment extends PHPTAL_Dom_ValueNode {}
|
||||
|
||||
/**
|
||||
* Document doctype representation.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Dom_Doctype extends PHPTAL_Dom_ValueNode {}
|
||||
|
||||
/**
|
||||
* XML declaration node.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Dom_XmlDeclaration extends PHPTAL_Dom_ValueNode {}
|
||||
|
||||
?>
|
||||
154
PHPTAL/Dom/Parser.php
Normal file
154
PHPTAL/Dom/Parser.php
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
//
|
||||
// Copyright (c) 2004-2005 Laurent Bedubourg
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
//
|
||||
|
||||
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Dom/Node.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Dom/XmlParser.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Dom/XmlnsState.php';
|
||||
require_once PHPTAL_DIR.'PHPTAL/Php/Tales.php';
|
||||
|
||||
/**
|
||||
* Template parser.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Dom_Parser extends PHPTAL_XmlParser
|
||||
{
|
||||
const ERR_DOCUMENT_END_STACK_NOT_EMPTY = "Not all elements were closed before end of the document (element stack not empty)";
|
||||
const ERR_UNSUPPORTED_ATTRIBUTE = "Unsupported attribute '%s'";
|
||||
const ERR_ELEMENT_CLOSE_MISMATCH = "Tag closure mismatch, expected '%s' but was '%s'";
|
||||
|
||||
public function __construct($input_encoding = 'UTF-8')
|
||||
{
|
||||
parent::__construct($input_encoding);
|
||||
$this->_xmlns = new PHPTAL_Dom_XmlnsState();
|
||||
}
|
||||
|
||||
public function getXmlnsState()
|
||||
{
|
||||
return $this->_xmlns;
|
||||
}
|
||||
|
||||
public function stripComments($b)
|
||||
{
|
||||
$this->_stripComments = $b;
|
||||
}
|
||||
|
||||
public function parseString($src, $filename = '<string>')
|
||||
{
|
||||
parent::parseString($src, $filename);
|
||||
return $this->_tree;
|
||||
}
|
||||
|
||||
public function parseFile($path)
|
||||
{
|
||||
parent::parseFile($path);
|
||||
return $this->_tree;
|
||||
}
|
||||
|
||||
// ~~~~~ XmlParser implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public function onDocumentStart()
|
||||
{
|
||||
$this->_tree = new PHPTAL_Dom_Tree();
|
||||
$this->_tree->setSource($this->getSourceFile(), $this->getLineNumber());
|
||||
$this->_stack = array();
|
||||
$this->_current = $this->_tree;
|
||||
}
|
||||
|
||||
public function onDocumentEnd()
|
||||
{
|
||||
if (count($this->_stack) > 0) {
|
||||
$this->raiseError(self::ERR_DOCUMENT_END_STACK_NOT_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
public function onDocType($doctype)
|
||||
{
|
||||
$this->pushNode(new PHPTAL_Dom_DocType($doctype));
|
||||
}
|
||||
|
||||
public function onXmlDecl($decl)
|
||||
{
|
||||
$this->pushNode(new PHPTAL_Dom_XmlDeclaration($decl));
|
||||
}
|
||||
|
||||
public function onComment($data)
|
||||
{
|
||||
if ($this->_stripComments)
|
||||
return;
|
||||
$this->pushNode(new PHPTAL_Dom_Comment($data));
|
||||
}
|
||||
|
||||
public function onSpecific($data)
|
||||
{
|
||||
$this->pushNode(new PHPTAL_Dom_Specific($data));
|
||||
}
|
||||
|
||||
public function onElementStart($name, $attributes)
|
||||
{
|
||||
$this->_xmlns = PHPTAL_Dom_XmlnsState::newElement($this->_xmlns, $attributes);
|
||||
|
||||
foreach ($attributes as $key=>$value) {
|
||||
if (!$this->_xmlns->isValidAttribute($key)) {
|
||||
$this->raiseError(self::ERR_UNSUPPORTED_ATTRIBUTE, $key);
|
||||
}
|
||||
}
|
||||
|
||||
$node = new PHPTAL_Dom_Element($name, $attributes);
|
||||
$node->setXmlnsState($this->getXmlnsState());
|
||||
$this->pushNode($node);
|
||||
array_push($this->_stack, $this->_current);
|
||||
$this->_current = $node;
|
||||
}
|
||||
|
||||
public function onElementData($data)
|
||||
{
|
||||
$this->pushNode(new PHPTAL_Dom_Text($data));
|
||||
}
|
||||
|
||||
public function onElementClose($name)
|
||||
{
|
||||
if (!$this->_current instanceof PHPTAL_Dom_Element) $this->raiseError("Found closing tag for '$name' where there are no open tags");
|
||||
if ($this->_current->getName() != $name) {
|
||||
$this->raiseError(self::ERR_ELEMENT_CLOSE_MISMATCH, $this->_current->getName(), $name);
|
||||
}
|
||||
$this->_current = array_pop($this->_stack);
|
||||
if ($this->_current instanceOf PHPTAL_Dom_Element)
|
||||
$this->_xmlns = $this->_current->getXmlnsState();
|
||||
}
|
||||
|
||||
private function pushNode(PHPTAL_Dom_Node $node)
|
||||
{
|
||||
$node->setSource($this->getSourceFile(), $this->getLineNumber());
|
||||
$this->_current->addChild($node);
|
||||
}
|
||||
|
||||
private $_tree; /* PHPTAL_Dom_Parser_NodeTree */
|
||||
private $_stack; /* array<PHPTAL_Dom_Parser_Node> */
|
||||
private $_current; /* PHPTAL_Dom_Parser_Node */
|
||||
private $_xmlns; /* PHPTAL_Dom_Parser_XmlnsState */
|
||||
private $_stripComments = false;
|
||||
}
|
||||
|
||||
?>
|
||||
386
PHPTAL/Dom/XmlParser.php
Normal file
386
PHPTAL/Dom/XmlParser.php
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
//
|
||||
// Copyright (c) 2004-2005 Laurent Bedubourg
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
//
|
||||
|
||||
/**
|
||||
* Simple sax like xml parser for PHPTAL.
|
||||
*
|
||||
* Because PHP Xml parser libraries tends to fail giving a real xml document
|
||||
* representation (at the time this file was created, it was impossible to
|
||||
* retrieve doctypes, xml declaration, problem with comments and CDATA) this
|
||||
* parser was created and can be manipulated to accept some user errors
|
||||
* like < and < in attribute values or inside text nodes.
|
||||
*
|
||||
* @package phptal
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
* @see PHPTAL_Dom_Parser
|
||||
*/
|
||||
abstract class PHPTAL_XmlParser
|
||||
{
|
||||
// available parser states
|
||||
const ST_ROOT = 0;
|
||||
const ST_TEXT = 1;
|
||||
const ST_LT = 2;
|
||||
const ST_TAG_NAME = 3;
|
||||
const ST_TAG_CLOSE = 4;
|
||||
const ST_TAG_SINGLE = 5;
|
||||
const ST_TAG_ATTRIBUTES = 6;
|
||||
const ST_CDATA = 7;
|
||||
const ST_COMMENT = 8;
|
||||
const ST_DOCTYPE = 9;
|
||||
const ST_XMLDEC = 15;
|
||||
const ST_PREPROC = 10;
|
||||
const ST_ATTR_KEY = 11;
|
||||
const ST_ATTR_EQ = 12;
|
||||
const ST_ATTR_QUOTE = 13;
|
||||
const ST_ATTR_VALUE = 14;
|
||||
|
||||
// exceptions error messages
|
||||
const ERR_CHARS_BEFORE_DOC_START =
|
||||
"Characters found before the begining of the document!";
|
||||
const ERR_EXPECT_VALUE_QUOTE =
|
||||
"Unexpected '%s' character, expecting attribute single or double quote";
|
||||
|
||||
const BOM_STR = "\xef\xbb\xbf";
|
||||
|
||||
|
||||
static $state_names = array(
|
||||
self::ST_ROOT => 'root node',
|
||||
self::ST_TEXT => 'text',
|
||||
self::ST_LT => 'start of tag',
|
||||
self::ST_TAG_NAME => 'tag name',
|
||||
self::ST_TAG_CLOSE => 'closing tag',
|
||||
self::ST_TAG_SINGLE => 'self-closing tag',
|
||||
self::ST_TAG_ATTRIBUTES => 'tag',
|
||||
self::ST_CDATA => 'CDATA',
|
||||
self::ST_COMMENT => 'comment',
|
||||
self::ST_DOCTYPE => 'doctype',
|
||||
self::ST_XMLDEC => 'XML declaration',
|
||||
self::ST_PREPROC => 'preprocessor directive',
|
||||
self::ST_ATTR_KEY => 'attribute name',
|
||||
self::ST_ATTR_EQ => 'attribute value',
|
||||
self::ST_ATTR_QUOTE => 'quoted attribute value',
|
||||
self::ST_ATTR_VALUE => 'unquoted attribute value',
|
||||
);
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->_file = "<string>";
|
||||
}
|
||||
|
||||
public function parseFile($src)
|
||||
{
|
||||
if (!file_exists($src)) {
|
||||
throw new PHPTAL_Exception("file $src not found");
|
||||
}
|
||||
$this->parseString(file_get_contents($src), $src);
|
||||
}
|
||||
|
||||
public function parseString($src, $filename = '<string>')
|
||||
{
|
||||
$this->_file = $filename;
|
||||
|
||||
// remove BOM (utf8 byte order mark)...
|
||||
if (substr($src,0,3) == self::BOM_STR){
|
||||
$src = substr($src, 3);
|
||||
}
|
||||
|
||||
$this->_line = 1;
|
||||
$state = self::ST_ROOT;
|
||||
$mark = 0;
|
||||
$len = strlen($src);
|
||||
|
||||
$quoteStyle = '"';
|
||||
$tagname = "";
|
||||
$attribute = "";
|
||||
$attributes = array();
|
||||
|
||||
$customDoctype = false;
|
||||
|
||||
$this->onDocumentStart();
|
||||
for ($i=0; $i<$len; $i++) {
|
||||
$c = $src[$i];
|
||||
|
||||
if ($c == "\n") $this->_line++;
|
||||
|
||||
switch ($state) {
|
||||
case self::ST_ROOT:
|
||||
if ($c == '<') {
|
||||
$mark = $i; // mark tag start
|
||||
$state = self::ST_LT;
|
||||
}
|
||||
else if (!self::isWhiteChar($c)) {
|
||||
$this->raiseError(self::ERR_CHARS_BEFORE_DOC_START);
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_TEXT:
|
||||
if ($c == '<') {
|
||||
if ($mark != $i) {
|
||||
$this->onElementData(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;
|
||||
}
|
||||
else if ($c == '?' and substr($src, $i, 4) == '?xml') {
|
||||
$state = self::ST_XMLDEC;
|
||||
}
|
||||
else if ($c == '?') {
|
||||
$state = self::ST_PREPROC;
|
||||
}
|
||||
else if ($c == '!' and substr($src, $i, 3) == '!--') {
|
||||
$state = self::ST_COMMENT;
|
||||
}
|
||||
else if ($c == '!' and substr($src, $i, 8) == '![CDATA[') {
|
||||
$state = self::ST_CDATA;
|
||||
}
|
||||
else if ($c == '!' and substr($src, $i, 8) == '!DOCTYPE') {
|
||||
$state = self::ST_DOCTYPE;
|
||||
}
|
||||
else if (!self::isAlpha($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)) {
|
||||
$tagname = substr($src, $mark, $i-$mark);
|
||||
$state = self::ST_TAG_ATTRIBUTES;
|
||||
}
|
||||
else if ($c == '/') {
|
||||
$tagname = substr($src, $mark, $i-$mark);
|
||||
$state = self::ST_TAG_SINGLE;
|
||||
}
|
||||
else if ($c == '>') {
|
||||
$tagname = substr($src, $mark, $i-$mark);
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
$this->onElementStart($tagname, $attributes);
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_TAG_CLOSE:
|
||||
if ($c == '>') {
|
||||
$tagname = rtrim(substr($src, $mark, $i-$mark));
|
||||
$this->onElementClose($tagname);
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_TAG_SINGLE:
|
||||
if ($c != '>') {
|
||||
// error
|
||||
}
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
$this->onElementStart($tagname, $attributes);
|
||||
$this->onElementClose($tagname);
|
||||
break;
|
||||
|
||||
case self::ST_TAG_ATTRIBUTES:
|
||||
if ($c == '>') {
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
$this->onElementStart($tagname, $attributes);
|
||||
}
|
||||
else if ($c == '/') {
|
||||
$state = self::ST_TAG_SINGLE;
|
||||
}
|
||||
else if (self::isWhiteChar($c)) {
|
||||
}
|
||||
else {
|
||||
$mark = $i; // mark attribute key start
|
||||
$state = self::ST_ATTR_KEY;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_COMMENT:
|
||||
if ($c == '>' and substr($src, $i-2, 2) == '--') {
|
||||
$this->onComment(substr($src, $mark, $i-$mark+1));
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_CDATA:
|
||||
if ($c == '>' and substr($src, $i-2, 2) == ']]') {
|
||||
$this->onSpecific(substr($src, $mark, $i-$mark+1));
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_XMLDEC:
|
||||
if ($c == '?' && substr($src, $i, 2) == '?>') {
|
||||
$this->onXmlDecl(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;
|
||||
}
|
||||
else if ($customDoctype && $c == '>' && substr($src, $i-1, 2) == ']>'){
|
||||
$customDoctype = false;
|
||||
$this->onDocType(substr($src, $mark, $i-$mark+1));
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
}
|
||||
else if (!$customDoctype && $c == '>') {
|
||||
$customDoctype = false;
|
||||
$this->onDocType(substr($src, $mark, $i-$mark+1));
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_PREPROC:
|
||||
if ($c == '>' and $src[$i-1] == '?') {
|
||||
$this->onSpecific(substr($src, $mark, $i-$mark+1));
|
||||
$mark = $i+1; // mark text start
|
||||
$state = self::ST_TEXT;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_ATTR_KEY:
|
||||
if (self::isWhiteChar($c)) {
|
||||
$attribute = substr($src, $mark, $i-$mark);
|
||||
$state = self::ST_ATTR_EQ;
|
||||
}
|
||||
else if ($c == '=') {
|
||||
$attribute = substr($src, $mark, $i-$mark);
|
||||
$state = self::ST_ATTR_VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_ATTR_EQ:
|
||||
if ($c == '=') {
|
||||
$state = self::ST_ATTR_VALUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_ATTR_VALUE:
|
||||
if (self::isWhiteChar($c)){
|
||||
}
|
||||
else if ($c == '"' or $c == '\'') {
|
||||
$quoteStyle = $c;
|
||||
$state = self::ST_ATTR_QUOTE;
|
||||
$mark = $i+1; // mark attribute real value start
|
||||
}
|
||||
else {
|
||||
$err = self::ERR_EXPECT_VALUE_QUOTE;
|
||||
$err = sprintf($err, $c);
|
||||
$this->raiseError($err);
|
||||
}
|
||||
break;
|
||||
|
||||
case self::ST_ATTR_QUOTE:
|
||||
if ($c == $quoteStyle) {
|
||||
if (isset($attributes[$attribute])) $this->raiseError("Attribute '$attribute' on '$tagname' is defined more than once");
|
||||
$attributes[$attribute] = substr($src, $mark, $i-$mark);
|
||||
$state = self::ST_TAG_ATTRIBUTES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($state == self::ST_TEXT) // allows text past root node, which is in violation of XML spec
|
||||
{
|
||||
if ($i > $mark)
|
||||
{
|
||||
$text = substr($src, $mark, $i-$mark);
|
||||
//if (!ctype_space($text)) $this->onElementData($text);
|
||||
if (!ctype_space($text)) $this->raiseError("Characters found after end of the root element");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PHPTAL_Exception("Finished document in unexpected state: ".self::$state_names[$state]." is not finished");
|
||||
}
|
||||
|
||||
$this->onDocumentEnd();
|
||||
}
|
||||
|
||||
public function getSourceFile()
|
||||
{
|
||||
return $this->_file;
|
||||
}
|
||||
|
||||
public function getLineNumber()
|
||||
{
|
||||
return $this->_line;
|
||||
}
|
||||
|
||||
public static function isWhiteChar($c)
|
||||
{
|
||||
return strpos(" \t\n\r\0", $c) !== false;
|
||||
}
|
||||
|
||||
public static function isAlpha($c)
|
||||
{
|
||||
$char = strtolower($c);
|
||||
return ($char >= 'a' && $char <= 'z');
|
||||
}
|
||||
|
||||
public abstract function onDocType($doctype);
|
||||
public abstract function onXmlDecl($decl);
|
||||
public abstract function onSpecific($data);
|
||||
public abstract function onComment($data);
|
||||
public abstract function onElementStart($name, $attributes);
|
||||
public abstract function onElementClose($name);
|
||||
public abstract function onElementData($data);
|
||||
public abstract function onDocumentStart();
|
||||
public abstract function onDocumentEnd();
|
||||
|
||||
protected function raiseError($errFmt)
|
||||
{
|
||||
$args = func_get_args();
|
||||
$errStr = call_user_func_array('sprintf', $args);
|
||||
|
||||
$str = "%s error: %s in %s:%d";
|
||||
$str = sprintf($str, get_class($this), $errStr, $this->_file, $this->_line);
|
||||
throw new PHPTAL_Exception($str);
|
||||
}
|
||||
|
||||
private $_file;
|
||||
private $_line;
|
||||
private $_source;
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
98
PHPTAL/Dom/XmlnsState.php
Normal file
98
PHPTAL/Dom/XmlnsState.php
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
//
|
||||
// Copyright (c) 2004-2005 Laurent Bedubourg
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
//
|
||||
|
||||
/**
|
||||
* Stores XMLNS aliases fluctuation in the xml flow.
|
||||
*
|
||||
* This class is used to bind a PHPTAL namespace to an alias, for example using
|
||||
* xmlns:t="http://xml.zope.org/namespaces/tal" and later use t:repeat instead
|
||||
* of tal:repeat.
|
||||
*
|
||||
* @package phptal.dom
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Dom_XmlnsState
|
||||
{
|
||||
/** Create a new XMLNS state inheriting provided aliases. */
|
||||
public function __construct($aliases = array())
|
||||
{
|
||||
assert(is_array($aliases));
|
||||
$this->_aliases = $aliases;
|
||||
}
|
||||
|
||||
/** Returns true if $attName is a valid attribute name, false otherwise. */
|
||||
public function isValidAttribute($attName)
|
||||
{
|
||||
$unaliased = $this->unAliasAttribute($attName);
|
||||
return PHPTAL_Dom_Defs::getInstance()->isValidAttribute($unaliased);
|
||||
}
|
||||
|
||||
/** Returns true if $attName is a PHPTAL attribute, false otherwise. */
|
||||
public function isPhpTalAttribute($attName)
|
||||
{
|
||||
$unaliased = $this->unAliasAttribute($attName);
|
||||
return PHPTAL_Dom_Defs::getInstance()->isPhpTalAttribute($unaliased);
|
||||
}
|
||||
|
||||
/** Returns the unaliased name of specified attribute. */
|
||||
public function unAliasAttribute($attName)
|
||||
{
|
||||
if (count($this->_aliases) == 0)
|
||||
return $attName;
|
||||
|
||||
$result = $attName;
|
||||
foreach ($this->_aliases as $alias => $real){
|
||||
$result = str_replace("$alias:", "$real:", $result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new XmlnsState inheriting of $currentState if $nodeAttributes contains
|
||||
* xmlns attributes, returns $currentState otherwise.
|
||||
*
|
||||
* This method is used by the PHPTAL parser to keep track of xmlns fluctuation for
|
||||
* each encountered node.
|
||||
*/
|
||||
public static function newElement(PHPTAL_Dom_XmlnsState $currentState, $nodeAttributes)
|
||||
{
|
||||
$aliases = array();
|
||||
foreach ($nodeAttributes as $att => $value){
|
||||
if (PHPTAL_Dom_Defs::getInstance()->isHandledXmlNs($att, $value)){
|
||||
preg_match('/^xmlns:(.*?)$/', $att, $m);
|
||||
list(,$alias) = $m;
|
||||
$aliases[$alias] = PHPTAL_Dom_Defs::getInstance()->xmlnsToLocalName($value);
|
||||
}
|
||||
}
|
||||
if (count($aliases) > 0){
|
||||
// inherit aliases with maybe an overwrite
|
||||
$aliases = array_merge($currentState->_aliases, $aliases);
|
||||
return new PHPTAL_Dom_XmlnsState($aliases);
|
||||
}
|
||||
return $currentState;
|
||||
}
|
||||
|
||||
private $_aliases;
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
Loading…
Add table
Add a link
Reference in a new issue