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 288859 2009-09-27 04:46:10Z clockwerx $
  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.      * @param string $encoding XML Files encoding, use NULL (default) to use whatever the document specifies
  229.      * @return mixed The XML tree root (an XML_Tree_Node), or PEAR_Error upon error.
  230.      * @access public
  231.      */
  232.     function &getTreeFromFile ($encoding = null)
  233.     {
  234.         $this->folding = false;
  235.         $this->XML_Parser($encoding'event');
  236.         $err $this->setInputFile($this->filename);
  237.         if (PEAR::isError($err)) {
  238.             return $err;
  239.         }
  240.         $this->cdata = null;
  241.         $err $this->parse();
  242.         if (PEAR::isError($err)) {
  243.             return $err;
  244.         }
  245.         return $this->root;
  246.     }
  247.  
  248.     /**
  249.      * Maps an XML string to an XML_Tree.
  250.      *
  251.      * @param string $encoding XML Files encoding, use NULL (default) to use whatever the document specifies
  252.      * @return mixed The XML tree root (an XML_Tree_Node), or PEAR_Error upon error.
  253.      * @access public
  254.      */
  255.     function &getTreeFromString($str$encoding = null)
  256.     {
  257.         $this->= null;
  258.         $this->folding = false;
  259.         $this->XML_Parser($encoding'event');
  260.         $this->cdata = null;
  261.         $err $this->parseString($str);
  262.         if (PEAR::isError($err)) {
  263.             return $err;
  264.         }
  265.         return $this->root;
  266.     }
  267.  
  268.     /**
  269.      * Handler for the xml-data
  270.      * Used by XML_Parser::XML_Parser() when parsing an XML stream.
  271.      *
  272.      * @param mixed  xp          ignored
  273.      * @param string elem        name of the element
  274.      * @param array  attribs     attributes for the generated node
  275.      *
  276.      * @access private
  277.      */
  278.     function startHandler($xp$elem&$attribs)
  279.     {
  280.         $lineno xml_get_current_line_number($xp);
  281.         // root elem
  282.         if (!isset($this->i)) {
  283.             $this->obj1 =$this->addRoot($elemnull$attribs$lineno);
  284.             $this->= 2;
  285.         else {
  286.             // mixed contents
  287.             if (!empty($this->cdata)) {
  288.                 $parent_id 'obj' ($this->- 1);
  289.                 $parent    =$this->$parent_id;
  290.                 $parent->children[&new XML_Tree_Node(null$this->cdatanull$lineno);
  291.             }
  292.             $obj_id 'obj' $this->i++;
  293.             $this->$obj_id &new XML_Tree_Node($elemnull$attribs$lineno);
  294.         }
  295.         $this->cdata = null;
  296.         return null;
  297.     }
  298.  
  299.     /**
  300.      * Handler for the xml-data
  301.      * Used by XML_Parser::XML_Parser() when parsing an XML stream.
  302.      *
  303.      * @param mixed  xp          ignored
  304.      * @param string elem        name of the element
  305.      *
  306.      * @access private
  307.      */
  308.     function endHandler($xp$elem)
  309.     {
  310.         $this->i--;
  311.         if ($this->> 1{
  312.             $obj_id 'obj' $this->i;
  313.             // recover the node created in StartHandler
  314.             $node   =$this->$obj_id;
  315.             // mixed contents
  316.             if (count($node->children> 0{
  317.                 if (trim($this->cdata!= ''{
  318.                     $node->children[&new XML_Tree_Node(null$this->cdata);
  319.                 }
  320.             else {
  321.                 $node->setContent($this->cdata);
  322.             }
  323.             $parent_id 'obj' ($this->- 1);
  324.             $parent    =$this->$parent_id;
  325.             // attach the node to its parent node children array
  326.             $parent->children[$node;
  327.         else {
  328.             $node =$this->obj1;
  329.             if (count($node->children> 0{
  330.                 if (trim($this->cdata)) {
  331.                     $node->children[&new XML_Tree_Node(null$this->cdata);
  332.                 }
  333.             else {
  334.                 $node->setContent($this->cdata);
  335.             }
  336.         }
  337.         $this->cdata = null;
  338.         return null;
  339.     }
  340.  
  341.     /**
  342.      * The xml character data handler
  343.      * Used by XML_Parser::XML_Parser() when parsing an XML stream.
  344.      *
  345.      * @param mixed  xp          ignored
  346.      * @param string data        PCDATA between tags
  347.      *
  348.      * @access private
  349.      */
  350.     function cdataHandler($xp$data)
  351.     {
  352.         $this->cdata .= $data;
  353.     }
  354.  
  355.     /**
  356.      * Get a copy of this tree by cloning and all of its nodes, recursively.
  357.      *
  358.      * @return object XML_Tree copy of this node.
  359.      * @access public
  360.      */
  361.     function cloneTree()
  362.     {
  363.         $clone = new XML_Tree($this->filename$this->version);
  364.         if (!is_null($this->root)) {
  365.             $clone->root = $this->root->cloneTree();
  366.         }
  367.  
  368.         // clone all other vars
  369.         $temp get_object_vars($this);
  370.         foreach($temp as $varname => $value{
  371.             if (!in_array($varname,array('filename','version','root'))) {
  372.                 $clone->$varname=$value;
  373.             }
  374.         }
  375.         return $clone;
  376.     }
  377.  
  378.     /**
  379.      * Print text representation of XML tree.
  380.      *
  381.      * @param bool xmlHeader     if true then generate also the leading XML
  382.      *                            'Content-type' header directive, e.g. for
  383.      *                            direct output to a web page.
  384.      *
  385.      * @access public
  386.      */
  387.     function dump($xmlHeader = false)
  388.     {
  389.         if ($xmlHeader{
  390.             header('Content-type: text/xml');
  391.         }
  392.         echo $this->get($this->use_cdata_sections);
  393.     }
  394.  
  395.     /**
  396.      * Get text representation of XML tree.
  397.      *
  398.      * @return string  Text (XML) representation of the tree
  399.      * @access public
  400.      */
  401.     function &get()
  402.     {
  403.         $out '<?xml version="' $this->version . "\"?>\n";
  404.  
  405.         if (!is_null($this->root))
  406.         {
  407.             if(!is_object($this->root|| (strtolower(get_class($this->root)) != 'xml_tree_node'))
  408.             return $this->raiseError("Bad XML root node");
  409.             $out .= $this->root->get($this->use_cdata_sections);
  410.         }
  411.         return $out;
  412.     }
  413.  
  414.     /**
  415.      * Get current namespace.
  416.      *
  417.      * @param  string  name  namespace
  418.      * @return string 
  419.      *
  420.      * @access public
  421.      */
  422.     function &getName($name{
  423.         return $this->root->getElement($this->namespace[$name]);
  424.     }
  425.  
  426.     /**
  427.      * Get A Nodes Namespace
  428.      */
  429.  
  430.     function getNodeNamespace(&$node{
  431.         $name_parts explode(':',$node->name);
  432.         if (sizeof($name_parts> 1{
  433.             $namespace $name_parts[0];
  434.         else {
  435.             $namespace '';
  436.         }
  437.  
  438.         if (isset($node->namespace[$namespace])) {
  439.             return $node->namespace[$namespace];
  440.         elseif (isset($this->root->namespace[$namespace])) {
  441.             return $this->root->namespace[$namespace];
  442.         else {
  443.             return '';
  444.         }
  445.     }
  446.  
  447.     /**
  448.      * Get a reference to a node. Node is searched by its 'path'.
  449.      *
  450.      * @param mixed  path  Path to node. Can be either a string (slash-separated
  451.      *                      children names) or an array (sequence of children names) both
  452.      *                      of them starting from node. Note that the first name in sequence
  453.      *                      must be the name of the document root.
  454.      * @return object    Reference to the XML_Tree_Node found, or PEAR_Error if
  455.      *                    the path does not exist. If more than one element matches
  456.      *                    then only the first match is returned.
  457.      * @access public
  458.      */
  459.     function &getNodeAt($path)
  460.     {
  461.         if (is_null($this->root)){
  462.             return $this->raiseError("XML_Tree hasn't a root node");
  463.         }
  464.         if (is_string($path))
  465.             $path explode("/"$path);
  466.         if (sizeof($path== 0{
  467.             return $this->raiseError("Path to node is empty");
  468.         }
  469.         $path1 $path;
  470.         $rootName array_shift($path1);
  471.         if ($this->root->name != $rootName{
  472.             return $this->raiseError("Path does not match the document root");
  473.         }
  474.         $x =$this->root->getNodeAt($path1);
  475.         if (!PEAR::isError($x)) {
  476.             return $x;
  477.         }
  478.         // No node with that name found
  479.         return $this->raiseError("Bad path to node: [".implode('/'$path)."]");
  480.     }
  481.  
  482.     /**
  483.      * Gets all children that match a given tag name.
  484.      *
  485.      * @param  string    Tag name
  486.      *
  487.      * @return array     An array of Node objects of the children found,
  488.      *                    an empty array if none
  489.      * @access public
  490.      * @author Pierre-Alain Joye <paj@pearfr.org>
  491.      */
  492.     function &getElementsByTagName($tagName)
  493.     {
  494.         if (empty($tagName)) {
  495.             return $this->raiseError('Empty tag name');
  496.         }
  497.         $result = array();
  498.         foreach ($this->root->children as $child{
  499.             if ($child->name == $tagName{
  500.                 $result[$child;
  501.             }
  502.         }
  503.         return $result;
  504.     }
  505.  
  506.     /**
  507.      * Gets all children that match a given tag name from node
  508.      *
  509.      * @param string $tagName Tag Name
  510.      * @param object &$node Node in which to search
  511.      * @see XML_Tree::getElementsByTagName()
  512.      * @return array An array of found Node objects, empty is none found.
  513.      * @access public
  514.      * @author Pierre-Alain Joye <paj@pearfr.org>
  515.      * @author Davey Shafik <davey@php.net>
  516.      */
  517.  
  518.     function &getElementsByTagNameFromNode($tagName&$node)
  519.     {
  520.         if (empty($tagName)) {
  521.             return $this->raiseError('Empty tag name');
  522.         }
  523.         $result = array();
  524.         foreach ($node->children as $child{
  525.             if ($child->name == $tagName{
  526.                 $result[$child;
  527.             }
  528.         }
  529.         return $result;
  530.     }
  531.  
  532.  
  533.     /**
  534.      * Checks if $name is a valid XML name
  535.      *
  536.      * @static
  537.      * @param string $name String to check for validity as an XML Name
  538.      * @return mixed 
  539.      */
  540.  
  541.     function isValidName($name$type{
  542.         if (trim($name== ''{
  543.             return true;
  544.         }
  545.         
  546.         //  check for invalid starting character
  547.         if (!preg_match("/[[:alpha:]_]/"$name{0})) {
  548.             return new PEAR_Errorucfirst($type. " ('$name') has an invalid name, an XML name may only start with a letter or underscore");
  549.         }
  550.  
  551.         if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?$/"$name)) {
  552.             return new PEAR_Errorucfirst($type. " ('$name') has an invalid name, an XML name may only contain alphanumeric chars, period, hyphen, colon and underscores");
  553.         }
  554.  
  555.         return true;
  556.     }
  557. }
  558. ?>

Documentation generated on Mon, 11 Mar 2019 15:34:45 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.