XML/Tree.php
authorAlan Knowles <alan@roojs.com>
Thu, 20 Oct 2016 03:19:56 +0000 (11:19 +0800)
committerAlan Knowles <alan@roojs.com>
Thu, 20 Oct 2016 03:19:56 +0000 (11:19 +0800)
XML/Tree.php [new file with mode: 0644]

diff --git a/XML/Tree.php b/XML/Tree.php
new file mode 100644 (file)
index 0000000..2328371
--- /dev/null
@@ -0,0 +1,471 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PEAR :: XML_Tree                                                     |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license,      |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/2_02.txt.                                 |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Bernd Römer <berndr@bonn.edu>                               |
+// |          Sebastian Bergmann <sb@sebastian-bergmann.de>               |
+// |          Tomas V.V.Cox <cox@idecnet.com>                             |
+// |          Michele Manzato <michele.manzato@verona.miz.it>             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Tree.php,v 1.31 2003/09/18 10:51:34 cox Exp $
+//
+
+require_once 'XML/Parser.php';
+require_once 'XML/Tree/Node.php';
+
+/**
+* PEAR::XML_Tree
+*
+* Purpose
+*
+*    Allows for the building of XML data structures
+*    using a tree representation, without the need
+*    for an extension like DOMXML.
+*
+* Example
+*
+*    $tree  = new XML_Tree;
+*    $root =& $tree->addRoot('root');
+*    $foo  =& $root->addChild('foo');
+*
+*    $tree->dump(true);
+*
+* @author  Bernd Römer <berndr@bonn.edu>
+* @package XML
+* @version $Version$ - 1.0
+*/
+class XML_Tree extends XML_Parser
+{
+    /**
+    * File Handle
+    *
+    * @var  resource
+    */
+    var $file = NULL;
+
+    /**
+    * Filename from which the XML_Tree was read
+    *
+    * @var  string
+    */
+    var $filename = '';
+
+    /**
+    * Namespace
+    *
+    * @var  array
+    */
+    var $namespace = array();
+
+    /**
+    * Root node of the XML tree
+    *
+    * @var  object XML_Tree_Node
+    */
+    var $root = NULL;
+
+    /**
+    * XML Version
+    *
+    * @var  string
+    */
+    var $version = '1.0';
+
+    /**
+    * Constructor
+    *
+    * @param  string  filename  Filename where to read the XML
+    * @param  string  version   XML Version to apply
+    */
+    function XML_Tree($filename = '', $version = '1.0')
+    {
+        $this->filename = $filename;
+        $this->version  = $version;
+    }
+
+    /**
+    * Gets the root node
+    *
+    * @return object    Root XML_Tree_Node, or PEAR_Error if there isn't any root node.
+    *
+    * @access public
+    */
+    function &getRoot()
+    {
+        if (!is_null($this->root)) {
+            return $this->root;
+        }
+        return $this->raiseError("No root");
+    }
+
+    /**
+    * Sets the root node of the XML tree.
+    *
+    * @param  string    name        Name of root element
+    *
+    * @return object XML_Tree_Node   Reference to the newly created root node
+    * @access public
+    */
+    function &addRoot($name, $content = '', $attributes = array(), $lineno = null)
+    {
+        $this->root = new XML_Tree_Node($name, $content, $attributes, $lineno);
+        return $this->root;
+    }
+
+    /**
+    * Inserts a child/tree (child) into tree ($path,$pos) and maintains
+    * namespace integrity
+    *
+    * @param mixed      path            Path to parent node to add child (see
+    *                                   getNodeAt() for format)
+    * @param integer    pos             Position where to insert the new child.
+    *                                   0 < means |$pos| elements before the end,
+    *                                   e.g. -1 appends as last child.
+    * @param mixed      child           Child to insert (XML_Tree or XML_Tree_Node),
+    *                                   or name of child node
+    * @param string     content         Content (text) for the new node (only if
+    *                                   $child is the node name)
+    * @param array      attributes      Attribute-hash for new node
+    *
+    * @return object Reference to the inserted child (node), or PEAR_Error upon error
+    * @access public
+    * @see getNodeAt()
+    */
+    function &insertChild($path, $pos, $child, $content = '', $attributes = array())
+    {
+        $parent =& $this->getNodeAt($path);
+        if (PEAR::isError($parent)) {
+            return $parent;
+        }
+
+        $x =& $parent->insertChild(null, $pos, $child, $content, $attributes);
+
+        if (!PEAR::isError($x)) {
+        // update namespace to maintain namespace integrity
+            $count = count($path);
+            foreach ($this->namespace as $key => $val) {
+                if ((array_slice($val,0,$count)==$path) && ($val[$count]>=$pos)) {
+                    $this->namespace[$key][$count]++;
+                }
+            }
+        }
+        return $x;
+    }
+
+    /*
+    * Removes a child node from tree and maintains namespace integrity
+    *
+    * @param array      path        Path to the parent of child to remove (see
+    *                               getNodeAt() for format)
+    * @param integer    pos         Position of child in parent children-list
+    *                               0 < means |$pos| elements before the end,
+    *                               e.g. -1 removes the last child.
+    *
+    * @return object    Parent XML_Tree_Node whose child was removed, or PEAR_Error upon error
+    * @access public
+    * @see getNodeAt()
+    */
+    function &removeChild($path, $pos)
+    {
+        $parent =& $this->getNodeAt($path);
+        if (PEAR::isError($parent)) {
+            return $parent;
+        }
+
+        $x =& $parent->removeChild($pos);
+
+        if (!PEAR::isError($x)) {
+            // Update namespace to maintain namespace integrity
+            $count=count($path);
+            foreach($this->namespace as $key => $val) {
+                if (array_slice($val,0,$count)==$path) {
+                    if ($val[$count]==$pos) {
+                        unset($this->namespace[$key]); break;
+                    }
+                    if ($val[$count]>$pos) {
+                        $this->namespace[$key][$count]--;
+                    }
+                }
+            }
+        }
+
+        return $x;
+    }
+
+    /*
+    * Maps a XML file to a XML_Tree
+    *
+    * @return mixed The XML tree root (an XML_Tree_Node), or PEAR_Error upon error.
+    * @access public
+    */
+    function &getTreeFromFile ()
+    {
+        $this->folding = false;
+        $this->XML_Parser(null, 'event');
+        $err = $this->setInputFile($this->filename);
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+        $this->cdata = null;
+        $err = $this->parse();
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+        return $this->root;
+    }
+
+    /*
+    * Maps an XML string to an XML_Tree.
+    *
+    * @return mixed The XML tree root (an XML_Tree_Node), or PEAR_Error upon error.
+    * @access public
+    */
+    function &getTreeFromString($str)
+    {
+        $this->folding = false;
+        $this->XML_Parser(null, 'event');
+        $this->cdata = null;
+        $err = $this->parseString($str);
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+        return $this->root;
+    }
+
+    /**
+    * Handler for the xml-data
+    * Used by XML_Parser::XML_Parser() when parsing an XML stream.
+    *
+    * @param mixed  xp          ignored
+    * @param string elem        name of the element
+    * @param array  attribs     attributes for the generated node
+    *
+    * @access private
+    */
+    function startHandler($xp, $elem, &$attribs)
+    {
+        $lineno = xml_get_current_line_number($xp);
+        // root elem
+        if (!isset($this->i)) {
+            $this->obj1 =& $this->addRoot($elem, null, $attribs, $lineno);
+            $this->i = 2;
+        } else {
+            // mixed contents
+            if (!empty($this->cdata)) {
+                $parent_id = 'obj' . ($this->i - 1);
+                $parent    =& $this->$parent_id;
+                $parent->children[] = new XML_Tree_Node(null, $this->cdata, null, $lineno);
+            }
+            $obj_id = 'obj' . $this->i++;
+            $this->$obj_id = new XML_Tree_Node($elem, null, $attribs, $lineno);
+        }
+        $this->cdata = null;
+        return null;
+    }
+
+    /**
+    * Handler for the xml-data
+    * Used by XML_Parser::XML_Parser() when parsing an XML stream.
+    *
+    * @param mixed  xp          ignored
+    * @param string elem        name of the element
+    *
+    * @access private
+    */
+    function endHandler($xp, $elem)
+    {
+        $this->i--;
+        if ($this->i > 1) {
+            $obj_id = 'obj' . $this->i;
+            // recover the node created in StartHandler
+            $node   =& $this->$obj_id;
+            // mixed contents
+            if (count($node->children) > 0) {
+                if (trim($this->cdata)) {
+                    $node->children[] = new XML_Tree_Node(null, $this->cdata);
+                }
+            } else {
+                $node->setContent($this->cdata);
+            }
+            $parent_id = 'obj' . ($this->i - 1);
+            $parent    =& $this->$parent_id;
+            // attach the node to its parent node children array
+            $parent->children[] = $node;
+        }
+        $this->cdata = null;
+        return null;
+    }
+
+    /*
+    * The xml character data handler
+    * Used by XML_Parser::XML_Parser() when parsing an XML stream.
+    *
+    * @param mixed  xp          ignored
+    * @param string data        PCDATA between tags
+    *
+    * @access private
+    */
+    function cdataHandler($xp, $data)
+    {
+        if (trim($data) != '') {
+            $this->cdata .= $data;
+        }
+    }
+
+    /**
+    * Get a copy of this tree by cloning and all of its nodes, recursively.
+    *
+    * @return object XML_Tree copy of this node.
+    * @access public
+    */
+    function cloneTree()
+    {
+        $clone = new XML_Tree($this->filename, $this->version);
+        if (!is_null($this->root)) {
+            $clone->root = $this->root->cloneNode();
+        }
+
+        // clone all other vars
+        $temp = get_object_vars($this);
+        foreach($temp as $varname => $value) {
+            if (!in_array($varname,array('filename','version','root'))) {
+                $clone->$varname=$value;
+            }
+        }
+        return $clone;
+    }
+
+    /**
+    * Print text representation of XML tree.
+    *
+    * @param bool xmlHeader     if true then generate also the leading XML
+    *                           'Content-type' header directive, e.g. for
+    *                           direct output to a web page.
+    *
+    * @access public
+    */
+    function dump($xmlHeader = false)
+    {
+        if ($xmlHeader) {
+            header('Content-type: text/xml');
+        }
+        echo $this->get();
+    }
+
+    /**
+    * Get text representation of XML tree.
+    *
+    * @return string  Text (XML) representation of the tree
+    * @access public
+    */
+    function &get()
+    {
+        $out = '<?xml version="' . $this->version . "\"?>\n";
+        if (!is_null($this->root))
+            {
+            if(!is_object($this->root) || (get_class($this->root) != 'xml_tree_node'))
+                return $this->raiseError("Bad XML root node");
+            $out .= $this->root->get();
+        }
+        return $out;
+    }
+
+    /**
+    * Get current namespace.
+    *
+    * @param  string  name  namespace
+    * @return string
+    *
+    * @access public
+    */
+    function &getName($name) {
+        return $this->root->getElement($this->namespace[$name]);
+    }
+
+    /**
+    * Register a namespace.
+    *
+    * @param  string  $name namespace
+    * @param  string  $path path
+    *
+    * @access public
+    */
+    function registerName($name, $path) {
+        $this->namespace[$name] = $path;
+    }
+
+    /**
+    * Get a reference to a node. Node is searched by its 'path'.
+    *
+    * @param mixed  path  Path to node. Can be either a string (slash-separated
+    *                     children names) or an array (sequence of children names) both
+    *                     of them starting from node. Note that the first name in sequence
+    *                     must be the name of the document root.
+    * @return object    Reference to the XML_Tree_Node found, or PEAR_Error if
+    *                   the path does not exist. If more than one element matches
+    *                   then only the first match is returned.
+    * @access public
+    */
+    function &getNodeAt($path)
+    {
+        if (is_null($this->root)){
+            return $this->raiseError("XML_Tree hasn't a root node");
+        }
+        if (is_string($path))
+            $path = explode("/", $path);
+        if (sizeof($path) == 0) {
+            return $this->raiseError("Path to node is empty");
+        }
+        $path1 = $path;
+        $rootName = array_shift($path1);
+        if ($this->root->name != $rootName) {
+            return $this->raiseError("Path does not match the document root");
+        }
+        $x =& $this->root->getNodeAt($path1);
+        if (!PEAR::isError($x)) {
+            return $x;
+        }
+        // No node with that name found
+        return $this->raiseError("Bad path to node: [".implode('/', $path)."]");
+    }
+
+    /**
+    * Gets all children that match a given tag name.
+    *
+    * @param  string    Tag name
+    *
+    * @return array     An array of Node objects of the children found,
+    *                   an empty array if none
+    * @access public
+    * @author Pierre-Alain Joye <paj@pearfr.org>
+    */
+    function &getElementsByTagName($tagName)
+    {
+        if (empty($tagName)) {
+            return $this->raiseError('Empty tag name');
+        }
+        if (sizeof($this->children)==0) {
+            return null;
+        }
+        $result = array();
+        foreach ($this->children as $child) {
+            if ($child->name == $tagName) {
+                $result[] = $child;
+            }
+        }
+        return $result;
+    }
+}
+?>