home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / XML / XPath / common.php next >
Encoding:
PHP Script  |  2008-07-02  |  59.8 KB  |  1,712 lines

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