XML
[ class tree: XML ] [ index: XML ] [ all elements ]

Source for file Tree.php

Documentation is available at Tree.php

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PEAR :: XML_Tree                                                     |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // |          Sebastian Bergmann <sb@sebastian-bergmann.de>               |
  17. // |          Tomas V.V.Cox <cox@idecnet.com>                             |
  18. // |          Michele Manzato <michele.manzato@verona.miz.it>             |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: Tree.php,v 1.34 2004/05/25 15:47:42 davey Exp $
  22. //
  23.  
  24. require_once 'XML/Parser.php';
  25. require_once 'XML/Tree/Node.php';
  26.  
  27. /**
  28.  * PEAR::XML_Tree
  29.  *
  30.  * Purpose
  31.  *
  32.  *    Allows for the building of XML data structures
  33.  *    using a tree representation, without the need
  34.  *    for an extension like DOMXML.
  35.  *
  36.  * Example
  37.  *
  38.  *    $tree  = new XML_Tree;
  39.  *    $root =& $tree->addRoot('root');
  40.  *    $foo  =& $root->addChild('foo');
  41.  *
  42.  *    $tree->dump(true);
  43.  *
  44.  * @author
  45.  * @package XML
  46.  * @version $Version$ - 1.0
  47.  */
  48. class XML_Tree extends XML_Parser
  49. {
  50.     /**
  51.      * File Handle
  52.      *
  53.      * @var  resource 
  54.      */
  55.     var $file = NULL;
  56.  
  57.     /**
  58.      * Filename from which the XML_Tree was read
  59.      *
  60.      * @var  string 
  61.      */
  62.     var $filename = '';
  63.  
  64.     /**
  65.      * Namespace
  66.      *
  67.      * @var  array 
  68.      */
  69.     var $namespace = array();
  70.  
  71.     /**
  72.      * Root node of the XML tree
  73.      *
  74.      * @var  object XML_Tree_Node 
  75.      */
  76.     var $root = NULL;
  77.  
  78.     /**
  79.      * XML Version
  80.      *
  81.      * @var  string 
  82.      */
  83.     var $version = '1.0';
  84.  
  85.     /**
  86.      * Whether to encapsulate the all CDATA in a <![CDATA[]]> section
  87.      *
  88.      * @var boolean 
  89.      */
  90.  
  91.     var $use_cdata_sections = false;
  92.  
  93.     /**
  94.      * Constructor
  95.      *
  96.      * @param  string  filename  Filename where to read the XML
  97.      * @param  string  version   XML Version to apply
  98.      */
  99.     function XML_Tree($filename ''$version '1.0')
  100.     {
  101.         $this->filename = $filename;
  102.         $this->version  = $version;
  103.     }
  104.  
  105.     /**
  106.      * Use <![CDATA[]]> for all CDATA sections
  107.      *
  108.      * @return void 
  109.      */
  110.  
  111.     function useCdataSections()
  112.     {
  113.         $this->use_cdata_sections = true;
  114.     }
  115.  
  116.     /**
  117.      * Gets the root node
  118.      *
  119.      * @return object    Root XML_Tree_Node, or PEAR_Error if there isn't any root node.
  120.      *
  121.      * @access public
  122.      */
  123.     function &getRoot()
  124.     {
  125.         if (!is_null($this->root)) {
  126.             return $this->root;
  127.         }
  128.         return $this->raiseError("No root");
  129.     }
  130.  
  131.     /**
  132.      * Sets the root node of the XML tree.
  133.      *
  134.      * @param  string    name        Name of root element
  135.      *
  136.      * @return object XML_Tree_Node   Reference to the newly created root node
  137.      * @access public
  138.      */
  139.     function &addRoot($name$content ''$attributes = array()$lineno = null)
  140.     {
  141.         $this->root = new XML_Tree_Node($name$content$attributes$lineno);
  142.         return $this->root;
  143.     }
  144.  
  145.     /**
  146.      * Inserts a child/tree (child) into tree ($path,$pos) and maintains
  147.      * namespace integrity
  148.      *
  149.      * @param mixed      path            Path to parent node to add child (see
  150.      *                                    getNodeAt() for format)
  151.      * @param integer    pos             Position where to insert the new child.
  152.      *                                    0 < means |$pos| elements before the end,
  153.      *                                    e.g. -1 appends as last child.
  154.      * @param mixed      child           Child to insert (XML_Tree or XML_Tree_Node),
  155.      *                                    or name of child node
  156.      * @param string     content         Content (text) for the new node (only if
  157.      *                                    $child is the node name)
  158.      * @param array      attributes      Attribute-hash for new node
  159.      *
  160.      * @return object Reference to the inserted child (node), or PEAR_Error upon error
  161.      * @access public
  162.      * @see getNodeAt()
  163.      */
  164.     function &insertChild($path$pos$child$content ''$attributes = array())
  165.     {
  166.         $parent =$this->getNodeAt($path);
  167.         if (PEAR::isError($parent)) {
  168.             return $parent;
  169.         }
  170.  
  171.         $x =$parent->insertChild(null$pos$child$content$attributes);
  172.  
  173.         if (!PEAR::isError($x)) {
  174.         // update namespace to maintain namespace integrity
  175.             $count count($path);
  176.             foreach ($this->namespace as $key => $val{
  177.                 if ((array_slice($val,0,$count)==$path&& ($val[$count]>=$pos)) {
  178.                     $this->namespace[$key][$count]++;
  179.                 }
  180.             }
  181.         }
  182.         return $x;
  183.     }
  184.  
  185.     /**
  186.      * Removes a child node from tree and maintains namespace integrity
  187.      *
  188.      * @param array      path        Path to the parent of child to remove (see
  189.      *                                getNodeAt() for format)
  190.      * @param integer    pos         Position of child in parent children-list
  191.      *                                0 < means |$pos| elements before the end,
  192.      *                                e.g. -1 removes the last child.
  193.      *
  194.      * @return object    Parent XML_Tree_Node whose child was removed, or PEAR_Error upon error
  195.      * @access public
  196.      * @see getNodeAt()
  197.      */
  198.     function &removeChild($path$pos)
  199.     {
  200.         $parent =$this->getNodeAt($path);
  201.         if (PEAR::isError($parent)) {
  202.             return $parent;
  203.         }
  204.  
  205.         $x =$parent->removeChild($pos);
  206.  
  207.         if (!PEAR::isError($x)) {
  208.             // Update namespace to maintain namespace integrity
  209.             $count=count($path);
  210.             foreach($this->namespace as $key => $val{
  211.                 if (array_slice($val,0,$count)==$path{
  212.                     if ($val[$count]==$pos{
  213.                         unset($this->namespace[$key]); break;
  214.                     }
  215.                     if ($val[$count]>$pos{
  216.                         $this->namespace[$key][$count]--;
  217.                     }
  218.                 }
  219.             }
  220.         }
  221.  
  222.         return $x;
  223.     }
  224.  
  225.     /**
  226.      * Maps a XML file to a XML_Tree
  227.      *
  228.      * @return mixed The XML tree root (an XML_Tree_Node), or PEAR_Error upon error.
  229.      * @access public
  230.      */
  231.     function &getTreeFromFile ()
  232.     {
  233.         $this->folding = false;
  234.         $this->XML_Parser(null'event');
  235.         $err $this->setInputFile($this->filename);
  236.         if (PEAR::isError($err)) {
  237.             return $err;
  238.         }
  239.         $this->cdata = null;
  240.         $err $this->parse();
  241.         if (PEAR::isError($err)) {
  242.             return $err;
  243.         }
  244.         return $this->root;
  245.     }
  246.  
  247.     /**
  248.      * Maps an XML string to an XML_Tree.
  249.      *
  250.      * @return mixed The XML tree root (an XML_Tree_Node), or PEAR_Error upon error.
  251.      * @access public
  252.      */
  253.     function &getTreeFromString($str)
  254.     {
  255.         $this->= null;
  256.         $this->folding = false;
  257.         $this->XML_Parser(null'event');
  258.         $this->cdata = null;
  259.         $err $this->parseString($str);
  260.         if (PEAR::isError($err)) {
  261.             return $err;
  262.         }
  263.         return $this->root;
  264.     }
  265.  
  266.     /**
  267.      * Handler for the xml-data
  268.      * Used by XML_Parser::XML_Parser() when parsing an XML stream.
  269.      *
  270.      * @param mixed  xp          ignored
  271.      * @param string elem        name of the element
  272.      * @param array  attribs     attributes for the generated node
  273.      *
  274.      * @access private
  275.      */
  276.     function startHandler($xp$elem&$attribs)
  277.     {
  278.         $lineno xml_get_current_line_number($xp);
  279.         // root elem
  280.         if (!isset($this->i)) {
  281.             $this->obj1 =$this->addRoot($elemnull$attribs$lineno);
  282.             $this->= 2;
  283.         else {
  284.             // mixed contents
  285.             if (!empty($this->cdata)) {
  286.                 $parent_id 'obj' ($this->- 1);
  287.                 $parent    =$this->$parent_id;
  288.                 $parent->children[&new XML_Tree_Node(null$this->cdatanull$lineno);
  289.             }
  290.             $obj_id 'obj' $this->i++;
  291.             $this->$obj_id &new XML_Tree_Node($elemnull$attribs$lineno);
  292.         }
  293.         $this->cdata = null;
  294.         return null;
  295.     }
  296.  
  297.     /**
  298.      * Handler for the xml-data
  299.      * Used by XML_Parser::XML_Parser() when parsing an XML stream.
  300.      *
  301.      * @param mixed  xp          ignored
  302.      * @param string elem        name of the element
  303.      *
  304.      * @access private
  305.      */
  306.     function endHandler($xp$elem)
  307.     {
  308.         $this->i--;
  309.         if ($this->> 1{
  310.             $obj_id 'obj' $this->i;
  311.             // recover the node created in StartHandler
  312.             $node   =$this->$obj_id;
  313.             // mixed contents
  314.             if (count($node->children> 0{
  315.                 if (trim($this->cdata!= ''{
  316.                     $node->children[&new XML_Tree_Node(null$this->cdata);
  317.                 }
  318.             else {
  319.                 $node->setContent($this->cdata);
  320.             }
  321.             $parent_id 'obj' ($this->- 1);
  322.             $parent    =$this->$parent_id;
  323.             // attach the node to its parent node children array
  324.             $parent->children[$node;
  325.         else {
  326.             $node =$this->obj1;
  327.             if (count($node->children> 0{
  328.                 if (trim($this->cdata)) {
  329.                     $node->children[&new XML_Tree_Node(null$this->cdata);
  330.                 }
  331.             else {
  332.                 $node->setContent($this->cdata);
  333.             }
  334.         }
  335.         $this->cdata = null;
  336.         return null;
  337.     }
  338.  
  339.     /**
  340.      * The xml character data handler
  341.      * Used by XML_Parser::XML_Parser() when parsing an XML stream.
  342.      *
  343.      * @param mixed  xp          ignored
  344.      * @param string data        PCDATA between tags
  345.      *
  346.      * @access private
  347.      */
  348.     function cdataHandler($xp$data)
  349.     {
  350.         $this->cdata .= $data;
  351.     }
  352.  
  353.     /**
  354.      * Get a copy of this tree by cloning and all of its nodes, recursively.
  355.      *
  356.      * @return object XML_Tree copy of this node.
  357.      * @access public
  358.      */
  359.     function cloneTree()
  360.     {
  361.         $clone = new XML_Tree($this->filename$this->version);
  362.         if (!is_null($this->root)) {
  363.             $clone->root = $this->root->cloneTree();
  364.         }
  365.  
  366.         // clone all other vars
  367.         $temp get_object_vars($this);
  368.         foreach($temp as $varname => $value{
  369.             if (!in_array($varname,array('filename','version','root'))) {
  370.                 $clone->$varname=$value;
  371.             }
  372.         }
  373.         return $clone;
  374.     }
  375.  
  376.     /**
  377.      * Print text representation of XML tree.
  378.      *
  379.      * @param bool xmlHeader     if true then generate also the leading XML
  380.      *                            'Content-type' header directive, e.g. for
  381.      *                            direct output to a web page.
  382.      *
  383.      * @access public
  384.      */
  385.     function dump($xmlHeader = false)
  386.     {
  387.         if ($xmlHeader{
  388.             header('Content-type: text/xml');
  389.         }
  390.         echo $this->get($this->use_cdata_sections);
  391.     }
  392.  
  393.     /**
  394.      * Get text representation of XML tree.
  395.      *
  396.      * @return string  Text (XML) representation of the tree
  397.      * @access public
  398.      */
  399.     function &get()
  400.     {
  401.         $out '<?xml version="' $this->version . "\"?>\n";
  402.  
  403.         if (!is_null($this->root))
  404.         {
  405.             if(!is_object($this->root|| (strtolower(get_class($this->root)) != 'xml_tree_node'))
  406.             return $this->raiseError("Bad XML root node");
  407.             $out .= $this->root->get($this->use_cdata_sections);
  408.         }
  409.         return $out;
  410.     }
  411.  
  412.     /**
  413.      * Get current namespace.
  414.      *
  415.      * @param  string  name  namespace
  416.      * @return string 
  417.      *
  418.      * @access public
  419.      */
  420.     function &getName($name{
  421.         return $this->root->getElement($this->namespace[$name]);
  422.     }
  423.  
  424.     /**
  425.      * Get A Nodes Namespace
  426.      */
  427.  
  428.     function getNodeNamespace(&$node{
  429.         $name_parts explode(':',$node->name);
  430.         if (sizeof($name_parts> 1{
  431.             $namespace $name_parts[0];
  432.         else {
  433.             $namespace '';
  434.         }
  435.  
  436.         if (isset($node->namespace[$namespace])) {
  437.             return $node->namespace[$namespace];
  438.         elseif (isset($this->root->namespace[$namespace])) {
  439.             return $this->root->namespace[$namespace];
  440.         else {
  441.             return '';
  442.         }
  443.     }
  444.  
  445.     /**
  446.      * Get a reference to a node. Node is searched by its 'path'.
  447.      *
  448.      * @param mixed  path  Path to node. Can be either a string (slash-separated
  449.      *                      children names) or an array (sequence of children names) both
  450.      *                      of them starting from node. Note that the first name in sequence
  451.      *                      must be the name of the document root.
  452.      * @return object    Reference to the XML_Tree_Node found, or PEAR_Error if
  453.      *                    the path does not exist. If more than one element matches
  454.      *                    then only the first match is returned.
  455.      * @access public
  456.      */
  457.     function &getNodeAt($path)
  458.     {
  459.         if (is_null($this->root)){
  460.             return $this->raiseError("XML_Tree hasn't a root node");
  461.         }
  462.         if (is_string($path))
  463.             $path explode("/"$path);
  464.         if (sizeof($path== 0{
  465.             return $this->raiseError("Path to node is empty");
  466.         }
  467.         $path1 $path;
  468.         $rootName array_shift($path1);
  469.         if ($this->root->name != $rootName{
  470.             return $this->raiseError("Path does not match the document root");
  471.         }
  472.         $x =$this->root->getNodeAt($path1);
  473.         if (!PEAR::isError($x)) {
  474.             return $x;
  475.         }
  476.         // No node with that name found
  477.         return $this->raiseError("Bad path to node: [".implode('/'$path)."]");
  478.     }
  479.  
  480.     /**
  481.      * Gets all children that match a given tag name.
  482.      *
  483.      * @param  string    Tag name
  484.      *
  485.      * @return array     An array of Node objects of the children found,
  486.      *                    an empty array if none
  487.      * @access public
  488.      * @author Pierre-Alain Joye <paj@pearfr.org>
  489.      */
  490.     function &getElementsByTagName($tagName)
  491.     {
  492.         if (empty($tagName)) {
  493.             return $this->raiseError('Empty tag name');
  494.         }
  495.         $result = array();
  496.         foreach ($this->root->children as $child{
  497.             if ($child->name == $tagName{
  498.                 $result[$child;
  499.             }
  500.         }
  501.         return $result;
  502.     }
  503.  
  504.     /**
  505.      * Gets all children that match a given tag name from node
  506.      *
  507.      * @param string $tagName Tag Name
  508.      * @param object &$node Node in which to search
  509.      * @see XML_Tree::getElementsByTagName()
  510.      * @return array An array of found Node objects, empty is none found.
  511.      * @access public
  512.      * @author Pierre-Alain Joye <paj@pearfr.org>
  513.      * @author Davey Shafik <davey@php.net>
  514.      */
  515.  
  516.     function &getElementsByTagNameFromNode($tagName&$node)
  517.     {
  518.         if (empty($tagName)) {
  519.             return $this->raiseError('Empty tag name');
  520.         }
  521.         $result = array();
  522.         foreach ($node->children as $child{
  523.             if ($child->name == $tagName{
  524.                 $result[$child;
  525.             }
  526.         }
  527.         return $result;
  528.     }
  529.  
  530.  
  531.     /**
  532.      * Checks if $name is a valid XML name
  533.      *
  534.      * @static
  535.      * @param string $name String to check for validity as an XML Name
  536.      * @return mixed 
  537.      */
  538.  
  539.     function isValidName($name$type{
  540.         //  check for invalid starting character
  541.         if (!preg_match("/[[:alpha:]_]/"$name{0})) {
  542.             return new PEAR_Errorucfirst($type. " ('$name') has an invalid name, an XML name may only start with a letter or underscore");
  543.         }
  544.  
  545.         if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?$/"$name)) {
  546.             return new PEAR_Errorucfirst($type. " ('$name') has an invalid name, an XML name may only contain alphanumeric chars, period, hyphen, colon and underscores");
  547.         }
  548.  
  549.         return true;
  550.     }
  551. }
  552. ?>

Documentation generated on Mon, 11 Mar 2019 10:16:51 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.