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

Source for file common.php

Documentation is available at common.php

  1. <?php
  2. // {{{ license
  3.  
  4. // +----------------------------------------------------------------------+
  5. // | PHP version 4.0                                                      |
  6. // +----------------------------------------------------------------------+
  7. // | Copyright (c) 1997-2001 The PHP Group                                |
  8. // +----------------------------------------------------------------------+
  9. // | This source file is subject to version 2.0 of the PHP license,       |
  10. // | that is bundled with this package in the file LICENSE, and is        |
  11. // | available at through the world-wide-web at                           |
  12. // | http://www.php.net/license/2_02.txt.                                 |
  13. // | If you did not receive a copy of the PHP license and are unable to   |
  14. // | obtain it through the world-wide-web, please send a note to          |
  15. // | license@php.net so we can mail you a copy immediately.               |
  16. // +----------------------------------------------------------------------+
  17. // | Authors: Dan Allen <dan@mojavelinux.com>                             |
  18. // +----------------------------------------------------------------------+
  19.  
  20. // $Id: common.php,v 1.23 2007/08/04 20:24:41 cweiske Exp $
  21.  
  22. // }}}
  23. // {{{ description
  24.  
  25. // Core DOM and internal pointer methods for the Xpath/DOM XML manipulation and query interface.
  26.  
  27. // }}}
  28. // {{{ functions
  29.  
  30. /**
  31.  * is_a is only defined in php for user functions,
  32.  * so I implemented its functionality for php objects
  33.  * until they fix (or never fix) this problem
  34.  *
  35.  * @param  object  $class the class to check
  36.  * @param  string  $match class name you are looking for
  37.  *
  38.  * @access public
  39.  * @return boolean whether the class is of the class type or a descendent of the class
  40.  */
  41. function is_a_php_class($class$match)
  42. {
  43.     if (empty($class)) {
  44.         return false;
  45.     }
  46.  
  47.     $class is_object($classget_class($class$class;
  48.     if (strtolower($class== strtolower($match)) {
  49.         return true;
  50.     }
  51.  
  52.     return is_a_php_class(get_parent_class($class)$match);
  53. }
  54.  
  55. // }}}
  56.  
  57. // {{{ class XML_XPath_common
  58.  
  59. /**
  60.  * The XML_XPath_common class contains the DOM functions used to manipulate
  61.  * and maneuver through the xml tree.  The main thing to understand is
  62.  * that all operations work around a single pointer.  This pointer is your
  63.  * place holder within the document.  Each function you run assumes the
  64.  * node in reference is your pointer.  However, every function can take
  65.  * an xpath query or DOM object reference, so that the pointer can be set
  66.  * before working on the node, and can retain this position if specified.
  67.  * Every DOM function call has a init() and shutdown() call.  This function
  68.  * prepares the pointer to the requested location in the tree if an xpath query
  69.  * or pointer object is provided.  In addition, the init() function checks to
  70.  * see that the node type is acceptable for the method, and if not throws an
  71.  * XML_XPath_Error exception.  If you want to execute a function and then remain
  72.  * in the location of your query, then you specify that you want to move the pointer.
  73.  * For the DOM step functions, this is the default action.
  74.  *
  75.  * Note: All offsets in the CharacterData interface start from 0.
  76.  *
  77.  * The object model of XML_XPath is as follows (indentation means inheritance):
  78.  *
  79.  * XML_XPath_common The main functionality of the XML_XPath class is here.  This
  80.  * |            holds all the DOM functions for manipulating and maneuvering
  81.  * |            through the DOM tree.
  82.  * |
  83.  * +-XML_XPath      The frontend for the XML_XPath implementation.  Provides default
  84.  * |            functions for preparing the main document, running xpath queries
  85.  * |            and handling errorMessages.
  86.  * |
  87.  * +-Result     Extended from the XML_XPath_common class, this object is returned when
  88.  *              an xpath query is executed and can be used to cycle through the
  89.  *              result nodeset or data
  90.  *
  91.  * @version  Revision: 1.1
  92.  * @author   Dan Allen <dan@mojavelinux.com>
  93.  * @access   public
  94.  * @since    PHP 4.2.1
  95.  * @package  XML_XPath
  96.  */
  97.  
  98. // }}}
  99.     // {{{ properties
  100.  
  101.     /**
  102.      * domxml node of the current location in the xml document
  103.      * @var object $pointer 
  104.      */
  105.     var $pointer;
  106.  
  107.     /**
  108.      * domxml node bookmark used for holding a place in the xml document
  109.      * @var object $bookmark 
  110.      */
  111.     var $bookmark;
  112.  
  113.     /**
  114.      * when working with the xml document, ignore the presence of blank nodes (white space)
  115.      * @var boolean $skipBlanks 
  116.      */
  117.     var $skipBlanks = true;
  118.  
  119.     /**
  120.      * path to xmllint used for reformating the xml output
  121.      * [!] should be using System_Command for this [!]
  122.      * @var string $xmllint 
  123.      */
  124.     var $xmllint = 'xmllint';
  125.  
  126.     // }}}
  127.     // {{{ string  nodeName()
  128.  
  129.     /**
  130.      * Return the name of this node, depending on its type, according to the DOM recommendation
  131.      *
  132.      * @param  string  $in_xpathQuery (optional) quick xpath query
  133.      * @param  boolean $in_movePointer (optional) move internal pointer with quick xpath query
  134.      *
  135.      * @access public
  136.      * @return string name of node corresponding to DOM recommendation {or XML_XPath_Error exception}
  137.      */
  138.     function nodeName($in_xpathQuery = null$in_movePointer = false)
  139.     {
  140.         if (!$this->pointer{
  141.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  142.         }
  143.  
  144.         if ($hasQuery !is_null($in_xpathQuery&& XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointer))) {
  145.             return $result;
  146.         }
  147.  
  148.         $nodeName $this->pointer->node_name();
  149.  
  150.         if ($hasQuery && !$in_movePointer{
  151.             $this->_restore_bookmark();
  152.         }
  153.  
  154.         return $nodeName;
  155.     }
  156.  
  157.     // }}}
  158.     // {{{ int     nodeType()
  159.  
  160.     /**
  161.      * Returns the integer value constant corresponding to the DOM node type
  162.      *
  163.      * @param  string  $in_xpathQuery (optional) quick xpath query
  164.      * @param  boolean $in_movePointer (optional) move internal pointer with quick xpath query
  165.      *
  166.      * @access public
  167.      * @return int DOM type of the node {or XML_XPath_Error exception}
  168.      */
  169.     function nodeType($in_xpathQuery = null$in_movePointer = false)
  170.     {
  171.         if (!$this->pointer{
  172.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  173.         }
  174.  
  175.         if ($hasQuery !is_null($in_xpathQuery&& XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointer))) {
  176.             return $result;
  177.         }
  178.  
  179.         $nodeType $this->pointer->node_type();
  180.  
  181.         if ($hasQuery && !$in_movePointer{
  182.             $this->_restore_bookmark();
  183.         }
  184.  
  185.         return $nodeType;
  186.     }
  187.  
  188.     // }}}
  189.     // {{{ object  childNodes()
  190.  
  191.     /**
  192.      * Retrieves the child nodes from the element node as an XML_XPath_result object
  193.      *
  194.      * Similar to an xpath query, this function will grab all the first descendant child
  195.      * nodes of the element node at the current position and will create an XML_XPath_result
  196.      * object of type nodeset with each of the child nodes as the nodes.
  197.      * DOM query functions do not take an xpathQuery argument
  198.      *
  199.      * @access public
  200.      * @return object XML_XPath_result object of type nodeset
  201.      *  [!] important note: since we had to hack the result object a bit, you cannot sort the
  202.      *  result object when generated in this manner right now [!]
  203.      */
  204.     function &childNodes()
  205.     {
  206.         if (!$this->pointer{
  207.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  208.         }
  209.  
  210.         $nodeset = array();
  211.         foreach($this->pointer->child_nodes(as $childNode{
  212.             // if this is a blank node and we are skipping blank nodes...skip to next child
  213.             if ($childNode->is_blank_node(&& $this->skipBlanks{
  214.                 continue;
  215.             }
  216.             $nodeset[$childNode;
  217.         }
  218.         return new XML_XPath_result($nodesetXPATH_NODESETarray($this->pointer'/*')$this->ctx$this->xml);
  219.     }
  220.  
  221.     // }}}
  222.     // {{{ object  getElementsByTagName()
  223.  
  224.     /**
  225.      * Create an XML_XPath_result object with the elements with the specified tagname
  226.      *
  227.      * DOM query functions do not take an xpathQuery argument
  228.      *
  229.      * @param  string $in_tagName 
  230.      *
  231.      * @return object XML_XPath_result object of matching nodes
  232.      * @access public
  233.      */
  234.     function getElementsByTagName($in_tagName)
  235.     {
  236.         // since we can't do an actual xpath query, we need to create a pseudo xpath result
  237.         $nodeset $this->xml->get_elements_by_tagname($in_tagName);
  238.         return new XML_XPath_result($nodesetXPATH_NODESETarray(null'//' $in_tagName)$this->ctx$this->xml);
  239.     }
  240.  
  241.     // }}}
  242.     // {{{ boolean documentElement()
  243.  
  244.     /**
  245.      * Move to the document element
  246.      *
  247.      * @param  boolean  $in_movePointer (optional) move the internal pointer or return reference
  248.      *
  249.      * @access public
  250.      * @return boolean whether pointer was moved or object pointer to document element
  251.      */
  252.     function documentElement($in_movePointer = true)
  253.     {
  254.         if (!$this->pointer{
  255.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  256.         }
  257.  
  258.         $documentElement $this->xml->document_element();
  259.         if ($in_movePointer{
  260.             $this->pointer = $documentElement;
  261.             return true;
  262.         }
  263.         else {
  264.             return $documentElement;
  265.         }
  266.     }
  267.  
  268.     // }}}
  269.     // {{{ boolean parentNode()
  270.  
  271.     /**
  272.      * Moves the internal pointer to the parent of the current node or returns the pointer.
  273.      * Step functions do not take an xpathQuery argument
  274.      *
  275.      * @param  boolean  $in_movePointer (optional) move the internal pointer or return reference
  276.      *
  277.      * @access public
  278.      * @return boolean whether pointer was moved or object pointer to parent
  279.      */
  280.     function parentNode($in_movePointer = true)
  281.     {
  282.         if (!$this->pointer{
  283.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  284.         }
  285.  
  286.         $parent $this->pointer->parent_node();
  287.         if ($parent{
  288.             if ($in_movePointer{
  289.                 $this->pointer = $parent;
  290.                 return true;
  291.             }
  292.             else {
  293.                 return $parent;
  294.             }
  295.         }
  296.         else {
  297.             return false;
  298.         }
  299.     }
  300.  
  301.     // }}}
  302.     // {{{ boolean nextSibling()
  303.  
  304.     /**
  305.      * Moves the internal pointer to the next sibling of the current node, or returns the pointer.
  306.      * If the flag is on to skip blank nodes then the first non-blank node is used.
  307.      * Step functions do not take an xpathQuery argument
  308.      *
  309.      * @param  boolean  $in_movePointer (optional) move the internal pointer or return reference
  310.      *
  311.      * @access public
  312.      * @return boolean whether the pointer was moved or object pointer to next sibling
  313.      */
  314.     function nextSibling($in_movePointer = true)
  315.     {
  316.         if (!$this->pointer{
  317.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  318.         }
  319.  
  320.         if (!$this->pointer->next_sibling()) {
  321.             return false;
  322.         }
  323.  
  324.         $next $this->pointer->next_sibling();
  325.         if ($this->skipBlanks{
  326.             while (true{
  327.                 // make sure we are not already at the end
  328.                 if (!$next{
  329.                     $next = false;
  330.                     break;
  331.                 }
  332.                 // we have found a non-blank node
  333.                 elseif (!$next->is_blank_node()) {
  334.                     break;
  335.                 }
  336.                 // we found a blank node at the very end
  337.                 elseif (!$next $next->next_sibling()) {
  338.                     $next = false;
  339.                     break;
  340.                 }
  341.             }
  342.         }
  343.         if ($next{
  344.             if ($in_movePointer{
  345.                 $this->pointer = $next;
  346.                 return true;
  347.             }
  348.             else {
  349.                 return $next;
  350.             }
  351.         }
  352.         else {
  353.             return false;
  354.         }
  355.     }
  356.  
  357.     // }}}
  358.     // {{{ boolean previousSibling()
  359.  
  360.     /**
  361.      * Moves the internal pointer to the previous sibling
  362.      * of the current node or returns the pointer.
  363.      * If the flag is on to skip blank nodes then the first non-blank node is used.
  364.      * Step functions do not take an xpathQuery argument
  365.      *
  366.      * @param  boolean  $in_movePointer (optional) move the internal pointer or return reference
  367.      *
  368.      * @access public
  369.      * @return boolean whether the pointer was moved or object pointer to previous sibling
  370.      */
  371.     function previousSibling($in_movePointer = true)
  372.     {
  373.         if (!$this->pointer{
  374.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  375.         }
  376.  
  377.         if (!$this->pointer->previous_sibling()) {
  378.             return false;
  379.         }
  380.  
  381.         $previous $this->pointer->previous_sibling();
  382.         if ($this->skipBlanks{
  383.             while (true{
  384.                 // we have found a non-blank node
  385.                 if (!$previous->is_blank_node()) {
  386.                     break;
  387.                 }
  388.                 // we have arrived at the beginning
  389.                 elseif (!$previous $previous->previous_sibling()) {
  390.                     $previous = false;
  391.                     break;
  392.                 }
  393.             }
  394.         }
  395.         if ($previous{
  396.             if ($in_movePointer{
  397.                 $this->pointer = $previous;
  398.                 return true;
  399.             }
  400.             else {
  401.                 return $previous;
  402.             }
  403.         }
  404.         else {
  405.             return false;
  406.         }
  407.     }
  408.  
  409.     // }}}
  410.     // {{{ boolean firstChild()
  411.  
  412.     /**
  413.      * Moves the pointer to the first child of this node or returns the first node.
  414.      * If the flag is on to skip blank nodes then the first non-blank node is used.
  415.      * Step functions do not take an xpathQuery argument
  416.      *
  417.      * @param  boolean  $in_movePointer (optional) move the internal pointer or return reference
  418.      *
  419.      * @access public
  420.      * @return boolean whether the pointer was moved to the first child or returns the first child
  421.      */
  422.     function firstChild($in_movePointer = true)
  423.     {
  424.         if (!$this->pointer{
  425.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  426.         }
  427.  
  428.         if (!$this->pointer->has_child_nodes()) {
  429.             return false;
  430.         }
  431.  
  432.         $first $this->pointer->first_child();
  433.         if ($this->skipBlanks{
  434.             while (true{
  435.                 // we have found a non-blank node
  436.                 if (!$first->is_blank_node()) {
  437.                     break;
  438.                 }
  439.                 // we have arrived at the end
  440.                 elseif (!$first $first->next_sibling()) {
  441.                     $first = false;
  442.                     break;
  443.                 }
  444.             }
  445.         }
  446.         if ($first{
  447.             if ($in_movePointer{
  448.                 $this->pointer = $first;
  449.                 return true;
  450.             }
  451.             else {
  452.                 return $first;
  453.             }
  454.         }
  455.         else {
  456.             return false;
  457.         }
  458.     }
  459.  
  460.     // }}}
  461.     // {{{ boolean lastChild()
  462.  
  463.     /**
  464.      * Moves the pointer to the last child of this node or returns the last child.
  465.      * If the flag is on to skip blank nodes then the first non-blank node is used.
  466.      * Step functions do not take an xpathQuery argument
  467.      *
  468.      * @param  boolean  $in_movePointer (optional) move the internal pointer or return reference
  469.      *
  470.      * @access public
  471.      * @return boolean whether the pointer was moved to the last child or returns the last child
  472.      */
  473.     function lastChild($in_movePointer = true)
  474.     {
  475.         if (!$this->pointer{
  476.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  477.         }
  478.  
  479.         if (!$this->pointer->has_child_nodes()) {
  480.             return false;
  481.         }
  482.  
  483.         $last $this->pointer->last_child();
  484.         if ($this->skipBlanks{
  485.             while (true{
  486.                 // we have found a non-blank node
  487.                 if (!$last->is_blank_node()) {
  488.                     break;
  489.                 }
  490.                 // we have arrived at the beginning
  491.                 elseif (!$last $last->previous_sibling()) {
  492.                     $last = false;
  493.                     break;
  494.                 }
  495.             }
  496.         }
  497.         if ($last{
  498.             if ($in_movePointer{
  499.                 $this->pointer = $last;
  500.                 return true;
  501.             }
  502.             else {
  503.                 return $last;
  504.             }
  505.         }
  506.         else {
  507.             return false;
  508.         }
  509.     }
  510.  
  511.     // }}}
  512.     // {{{ boolean hasChildNodes()
  513.  
  514.     /**
  515.      * Returns whether this node has any children.
  516.      *
  517.      * @param  string  $in_xpathQuery (optional) quick xpath query
  518.      * @param  boolean $in_movePointer (optional) move internal pointer
  519.      *
  520.      * @access public
  521.      * @return boolean has child nodes {or XML_XPath_Error exception}
  522.      */
  523.     function hasChildNodes($in_xpathQuery = null$in_movePointer = false)
  524.     {
  525.         if (!$this->pointer{
  526.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  527.         }
  528.  
  529.         if ($hasQuery !is_null($in_xpathQuery&& XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointer))) {
  530.             return $result;
  531.         }
  532.  
  533.         $hasChildNodes $this->pointer->has_child_nodes();
  534.  
  535.         if ($hasQuery && !$in_movePointer{
  536.             $this->_restore_bookmark();
  537.         }
  538.  
  539.         return $hasChildNodes;
  540.     }
  541.  
  542.     // }}}
  543.     // {{{ boolean hasAttributes()
  544.  
  545.     /**
  546.      * Returns whether this node has any attributes.
  547.      *
  548.      * @param string  $in_xpathQuery (optional) quick xpath query
  549.      * @param boolean $in_movePointer (optional) move internal pointer
  550.      *
  551.      * @access public
  552.      * @return boolean attributes exist {or XML_XPath_Error exception}
  553.      */
  554.     function hasAttributes($in_xpathQuery = null$in_movePointer = false)
  555.     {
  556.         if (!$this->pointer{
  557.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  558.         }
  559.  
  560.         if ($hasQuery !is_null($in_xpathQuery&& XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointer))) {
  561.             return $result;
  562.         }
  563.  
  564.         $hasAttributes $this->pointer->has_attributes();
  565.  
  566.         if ($hasQuery && !$in_movePointer{
  567.             $this->_restore_bookmark();
  568.         }
  569.  
  570.         return $hasAttributes;
  571.     }
  572.  
  573.     // }}}
  574.     // {{{ boolean hasAttribute()
  575.  
  576.     /**
  577.      * Returns true when an attribute with a given name is specified on this element
  578.      * false otherwise.
  579.      *
  580.      * @param string  $in_name name of attribute
  581.      * @param string  $in_xpathQuery (optional) quick xpath query
  582.      * @param boolean $in_movePointer (optional) move internal pointer
  583.      *
  584.      * @access public
  585.      * @return boolean existence of attribute {or XML_XPath_Error exception}
  586.      */
  587.     function hasAttribute($in_name$in_xpathQuery = null$in_movePointer = false)
  588.     {
  589.         if (!$this->pointer{
  590.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  591.         }
  592.  
  593.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODE)))) {
  594.             return $result;
  595.         }
  596.  
  597.         $hasAttribute $this->pointer->has_attribute($in_name? true : false;
  598.  
  599.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  600.             $this->_restore_bookmark();
  601.         }
  602.  
  603.         return $hasAttribute;
  604.     }
  605.  
  606.     // }}}
  607.     // {{{ array   getAttributes()
  608.  
  609.     /**
  610.      * Return an associative array of attribute names as the keys and attribute values as the
  611.      * values.  This is not a DOM function, but is a convenient addition.
  612.      *
  613.      * @param string  $in_xpathQuery (optional) quick xpath query
  614.      * @param boolean $in_movePointer (optional) move internal pointer
  615.      *
  616.      * @access public
  617.      * @return array associative array of attributes {or XML_XPath_Error exception}
  618.      */
  619.     function getAttributes($in_xpathQuery = null$in_movePointer = false)
  620.     {
  621.         if (!$this->pointer{
  622.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  623.         }
  624.  
  625.         if ($hasQuery !is_null($in_xpathQuery&& XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointer))) {
  626.             return $result;
  627.         }
  628.  
  629.         $return = array();
  630.         if (is_array($attributeNodes $this->pointer->attributes())) {
  631.             foreach($attributeNodes as $attributeNode{
  632.                 $return[$attributeNode->name$attributeNode->value;
  633.             }
  634.         }
  635.  
  636.         if ($hasQuery && !$in_movePointer{
  637.             $this->_restore_bookmark();
  638.         }
  639.  
  640.         return $return;
  641.     }
  642.  
  643.     // }}}
  644.     // {{{ string  getAttribute()
  645.  
  646.     /**
  647.      * Retrieves an attribute value by name from the element node at the current pointer.
  648.      *
  649.      * Grab the attribute value if it exists and return it.  If the attribute does not
  650.      * exist, this function will return the boolean 'false', so be sure to check properly
  651.      * if the value is "" or if the attribute doesn't exist at all.  This function is the
  652.      * only attribute function which allows you to step onto the attribute node.
  653.      *
  654.      * @param string  $in_name Name of the attribute
  655.      * @param string  $in_xpathQuery (optional) quick xpath query
  656.      * @param boolean $in_movePointer (optional) move the internal pointer with quick xpath query
  657.      *
  658.      * @access public
  659.      * @return string value of attribute or false if attribute DNE {or XML_XPath_Error exception}
  660.      */
  661.     function getAttribute($in_name$in_xpathQuery = null$in_movePointer = false)
  662.     {
  663.         if (!$this->pointer{
  664.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  665.         }
  666.  
  667.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODE)))) {
  668.             return $result;
  669.         }
  670.  
  671.         $result $this->pointer->get_attribute($in_name);
  672.         // if we found the attribute, move to it if we are moving the pointer
  673.         if ($result && $in_movePointer{
  674.             $this->pointer = $this->pointer->get_attribute_node($in_name);
  675.         }
  676.  
  677.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  678.             $this->_restore_bookmark();
  679.         }
  680.  
  681.         return $result;
  682.     }
  683.  
  684.     // }}}
  685.     // {{{ boolean setAttribute()
  686.  
  687.     /**
  688.      * Adds a new attribute. If an attribute with that name is already present
  689.      * in the element, its value is changed to be that of the value parameter.
  690.      * Invalid characters are escaped.
  691.      *
  692.      * @param string  $in_name name of the attribute to be set
  693.      * @param string  $in_value new attribute value
  694.      * @param string  $in_xpathQuery (optional) quick xpath query
  695.      * @param boolean $in_movePointer (optional) move internal pointer
  696.      *
  697.      * @access public
  698.      * @return boolean success {or XML_XPath_Error exception}
  699.      */
  700.     function setAttribute($in_name$in_value$in_xpathQuery = null$in_movePointer = false)
  701.     {
  702.         if (!$this->pointer{
  703.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  704.         }
  705.  
  706.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODE)))) {
  707.             return $result;
  708.         }
  709.  
  710.         $result $this->pointer->set_attribute($in_name$in_value);
  711.  
  712.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  713.             $this->_restore_bookmark();
  714.         }
  715.  
  716.         return $result;
  717.     }
  718.  
  719.     // }}}
  720.     // {{{ void    removeAttribute()
  721.  
  722.     /**
  723.      * Remove the attribute by name.
  724.      *
  725.      * @param  string  $in_name name of the attribute
  726.      * @param  string  $in_xpathQuery (optional) quick xpath query
  727.      * @param  boolean $in_movePointer (optional) move internal pointer
  728.      *
  729.      * @access public
  730.      * @return boolean success {or XML_XPath_Error exception}
  731.      */
  732.     function removeAttribute($in_name$in_xpathQuery = null$in_movePointer = false)
  733.     {
  734.         if (!$this->pointer{
  735.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  736.         }
  737.  
  738.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODE)))) {
  739.             return $result;
  740.         }
  741.  
  742.         $result $this->pointer->remove_attribute($in_name);
  743.  
  744.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  745.             $this->_restore_bookmark();
  746.         }
  747.  
  748.         return $result;
  749.     }
  750.  
  751.     // }}}
  752.     // {{{ string  substringData()
  753.  
  754.     /**
  755.      * Extracts a range of data from the node.  Takes an offset and a count, which are optional
  756.      * and will default to retrieving the whole string.  If an XML_ELEMENT_NODE provided, then
  757.      * it first concats all the adjacent text nodes recusively and works on those.
  758.      * ??? implement wholeText() which concats all text nodes adjacent to a text node ???
  759.      *
  760.      * @param int  $in_offset offset of substring to extract
  761.      * @param int  $in_count length of substring to extract
  762.      * @param string $in_xpathQuery (optional) quick xpath query
  763.      * @param boolean $in_movePointer (optional) move internal pointer
  764.      *
  765.      * @access public
  766.      * @return string substring of the character data {or XML_XPath_Error exception}
  767.      */
  768.     function substringData($in_offset = 0$in_count = 0$in_xpathQuery = null$in_movePointer = false)
  769.     {
  770.         if (!$this->pointer{
  771.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  772.         }
  773.  
  774.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_TEXT_NODEXML_ELEMENT_NODEXML_CDATA_SECTION_NODEXML_COMMENT_NODE)))) {
  775.             return $result;
  776.         }
  777.  
  778.         if (!is_int($in_count|| $in_count < 0{
  779.             $return = PEAR::raiseError(nullXML_XPATH_INDEX_SIZEnullE_USER_WARNING"Count: $in_offset"'XML_XPath_Error'true);
  780.         }
  781.         elseif (!is_int($in_offset|| $in_offset < 0 || $in_offset strlen($content $this->pointer->get_content())) {
  782.             $return = PEAR::raiseError(nullXML_XPATH_INDEX_SIZEnullE_USER_WARNING"Offset: $in_offset"'XML_XPath_Error'true);
  783.         }
  784.         else {
  785.             $return $in_count substr($content$in_offset$in_count:
  786.                                   substr($content$in_offset);
  787.         }
  788.  
  789.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  790.             $this->_restore_bookmark();
  791.         }
  792.  
  793.         return $return;
  794.     }
  795.  
  796.     // }}}
  797.     // {{{ void    insertData()
  798.  
  799.     /**
  800.      * Will insert data at offset for a text node.
  801.      *
  802.      * @param string  $in_content content to be inserted
  803.      * @param int     $in_offset offset to insert data
  804.      * @param string  $in_xpathQuery (optional) quick xpath query
  805.      * @param boolean $in_movePointer (optional) move internal pointer
  806.      *
  807.      * @access public
  808.      * @return void {or XML_XPath_Error exception}
  809.      */
  810.     function insertData($in_content$in_offset = 0$in_xpathQuery = null$in_movePointer = false)
  811.     {
  812.         return $this->_set_content($in_content$in_xpathQuery$in_movePointerfalse$in_offset);
  813.     }
  814.  
  815.     // }}}
  816.     // {{{ void    deleteData()
  817.  
  818.     /**
  819.      * Will delete data at offset and for count for a text node.
  820.      *
  821.      * @param int     $in_offset (optional) offset to delete data
  822.      * @param int     $in_count (optional) number of characters to delete
  823.      * @param string  $in_xpathQuery (optional) quick xpath query
  824.      * @param boolean $in_movePointer (optional) move internal pointer
  825.      *
  826.      * @access public
  827.      * @return void {or XML_XPath_Error exception}
  828.      */
  829.     function deleteData($in_offset = 0$in_count = 0$in_xpathQuery = null$in_movePointer = false)
  830.     {
  831.         return $this->_set_content(null$in_xpathQuery$in_movePointertrue$in_offset$in_count);
  832.     }
  833.  
  834.     // }}}
  835.     // {{{ void    replaceData()
  836.  
  837.     /**
  838.      * Will replace data at offset and for count with content
  839.      *
  840.      * @param string  $in_content content to insert
  841.      * @param int     $in_offset (optional) offset to replace data
  842.      * @param int     $in_count (optional) number of characters to replace
  843.      * @param string  $in_xpathQuery (optional) quick xpath query
  844.      * @param boolean $in_movePointer (optional) move internal pointer
  845.      *
  846.      * @access public
  847.      * @return void {or XML_XPath_Error exception}
  848.      */
  849.     function replaceData($in_content$in_offset = 0$in_count = 0$in_xpathQuery = null$in_movePointer = false)
  850.     {
  851.         return $this->_set_content($in_content$in_xpathQuery$in_movePointertrue$in_offset$in_count);
  852.     }
  853.  
  854.     // }}}
  855.     // {{{  void    appendData()
  856.  
  857.     /**
  858.      * Will append data to end of text node.
  859.      *
  860.      * @param string  $in_content content to append
  861.      * @param string  $in_xpathQuery (optional) quick xpath query
  862.      * @param boolean $in_movePointer (optional) move internal pointer
  863.      *
  864.      * @access public
  865.      * @return void {or XML_XPath_Error exception}
  866.      */
  867.     function appendData($in_content$in_xpathQuery = null$in_movePointer = false)
  868.     {
  869.         return $this->_set_content($in_content$in_xpathQuery$in_movePointerfalsenull);
  870.     }
  871.  
  872.     // }}}
  873.     // {{{ object  replaceChild()
  874.  
  875.     /**
  876.      * Replaces the old child with the new child.  If the new child is already in the document,
  877.      * it is first removed (not implemented yet).  If the new child is a document fragment, then
  878.      * all of the nodes are inserted in the location of the old child.
  879.      *
  880.      * @param mixed   $in_xmlData document fragment or node
  881.      * @param string  $in_xpathQuery (optional) quick xpath query
  882.      * @param boolean $in_movePointer (optional) move internal pointer
  883.      *
  884.      * @access public
  885.      * @return object pointer to old node {or XML_XPath_Error exception}
  886.      */
  887.     function replaceChild($in_xmlData$in_xpathQuery = null$in_movePointer = false)
  888.     {
  889.         if (!$this->pointer{
  890.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  891.         }
  892.  
  893.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODEXML_TEXT_NODEXML_COMMENT_NODEXML_CDATA_SECTION_NODEXML_PI_NODE)))) {
  894.             return $result;
  895.         }
  896.  
  897.         if (XML_XPath::isError($importedNodes $this->_build_fragment($in_xmlData))) {
  898.             if (!is_null($in_xpathQuery&& !$in_movePointer{
  899.                 $this->_restore_bookmark();
  900.             }
  901.  
  902.             return $importedNodes;
  903.         }
  904.  
  905.         $parent $this->pointer->parent_node();
  906.         $lastNodeIndex = sizeOf($importedNodes- 1;
  907.         // run through all the new nodes...on the last new node, run replace_child()
  908.         foreach($importedNodes as $index => $importedNode{
  909.             if ($index == $lastNodeIndex{
  910.                 $oldNode $parent->replace_child($importedNode$this->pointer);
  911.             }
  912.             else {
  913.                 $parent->insert_before($importedNode$this->pointer);
  914.             }
  915.         }
  916.  
  917.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  918.             $this->_restore_bookmark();
  919.         }
  920.  
  921.         return new XML_XPath_result(array($oldNode)XPATH_NODESETnull$this->ctx$this->xml);
  922.     }
  923.  
  924.     // }}}
  925.     // {{{ object  appendChild()
  926.  
  927.     /**
  928.      * Adds the node or document fragment to the end of the list of children.  If the node
  929.      * is already in the tree it is first removed (not sure if this works yet)
  930.      *
  931.      * @param mixed   $in_xmlData string document fragment or node
  932.      * @param string  $in_xpathQuery (optional) quick xpath query
  933.      * @param boolean $in_movePointer move internal pointer
  934.      *
  935.      * @access public
  936.      * @return object pointer to the first of the nodes appended {or XML_XPath_Error exception}
  937.      */
  938.     function appendChild($in_xmlData$in_xpathQuery = null$in_movePointer = false)
  939.     {
  940.         if (!$this->pointer{
  941.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  942.         }
  943.  
  944.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODEXML_DOCUMENT_NODE)))) {
  945.             return $result;
  946.         }
  947.  
  948.         // if this is a document node, make sure no root exists
  949.         if ($this->pointer->node_type(== XML_DOCUMENT_NODE && $this->xml->document_element()) {
  950.             if (!is_null($in_xpathQuery&& !$in_movePointer{
  951.                 $this->_restore_bookmark();
  952.             }
  953.  
  954.             return PEAR::raiseError(nullXML_DUPLICATE_ROOTnullE_USER_WARNINGnull'XML_XPath_Error'true);
  955.         }
  956.  
  957.         if (XML_XPath::isError($importedNodes $this->_build_fragment($in_xmlData))) {
  958.             return $importedNodes;
  959.         }
  960.  
  961.         foreach($importedNodes as $index => $importedNode{
  962.             $node $this->pointer->append_child($importedNode);
  963.             if ($index == 0{
  964.                 $newNode $node;
  965.             }
  966.         }
  967.  
  968.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  969.             $this->_restore_bookmark();
  970.         }
  971.  
  972.         return $newNode;
  973.     }
  974.  
  975.     // }}}
  976.     // {{{ object  insertBefore()
  977.  
  978.     /**
  979.      * Inserts the node before the current pointer.
  980.      *
  981.      * @param mixed   $in_xmlData either a document fragment xml string or a node
  982.      * @param string  $in_xpathQuery (optional) quick xpath query
  983.      * @param boolean $in_movePointer (optional) move internal pointer
  984.      *
  985.      * @access public
  986.      * @return object pointer to the first of the new inserted nodes
  987.      */
  988.     function insertBefore($in_xmlData$in_xpathQuery = null$in_movePointer = false)
  989.     {
  990.         if (!$this->pointer{
  991.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  992.         }
  993.  
  994.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODE)))) {
  995.             return $result;
  996.         }
  997.  
  998.         // we do some fance stuff here to make this general...make a fake node
  999.         $importedNodes $this->_build_fragment($in_xmlData);
  1000.         if (XML_XPath::isError($importedNodes)) {
  1001.             return $importedNodes;
  1002.         }
  1003.  
  1004.         $parent $this->pointer->parent_node();
  1005.         foreach($importedNodes as $index => $importedNode{
  1006.             $node $parent->insert_before($importedNode$this->pointer);
  1007.             if ($index == 0{
  1008.                 $newNode $node;
  1009.             }
  1010.         }
  1011.  
  1012.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  1013.             $this->_restore_bookmark();
  1014.         }
  1015.  
  1016.         return $newNode;
  1017.     }
  1018.  
  1019.     // }}}
  1020.     // {{{ object  removeChild()
  1021.  
  1022.     /**
  1023.      * Removes the child node at the current pointer and returns it.
  1024.      *
  1025.      * This function will remove a child from the list of children and will
  1026.      * move the pointer to the parent node.
  1027.      *
  1028.      * @param string  $in_xpathQuery (optional) quick xpath query
  1029.      * @param boolean $in_movePointer (optional) move internal pointer
  1030.      *
  1031.      * @access public
  1032.      * @return object cloned node of the removed node, ready to be put in another document
  1033.      */
  1034.     function removeChild($in_xpathQuery = null$in_movePointer = false)
  1035.     {
  1036.         if (!$this->pointer{
  1037.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  1038.         }
  1039.  
  1040.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODEXML_TEXT_NODEXML_COMMENT_NODEXML_PI_NODEXML_CDATA_SECTION_NODE)))) {
  1041.             return $result;
  1042.         }
  1043.  
  1044.         // get the parent
  1045.         $parent $this->pointer->parent_node();
  1046.         // remove the child
  1047.         $removedNode $parent->remove_child($this->pointer);
  1048.         // set pointer to the parent
  1049.         $this->pointer = $parent;
  1050.  
  1051.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  1052.             $this->_restore_bookmark();
  1053.         }
  1054.  
  1055.         return new XML_XPath_result(array($removedNode)XPATH_NODESETnull$this->ctx$this->xml);
  1056.     }
  1057.  
  1058.     // }}}
  1059.     // {{{ object  cloneNode()
  1060.  
  1061.     /**
  1062.      * Clones the node and return the node as a result object
  1063.      *
  1064.      * @param bool    $in_deep (optional) clone node children
  1065.      * @param string  $in_xpathQuery (optional) quick xpath query
  1066.      * @param boolean $in_movePointer (optional) move internal pointer
  1067.      *
  1068.      * @access public
  1069.      * @return object cloned node of the current node, ready to be put in another document
  1070.      */
  1071.     function cloneNode($in_deep = false$in_xpathQuery = null$in_movePointer = false)
  1072.     {
  1073.         if (!$this->pointer{
  1074.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  1075.         }
  1076.  
  1077.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointer))) {
  1078.             return $result;
  1079.         }
  1080.  
  1081.         $clonedNode $this->pointer->clone_node($in_deep);
  1082.  
  1083.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  1084.             $this->_restore_bookmark();
  1085.         }
  1086.  
  1087.         return new XML_XPath_result(array($clonedNode)XPATH_NODESETnull$this->ctx$this->xml);
  1088.     }
  1089.     // }}}
  1090.     // {{{ void    replaceChildren()
  1091.  
  1092.     /**
  1093.      * Not in the DOM specification, but certainly a convenient function.  Allows you to pass
  1094.      * in an xml document fragment which will be parsed into an xml object and merged into the
  1095.      * xml document, replacing all the previous children of the node.  It does this by shallow
  1096.      * cloning the node, restoring the attributes and then adding the parsed children.
  1097.      *
  1098.      * @param string  $in_fragment xml fragment which will be merged into tree
  1099.      * @param string  $in_xpathQuery (optional) quick xpath query
  1100.      * @param boolean $in_movePointer (optional) move internal pointer
  1101.      *
  1102.      * @access public
  1103.      * @return void {or XML_XPath_Error exception}
  1104.      */
  1105.     function replaceChildren($in_xmlData$in_xpathQuery = null$in_movePointer = false)
  1106.     {
  1107.         if (!$this->pointer{
  1108.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  1109.         }
  1110.  
  1111.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODE)))) {
  1112.             return $result;
  1113.         }
  1114.  
  1115.         if (XML_XPath::isError($importedNodes $this->_build_fragment($in_xmlData))) {
  1116.             if (!is_null($in_xpathQuery&& !$in_movePointer{
  1117.                 $this->_restore_bookmark();
  1118.             }
  1119.  
  1120.             return $importedNodes;
  1121.         }
  1122.  
  1123.         // nix all the children by overwriting node and fixing attributes
  1124.         $attributes $this->pointer->has_attributes($this->pointer->attributes(: array();
  1125.         $this->pointer->replace_node($clone $this->pointer->clone_node());
  1126.         $this->pointer = $clone;
  1127.  
  1128.         foreach($attributes as $attributeNode{
  1129.            // waiting on set_attribute_node() to work here
  1130.            $this->pointer->set_attribute($attributeNode->node_name()$attributeNode->value());
  1131.         }
  1132.  
  1133.         foreach ($importedNodes as $importedNode{
  1134.             $this->pointer->append_child($importedNode);
  1135.         }
  1136.  
  1137.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  1138.             $this->_restore_bookmark();
  1139.         }
  1140.     }
  1141.  
  1142.     // }}}
  1143.     // {{{ string  dumpChildren()
  1144.  
  1145.     /**
  1146.      * Returns all the contents of an element node, regardless of type, as is.
  1147.      *
  1148.      * @param string  $in_xpathQuery quick xpath query
  1149.      * @param boolean $in_movePointer move internal pointer
  1150.      *
  1151.      * @access public
  1152.      * @return string xml string, a concatenation of all the children of the element node
  1153.      */
  1154.     function dumpChildren($in_xpathQuery = null$in_movePointer = false$in_format = true)
  1155.     {
  1156.         if (!$this->pointer{
  1157.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  1158.         }
  1159.  
  1160.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODE)))) {
  1161.             return $result;
  1162.         }
  1163.  
  1164.         $xmlString trim($this->xml->dump_node($this->pointer$in_format));
  1165.         $xmlString substr($xmlString,strpos($xmlString,'>')+1,-(strlen($this->nodeName())+3));
  1166.  
  1167.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  1168.             $this->_restore_bookmark();
  1169.         }
  1170.  
  1171.         return $xmlString;
  1172.     }
  1173.  
  1174.     // }}}
  1175.     // {{{ void    toFile()
  1176.  
  1177.     /**
  1178.      * Exports the xml document to a file.  Only works for the whole document right now.
  1179.      *
  1180.      * @param  file  $in_file file to export the xml to
  1181.      * @param  int   $in_compression (optional) ratio of compression using zlib (0-9)
  1182.      *
  1183.      * @access public
  1184.      * @return void {or XML_XPath_Error exception}
  1185.      */
  1186.     function toFile($in_file$in_compression = 0)
  1187.     {
  1188.         // If the file does not exist, make sure we can write in this directory
  1189.         if (!file_exists($in_file)) {
  1190.             if (!is_writable(dirname($in_file))) {
  1191.                 return PEAR::raiseError(nullXML_XPATH_FILE_NOT_WRITABLEnullE_USER_WARNING"File: $in_file"'XML_XPath_Error'true);
  1192.             }
  1193.         }
  1194.         // If the file exists, make sure we can overwrite it
  1195.         else {
  1196.             if (!is_writable($in_file)) {
  1197.                 return PEAR::raiseError(nullXML_XPATH_FILE_NOT_WRITABLEnullE_USER_WARNING"File: $in_file"'XML_XPath_Error'true);
  1198.             }
  1199.         }
  1200.  
  1201.         if (!(is_int($in_compression&& $in_compression >= 0 && $in_compression <= 9)) {
  1202.             $in_compression = 0;
  1203.         }
  1204.         $this->xml->dump_mem_file($in_file$in_compression);
  1205.         return true;
  1206.     }
  1207.  
  1208.     // }}}
  1209.     // {{{ string  toString()
  1210.  
  1211.     /**
  1212.      * Export the xml document to a string, beginning from the pointer.
  1213.      *
  1214.      * @param string  $in_xpathQuery quick xpath query
  1215.      * @param boolean $in_movePointer move internal pointer
  1216.      * @param boolean $in_format reformat using xmllint --format
  1217.      *
  1218.      * @access public
  1219.      * @return string xml string, starting at pointer
  1220.      */
  1221.     function toString($in_xpathQuery = null$in_movePointer = false$in_format = true)
  1222.     {
  1223.         if (!$this->pointer{
  1224.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  1225.         }
  1226.  
  1227.         if ($hasQuery !is_null($in_xpathQuery&& XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointer))) {
  1228.             return $result;
  1229.         }
  1230.  
  1231.         if ($this->nodeType(== XML_DOCUMENT_NODE{
  1232.             $xmlString $this->xml->dump_mem($in_format);
  1233.         }
  1234.         else {
  1235.             $xmlString $this->xml->dump_node($this->pointer$in_format);
  1236.         }
  1237.         /*
  1238.         if ($in_format) {
  1239.             $xmlString = escapeshellarg($xmlString);
  1240.             $xmlString = `echo $xmlString | {$this->xmllint} --format - 2>&1`;
  1241.         }*/
  1242.  
  1243.         if ($hasQuery && !$in_movePointer{
  1244.             $this->_restore_bookmark();
  1245.         }
  1246.  
  1247.         return $xmlString;
  1248.     }
  1249.  
  1250.     // }}}
  1251.     // {{{ object  getPointer()
  1252.  
  1253.     /**
  1254.      * Get the current pointer in the xml document.
  1255.      *
  1256.      * @access public
  1257.      * @return object current pointer object
  1258.      */
  1259.     function getPointer()
  1260.     {
  1261.         return $this->pointer;
  1262.     }
  1263.  
  1264.     // }}}
  1265.     // {{{ object  setPointer()
  1266.  
  1267.     /**
  1268.      * Set the pointer in the xml document
  1269.      *
  1270.      * @param  object  $in_node node to move to in the xml document
  1271.      *
  1272.      * @access public
  1273.      * @return void {or XML_XPath_Error exception}
  1274.      */
  1275.     function setPointer($in_node)
  1276.     {
  1277.         // if this is an error object, just return it
  1278.         if (XML_XPath::isError($in_node)) {
  1279.             return $in_node;
  1280.         }
  1281.         elseif (!$this->_is_dom_node($in_node)) {
  1282.             return PEAR::raiseError(nullXML_XPATH_NODE_REQUIREDnullE_USER_WARNING$in_node'XML_XPath_Error'true);
  1283.         }
  1284.         else {
  1285.             // we are okay, set the node and return true
  1286.             $this->pointer = $in_node;
  1287.             return true;
  1288.         }
  1289.     }
  1290.  
  1291.     // }}}
  1292.     // {{{ string  getNodePath()
  1293.  
  1294.     /**
  1295.      * Resolve the xpath location of the current node
  1296.      *
  1297.      * @param  string $in_xpathQuery (optional)
  1298.      * @param  boolean $in_movePointer (optional)
  1299.      *
  1300.      * @return string xpath location query
  1301.      * @access public
  1302.      */
  1303.     function getNodePath($in_node)
  1304.     {
  1305.         if (!$in_node{
  1306.             return null;
  1307.         }
  1308.         elseif (!$this->_is_dom_node($in_node)) {
  1309.             return PEAR::raiseError(nullXML_XPATH_NODE_REQUIREDnullE_USER_WARNING$in_node'XML_XPath_Error'true);
  1310.         }
  1311.  
  1312.         $buffer '';
  1313.         $cur $in_node;
  1314.         do {
  1315.             $name '';
  1316.             $sep '/';
  1317.             $occur = 0;
  1318.             if (($type $cur->node_type()) == XML_DOCUMENT_NODE{
  1319.                 if ($buffer[0== '/'{
  1320.                     break;
  1321.                 }
  1322.  
  1323.                 $next = false;
  1324.             }
  1325.             else if ($type == XML_ATTRIBUTE_NODE{
  1326.                 $sep .= '@';
  1327.                 $name $cur->node_name();
  1328.                 $next $cur->parent_node();
  1329.             }
  1330.             else {
  1331.                 $name $cur->node_name();
  1332.                 $next $cur->parent_node();
  1333.  
  1334.                 // now figure out the index
  1335.                 $tmp $cur->previous_sibling();
  1336.                 while ($tmp != false{
  1337.                     if ($name == $tmp->node_name()) {
  1338.                         $occur++;
  1339.                     }
  1340.                     $tmp $tmp->previous_sibling();
  1341.                 }
  1342.  
  1343.                 $occur++;
  1344.  
  1345.                 if ($type == XML_ELEMENT_NODE{
  1346.                     // this is a hack and only works for some cases
  1347.                     // we can have to nodes with the same name but different namespace,
  1348.                     // so this should actually go above
  1349.                     if (($prefix $cur->prefix()) != ''{
  1350.                         $name $prefix ':' $name;
  1351.                     }
  1352.                 }
  1353.                 // fix the names for those nodes where xpath query and dom node name don't match
  1354.                 elseif ($type == XML_COMMENT_NODE{
  1355.                     $name 'comment()';
  1356.                 }
  1357.                 elseif ($type == XML_PI_NODE{
  1358.                     $name 'processing-instruction()';
  1359.                 }
  1360.                 elseif ($type == XML_TEXT_NODE{
  1361.                     $name 'text()';
  1362.                 }
  1363.                 // anything left here has not been coded yet (cdata is broken)
  1364.                 else {
  1365.                     $name '';
  1366.                     $sep '';
  1367.                     $occur = 0;
  1368.                 }
  1369.             }
  1370.  
  1371.             if ($occur == 0{
  1372.                 $buffer $sep $name $buffer;
  1373.             }
  1374.             else {
  1375.                 $buffer $sep $name '[' $occur ']' $buffer;
  1376.             }
  1377.  
  1378.             $cur $next;
  1379.  
  1380.         while ($cur != false);
  1381.  
  1382.         return $buffer;
  1383.     }
  1384.  
  1385.     // }}}
  1386.     // {{{ mixed   getOne()
  1387.  
  1388.     /**
  1389.      * A quick version of the evaluate, where the results are returned immediately. This
  1390.      * function is equivalent to xsl:value-of select in every way.
  1391.      *
  1392.      * @param  string  $in_xpathQuery (optional) quick xpath query
  1393.      * @param  boolean $in_movePointer (optional) move internal pointer
  1394.      *
  1395.      * @access public
  1396.      * @return mixed number of nodes or value of scalar result {or XML_XPath_Error exception}
  1397.      */
  1398.     function getOne($in_xpathQuery$in_movePointer = false)
  1399.     {
  1400.         // Execute the xpath query and return the results, then reset the result index
  1401.         if (XML_XPath::isError($result $this->evaluate($in_xpathQuery$in_movePointer))) {
  1402.             return $result;
  1403.         }
  1404.  
  1405.         return $result->getData();
  1406.     }
  1407.  
  1408.     // }}}
  1409.     // {{{ void    evaluate()
  1410.  
  1411.     /**
  1412.      * Evaluate the xpath expression on the loaded xml document.
  1413.      *
  1414.      * The xpath query provided is evaluated and either an XML_XPath_result object is
  1415.      * returned, or, if the pointer is being moved, it acts like a glorified step function
  1416.      * and moves the pointer to the specified node (or first node if it is a set) and returns
  1417.      * a boolean success
  1418.      *
  1419.      * @param  string  $in_xpathQuery xpath query
  1420.      * @param  boolean $in_movePointer (optional) move internal pointer
  1421.      *
  1422.      * @access public
  1423.      * @return mixed result object or boolean success (for move pointer)
  1424.      * @throws XML_XPath_error XML_XPATH_NOT_LOADED
  1425.      */
  1426.     function &evaluate($in_xpathQuery$in_movePointer = false)
  1427.     {
  1428.         // Make sure we have loaded an xml document and were able to create an xpath context
  1429.         if (strtolower(get_class($this->ctx)) != 'xpathcontext'{
  1430.             return PEAR::raiseError(nullXML_XPATH_NOT_LOADEDnullE_USER_ERRORnull'XML_XPath_Error'true);
  1431.         }
  1432.  
  1433.         // enable relative xpath queries (I don't check a valid dom object yet)
  1434.         settype($in_xpathQuery'array');
  1435.         if (isset($in_xpathQuery[1])) {
  1436.             $sep '/';
  1437.             // those double slashes cause an anomally
  1438.             if (substr($in_xpathQuery[1]02== '//'{
  1439.                 $sep '';
  1440.             }
  1441.  
  1442.             if ($in_xpathQuery[0== 'current()' || $in_xpathQuery[0== '.'{
  1443.                 $in_xpathQuery[0$this->getNodePath($this->pointer);
  1444.             }
  1445.             elseif ($in_xpathQuery[0== 'parent::node()' || $in_xpathQuery[0== '..'{
  1446.                 if ($this->pointer->node_type(!= XML_DOCUMENT_NODE{
  1447.                     $in_xpathQuery[0$this->getNodePath($this->pointer->parent_node());
  1448.                 }
  1449.                 else {
  1450.                     $in_xpathQuery[0$this->getNodePath($this->pointer);
  1451.                 }
  1452.             }
  1453.             else {
  1454.                 $in_xpathQuery[0$this->getNodePath($in_xpathQuery[0]);
  1455.             }
  1456.  
  1457.             // handle or statements and construct query
  1458.             $parts explode('|'$in_xpathQuery[1]);
  1459.             $xpathQuery $in_xpathQuery[0$sep implode('|' $in_xpathQuery[0$sep$parts);
  1460.         }
  1461.         else {
  1462.             $xpathQuery reset($in_xpathQuery);
  1463.         }
  1464.  
  1465.         // we don't care if this messes up, we will let the result object handle no results
  1466.         $result @xpath_eval($this->ctx$xpathQuery);
  1467.  
  1468.         // if we are moving the pointer, return boolean success just like the step functions
  1469.         if ($in_movePointer{
  1470.             if ($result->type == XPATH_NODESET && !empty($result->nodeset)) {
  1471.                 $this->pointer = reset($result->nodeset);
  1472.                 return true;
  1473.             }
  1474.             else {
  1475.                 return false;
  1476.             }
  1477.         }
  1478.  
  1479.         $ret = new XML_XPath_result(
  1480.             $result->type == XPATH_NODESET ? $result->nodeset : $result->value,
  1481.             $result->type,
  1482.             $xpathQuery,
  1483.             $this->ctx,
  1484.             $this->xml
  1485.         );
  1486.         return $ret;
  1487.     }
  1488.  
  1489.     // }}}
  1490.     // {{{ _build_fragment()
  1491.  
  1492.     /**
  1493.      * For functions which take a document fragment I have a general way to import the data
  1494.      * into a nodeset and then I return the nodeset.  If the xml data was already a node, I
  1495.      * just cast it to a single element array so the return type is consistent.
  1496.      *
  1497.      * @param mixed  $in_xmlData either document fragment string or dom node
  1498.      *
  1499.      * @access private
  1500.      * @return array nodeset array
  1501.      */
  1502.     function _build_fragment($in_xmlData)
  1503.     {
  1504.         if ($this->_is_dom_node($in_xmlData)) {
  1505.             $fakeChildren = array($in_xmlData);
  1506.         }
  1507.         else {
  1508.             $fake @domxml_open_mem('<fake>'.$in_xmlData.'</fake>');
  1509.             if (!$fake{
  1510.                 return PEAR::raiseError(nullXML_PARSE_ERRORnullE_USER_WARNING$in_xmlData'XML_XPath_Error'true);
  1511.             }
  1512.             $fakeRoot $fake->document_element();
  1513.             $fakeChildren $fakeRoot->has_child_nodes($fakeRoot->child_nodes(: array();
  1514.         }
  1515.         return $fakeChildren;
  1516.     }
  1517.  
  1518.     // }}}
  1519.     // {{{ _set_content()
  1520.  
  1521.     /**
  1522.      * Generic function to handle manipulation of a data string based on manipulation parameters.
  1523.      *
  1524.      * @param string  $in_content data to be added
  1525.      * @param boolean $in_replace method of manipulation
  1526.      * @param int     $in_offset offset of manipulation
  1527.      * @param int     $in_count length of manipulation
  1528.      *
  1529.      * @access private
  1530.      * @return object XML_XPath_Error on fail
  1531.      */
  1532.     function _set_content($in_content$in_xpathQuery$in_movePointer$in_replace$in_offset = 0$in_count = 0)
  1533.     {
  1534.         if (!$this->pointer{
  1535.             return PEAR::raiseError(nullXML_XPATH_NULL_POINTERnullE_USER_WARNING'''XML_XPath_Error'true);
  1536.         }
  1537.  
  1538.         if (XML_XPath::isError($result $this->_quick_evaluate_init($in_xpathQuery$in_movePointerarray(XML_ELEMENT_NODEXML_TEXT_NODEXML_CDATA_SECTION_NODEXML_COMMENT_NODEXML_PI_NODE)))) {
  1539.             return $result;
  1540.         }
  1541.  
  1542.         $data $this->pointer->get_content();
  1543.         // little hack to get appendData to use this function here...special little exception
  1544.         $in_offset is_null($in_offsetstrlen($data$in_offset;
  1545.         if (!is_int($in_offset|| $in_offset < 0 || $in_offset strlen($data)) {
  1546.             $return = PEAR::raiseError(nullXML_XPATH_INDEX_SIZEnullE_USER_WARNING"Offset: $in_offset"'XML_XPath_Error'true);
  1547.         }
  1548.         elseif (!is_int($in_count|| $in_count < 0{
  1549.             $return = PEAR::raiseError(nullXML_XPATH_INDEX_SIZEnullE_USER_WARNING"Count: $in_offset"'XML_XPath_Error'true);
  1550.         }
  1551.         else {
  1552.             if ($in_replace{
  1553.                 $data $in_count substr($data0$in_offset$in_content substr($data$in_offset $in_countsubstr($data0$in_offset$in_content;
  1554.             }
  1555.             else {
  1556.                 $data substr($data0$in_offset$in_content substr($data$in_offset);
  1557.             }
  1558.  
  1559.             if ($this->pointer->node_type(== XML_ELEMENT_NODE{
  1560.                 $this->replaceChildren($data);
  1561.             }
  1562.             else {
  1563.                 $this->pointer->replace_node($this->xml->create_text_node($data));
  1564.             }
  1565.  
  1566.             $return = null;
  1567.         }
  1568.  
  1569.         if (!is_null($in_xpathQuery&& !$in_movePointer{
  1570.             $this->_restore_bookmark();
  1571.         }
  1572.  
  1573.         return $return;
  1574.     }
  1575.  
  1576.     // }}}
  1577.     // {{{ _is_dom_node()
  1578.  
  1579.     /**
  1580.      * Determines if the provided object is a domnode descendent.
  1581.      *
  1582.      * @param  object  $in_object object in question
  1583.      *
  1584.      * @access private
  1585.      * @return boolean whether the object is a domnode
  1586.      */
  1587.     function _is_dom_node($in_object)
  1588.     {
  1589.         return (is_object($in_object&& is_a_php_class($in_object'domnode'));
  1590.     }
  1591.  
  1592.     // }}}
  1593.     // {{{ _restore_bookmark()
  1594.  
  1595.     /**
  1596.      * Restore the internal pointer after a quick query operation
  1597.      *
  1598.      * @access private
  1599.      * @return void 
  1600.      */
  1601.     function _restore_bookmark()
  1602.     {
  1603.         $this->pointer = $this->bookmark;
  1604.     }
  1605.  
  1606.     // }}}
  1607.     // {{{ _quick_evaluate_init()
  1608.  
  1609.     /**
  1610.      * The function will allow an on the quick xpath query to move the internal pointer before
  1611.      * invoking the xmldom function.  The requirements are that the xpath query must return
  1612.      * an XPATH_NODESET and have at least one node.  If not, an XML_XPath_Error will be returned
  1613.      * ** In addition this function does a check on the correct nodeType to run the caller method
  1614.      *
  1615.      * @param string  $in_xpathQuery optional xpath query to move the internal pointer
  1616.      * @param boolean $in_movePointer move the pointer temporarily or permanently
  1617.      * @param array   $in_nodeTypes required nodeType list for the caller method
  1618.      *
  1619.      * @access private
  1620.      * @return boolean true on success, XML_XPath_Error on error
  1621.      */
  1622.     function _quick_evaluate_init($in_xpathQuery = null$in_movePointer = false$in_nodeTypes = null)
  1623.     {
  1624.         // we don't need to check for null, since false or 0 is not a valid query anyway
  1625.         if ($in_xpathQuery{
  1626.             if (!is_object($in_xpathQuery)) {
  1627.                 // doing the following manually (without evaluate()) is critical for speed
  1628.  
  1629.                 // Make sure we have an xpath context (mildly costly)
  1630.                 if (strtolower(get_class($this->ctx)) != 'xpathcontext'{
  1631.                     return PEAR::raiseError(nullXML_XPATH_NOT_LOADEDnullE_USER_ERRORnull'XML_XPath_Error'true);
  1632.                 }
  1633.  
  1634.                 // enable relative xpath queries (I don't check a valid dom object yet)
  1635.                 settype($in_xpathQuery'array');
  1636.                 if (isset($in_xpathQuery[1])) {
  1637.                     $sep '/';
  1638.                     // those double slashes cause an anomally
  1639.                     if (substr($in_xpathQuery[1]02== '//'{
  1640.                         $sep '';
  1641.                     }
  1642.  
  1643.                     if ($in_xpathQuery[0== 'current()' || $in_xpathQuery[0== '.'{
  1644.                         $in_xpathQuery[0$this->getNodePath($this->pointer);
  1645.                     }
  1646.                     elseif ($in_xpathQuery[0== 'parent::node()' || $in_xpathQuery[0== '..'{
  1647.                         if ($this->pointer->node_type(!= XML_DOCUMENT_NODE{
  1648.                             $in_xpathQuery[0$this->getNodePath($this->pointer->parent_node());
  1649.                         }
  1650.                         else {
  1651.                             $in_xpathQuery[0$this->getNodePath($this->pointer);
  1652.                         }
  1653.                     }
  1654.                     else {
  1655.                         $in_xpathQuery[0$this->getNodePath($in_xpathQuery[0]);
  1656.                     }
  1657.  
  1658.                     $xpathQuery $in_xpathQuery[0$sep $in_xpathQuery[1];
  1659.                 }
  1660.                 else {
  1661.                     $xpathQuery reset($in_xpathQuery);
  1662.                 }
  1663.  
  1664.                 if (!$result @xpath_eval($this->ctx$xpathQuery)) {
  1665.                     return PEAR::raiseError(nullXML_XPATH_INVALID_QUERYnullE_USER_WARNING"XML_XPath query: $xpathQuery"'XML_XPath_Error'true);
  1666.                 }
  1667.  
  1668.                 if (empty($result->nodeset|| $result->type != XPATH_NODESET{
  1669.                     return PEAR::raiseError(nullXML_XPATH_INVALID_NODESETnullE_USER_WARNING"XML_XPath query: $xpathQuery"'XML_XPath_Error'true);
  1670.                 }
  1671.  
  1672.                 // this takes the first result (too bad if you had more)
  1673.                 $tmpPointer reset($result->nodeset);
  1674.             }
  1675.             // a bit costly, so we put it second
  1676.             elseif ($this->_is_dom_node($in_xpathQuery)) {
  1677.                 $tmpPointer $in_xpathQuery;
  1678.             }
  1679.             else {
  1680.                 return PEAR::raiseError(nullXML_XPATH_INVALID_QUERYnullE_USER_WARNING"XML_XPath query: $in_xpathQuery"'XML_XPath_Error'true);
  1681.             }
  1682.  
  1683.             // if we are moving the internal pointer, then do it now
  1684.             if ($in_movePointer{
  1685.                 $this->pointer = $tmpPointer;
  1686.             }
  1687.             // set the bookmark if we only want to temporarily move the pointer
  1688.             // this area is too critical to call class methods...we have to be nasty
  1689.             else {
  1690.                 $this->bookmark = $this->pointer;
  1691.                 $this->pointer = $tmpPointer;
  1692.             }
  1693.         }
  1694.  
  1695.         // see if we have a restricted nodeType requirement (negligible time)
  1696.         if (is_array($in_nodeTypes&& !in_array($nodeType $this->pointer->node_type()$in_nodeTypes)) {
  1697.             if (!is_null($in_xpathQuery&& !$in_movePointer{
  1698.                 $this->_restore_bookmark();
  1699.             }
  1700.  
  1701.             return PEAR::raiseError(nullXML_XPATH_INVALID_NODETYPEnullE_USER_WARNING"Required type: ".implode(" or "$in_nodeTypes).", Provided type: ".$nodeType'XML_XPath_Error'true);
  1702.         }
  1703.         else {
  1704.             return true;
  1705.         }
  1706.     }
  1707.  
  1708.     // }}}
  1709. }
  1710. ?>

Documentation generated on Mon, 11 Mar 2019 14:44:42 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.