cvs: pear /XML_XPath TODO XPath.php /XML_XPath/XPath common.php result.php /XML_XPath/docs XML_XPath_example.php
| From: | Daniel Allen | Date: | Wed, 12 Jun 2002 21:23:55 +0000 |
| Subject: | cvs: pear /XML_XPath TODO XPath.php /XML_XPath/XPath common.php result.php /XML_XPath/docs XML_XPath_example.php | ||
| Groups: | php.pear.cvs | ||
| Request: | Send a blank email to pear-cvs+get-4587@lists.php.net to get a copy of this message | ||
dallen Wed Jun 12 17:23:55 2002 EDT
Modified files:
/pear/XML_XPath TODO XPath.php
/pear/XML_XPath/XPath common.php result.php
/pear/XML_XPath/docs XML_XPath_example.php
Log:
- fixes for evaluate()
- move evaluate(), getOne() into common.php
@ added registerNamespace to temporarily fix the lack of registering namespaces in domxml
- bug fixes and code cleanup
Index: pear/XML_XPath/TODO diff -u pear/XML_XPath/TODO:1.4 pear/XML_XPath/TODO:1.5 --- pear/XML_XPath/TODO:1.4 Thu Jun 6 06:54:48 2002 +++ pear/XML_XPath/TODO Wed Jun 12 17:23:55 2002 @@ -19,21 +19,18 @@ [@] make an example.php and example.out for people to read through (Cooker) - [@] implement childNodes() to return an xml_xpath_result object + [@] implement childNodes() to return an xml_xpath_result object, Fix sorting in result + object for DOM queries [*] make a short test harness just so I know I don't commit bad shit [*] finally figured out substitute entities default, so see if we can make this usable in the class - [*] implement getElementsByTagName and getElementById since there are domxml functions - now...return as getElementsByTagName as XML_XPath_result objects..getElementById - will be a move function + [@] implement getElementsByTagName since there are domxml functions + now...return as getElementsByTagName as XML_XPath_result objects - [*] considering doing substringData for attribute nodes...just seems to make sense - - [*] in _quick_evaluate_init, make sure the pointer is not null, could happen coming - off of a results object + [@] always check that pointer is not NULL [@] functions for result object are now next() @@ -48,7 +45,19 @@ it moves from off the stage to on the stage the first time, then it moves to the next node after that - [@] relative xpath queries + [@] relative xpath queries in evaluate [*] fix this stdClass hack we have to do to get the result object to work with non xpath queries (in cases of using dom queries) + + [*] waiting on stripping of formatting on the way in + + [*] insert_before() now copies node before inserting, does this affect us? + + [@] when using getAttribute with the move = true, we should sit on the attribute node, not the parent element + + [@] manually do the xpath_eval in the quick xpath query function, much faster + + [@] elimiate _quick_evaluate_shutdown? to slow in critical areas + + [*] figure out a way to implement the create functions Index: pear/XML_XPath/XPath.php diff -u pear/XML_XPath/XPath.php:1.13 pear/XML_XPath/XPath.php:1.14 --- pear/XML_XPath/XPath.php:1.13 Fri Jun 7 19:17:21 2002 +++ pear/XML_XPath/XPath.php Wed Jun 12 17:23:55 2002 @@ -17,7 +17,7 @@ // | Authors: Dan Allen <dan@mojavelinux.com> | // +----------------------------------------------------------------------+ -// $Id: XPath.php,v 1.13 2002/06/07 23:17:21 dallen Exp $ +// $Id: XPath.php,v 1.14 2002/06/12 21:23:55 dallen Exp $ // }}} // {{{ description @@ -140,7 +140,7 @@ // not a valid xml instance, so throw error else { ob_end_clean(); - return PEAR::raiseError(null, XML_XPATH_INVALID_DOCUMENT, null, E_USER_ERROR, "The xml data '$in_xml' could not be parsed to xml dom", 'XML_XPath_Error', true); + return PEAR::raiseError(null, XML_XPATH_INVALID_DOCUMENT, null, E_USER_ERROR, "The xml $in_type '$in_xml' could not be parsed to xml dom", 'XML_XPath_Error', true); } $loadError = ob_get_contents(); ob_end_clean(); @@ -158,80 +158,25 @@ } // }}} - // {{{ mixed getOne() + // {{{ void registerNamespace() /** - * A quick version of the evaluate, where the results are returned immediately. This - * function is equivalent to xsl:value-of select in every way. + * This function is sort of temporary hack for registering namespace prefixes * - * @param string $in_xpathQuery (optional) quick xpath query - * @param boolean $in_movePointer (optional) move internal pointer + * The domxml functions should do this automatically when a domxml object is read in. + * For now, you can use this function. * - * @access public - * @return mixed number of nodes or value of scalar result {or XML_XPath_Error exception} - */ - function getOne($in_xpathQuery, $in_movePointer = false) - { - // Execute the xpath query and return the results, then reset the result index - if (XML_XPath::isError($result = $this->evaluate($in_xpathQuery, $in_movePointer))) { - return $result; - } - return $result->getData(); - } - - // }}} - // {{{ void evaluate() - - /** - * Evaluate the xpath expression on the loaded xml document. An XML_XPath_Result object is - * returned which can be used to manipulate the results - * - * @param string $in_xpathQuery xpath query - * @param boolean $in_movePointer (optional) move internal pointer + * @param array $in_namespaces * + * @return void * @access public - * @return object result object {or XML_XPath_Error exception} */ - function &evaluate($in_xpathQuery, $in_movePointer = false) + function registerNamespace($in_namespaces) { - // Make sure we have loaded an xml document and were able to create an xpath context - if (get_class($this->ctx) != 'XPathContext') { - return PEAR::raiseError(null, XML_XPATH_NOT_LOADED, null, E_USER_ERROR, null, 'XML_XPath_Error', true); + settype($in_namespaces, 'array'); + foreach($in_namespaces as $localName => $namespace) { + $this->ctx->xpath_register_ns($localName, $namespace); } - - // enable relative xpath queries (I don't check a valid dom object yet) - settype($in_xpathQuery, 'array'); - if (isset($in_xpathQuery[1])) { - $sep = '/'; - // those double slashes cause an anomally - if (substr($in_xpathQuery[0], 0, 2) == '//') { - $sep = ''; - } - - if ($in_xpathQuery[1] == 'current()') { - $in_xpathQuery[1] = $this->getNodePath($this->pointer); - } - else { - $in_xpathQuery[1] = $this->getNodePath($in_xpathQuery[1]); - } - - $xpathQuery = $in_xpathQuery[1] . $sep . $in_xpathQuery[0]; - } - else { - $xpathQuery = reset($in_xpathQuery); - } - - if (!$result = @xpath_eval($this->ctx, $xpathQuery)) { - return PEAR::raiseError(null, XML_XPATH_INVALID_QUERY, null, E_USER_WARNING, "XML_XPath query: $xpathQuery", 'XML_XPath_Error', true); - } - - $resultObj =& new XML_XPath_result($result->type == XPATH_NODESET ? $result->nodeset : $result->value, $result->type, $xpathQuery, $this->ctx); - - if ($in_movePointer && $result->type == XPATH_NODESET && !empty($result->nodeset)) { - $this->pointer = reset($result->nodeset); - } - - return $resultObj; } // }}} Index: pear/XML_XPath/XPath/common.php diff -u pear/XML_XPath/XPath/common.php:1.16 pear/XML_XPath/XPath/common.php:1.17 --- pear/XML_XPath/XPath/common.php:1.16 Fri Jun 7 19:17:21 2002 +++ pear/XML_XPath/XPath/common.php Wed Jun 12 17:23:55 2002 @@ -17,7 +17,7 @@ // | Authors: Dan Allen <dan@mojavelinux.com> | // +----------------------------------------------------------------------+ -// $Id: common.php,v 1.16 2002/06/07 23:17:21 dallen Exp $ +// $Id: common.php,v 1.17 2002/06/12 21:23:55 dallen Exp $ // }}} // {{{ description @@ -216,7 +216,7 @@ } $nodeset[] = $childNode; } - return new XML_XPath_result($nodeset, XPATH_NODESET, array($this->pointer, '/*'), $this->ctx); + return new XML_XPath_result($nodeset, XPATH_NODESET, array($this->pointer, '/*'), $this->ctx, $this->xml); } // }}} @@ -236,7 +236,7 @@ { // since we can't do an actual xpath query, we need to create a pseudo xpath result $nodeset = $this->xml->get_elements_by_tagname($in_tagName); - return new XML_XPath_result($nodeset, XPATH_NODESET, array(null, '//' . $in_tagName), $this->ctx); + return new XML_XPath_result($nodeset, XPATH_NODESET, array(null, '//' . $in_tagName), $this->ctx, $this->xml); } // }}} @@ -1286,7 +1286,12 @@ $occur++; if ($type == XML_ELEMENT_NODE) { - $name = $name; + // this is a hack and only works for some cases + // we can have to nodes with the same name but different namespace, + // so this should actually go above + if (($prefix = $cur->prefix()) != '') { + $name = $prefix . ':' . $name; + } } // fix the names for those nodes where xpath query and dom node name don't match elseif ($type == XML_COMMENT_NODE) { @@ -1305,6 +1310,7 @@ $occur = 0; } } + if ($occur == 0) { $buffer = $sep . $name . $buffer; } @@ -1320,6 +1326,88 @@ } // }}} + // {{{ mixed getOne() + + /** + * A quick version of the evaluate, where the results are returned immediately. This + * function is equivalent to xsl:value-of select in every way. + * + * @param string $in_xpathQuery (optional) quick xpath query + * @param boolean $in_movePointer (optional) move internal pointer + * + * @access public + * @return mixed number of nodes or value of scalar result {or XML_XPath_Error exception} + */ + function getOne($in_xpathQuery, $in_movePointer = false) + { + // Execute the xpath query and return the results, then reset the result index + if (XML_XPath::isError($result = $this->evaluate($in_xpathQuery, $in_movePointer))) { + return $result; + } + + return $result->getData(); + } + + // }}} + // {{{ void evaluate() + + /** + * Evaluate the xpath expression on the loaded xml document. An XML_XPath_Result object is + * returned which can be used to manipulate the results + * + * @param string $in_xpathQuery xpath query + * @param boolean $in_movePointer (optional) move internal pointer + * + * @access public + * @return object result object {or XML_XPath_Error exception} + */ + function &evaluate($in_xpathQuery, $in_movePointer = false) + { + // Make sure we have loaded an xml document and were able to create an xpath context + if (get_class($this->ctx) != 'XPathContext') { + return PEAR::raiseError(null, XML_XPATH_NOT_LOADED, null, E_USER_ERROR, null, 'XML_XPath_Error', true); + } + + // enable relative xpath queries (I don't check a valid dom object yet) + settype($in_xpathQuery, 'array'); + if (isset($in_xpathQuery[1])) { + $sep = '/'; + // those double slashes cause an anomally + if (substr($in_xpathQuery[1], 0, 2) == '//') { + $sep = ''; + } + + if ($in_xpathQuery[0] == 'current()' || $in_xpathQuery[0] == '.') { + $in_xpathQuery[0] = $this->getNodePath($this->pointer); + } + else { + $in_xpathQuery[0] = $this->getNodePath($in_xpathQuery[0]); + } + + $xpathQuery = $in_xpathQuery[0] . $sep . $in_xpathQuery[1]; + } + else { + $xpathQuery = reset($in_xpathQuery); + } + + // we don't care if this messes up, we will let the result object handle no results + $result = @xpath_eval($this->ctx, $xpathQuery); + + // if we are moving the pointer, return boolean success just like the step functions + if ($in_movePointer) { + if ($result->type == XPATH_NODESET && !empty($result->nodeset)) { + $this->pointer = reset($result->nodeset); + return true; + } + else { + return false; + } + } + + return new XML_XPath_result($result->type == XPATH_NODESET ? $result->nodeset : $result->value, $result->type, $xpathQuery, $this->ctx, $this->xml); + } + + // }}} // {{{ _build_fragment() /** @@ -1461,18 +1549,18 @@ if (isset($in_xpathQuery[1])) { $sep = '/'; // those double slashes cause an anomally - if (substr($in_xpathQuery[0], 0, 2) == '//') { + if (substr($in_xpathQuery[1], 0, 2) == '//') { $sep = ''; } - if ($in_xpathQuery[1] == 'current()') { - $in_xpathQuery[1] = $this->getNodePath($this->pointer); + if ($in_xpathQuery[0] == 'current()' || $in_xpathQuery[0] == '.') { + $in_xpathQuery[0] = $this->getNodePath($this->pointer); } else { - $in_xpathQuery[1] = $this->getNodePath($in_xpathQuery[1]); + $in_xpathQuery[0] = $this->getNodePath($in_xpathQuery[0]); } - $xpathQuery = $in_xpathQuery[1] . $sep . $in_xpathQuery[0]; + $xpathQuery = $in_xpathQuery[0] . $sep . $in_xpathQuery[1]; } else { $xpathQuery = reset($in_xpathQuery); Index: pear/XML_XPath/XPath/result.php diff -u pear/XML_XPath/XPath/result.php:1.10 pear/XML_XPath/XPath/result.php:1.11 --- pear/XML_XPath/XPath/result.php:1.10 Fri Jun 7 19:17:21 2002 +++ pear/XML_XPath/XPath/result.php Wed Jun 12 17:23:55 2002 @@ -17,7 +17,7 @@ // | Authors: Dan Allen <dan@mojavelinux.com> | // +----------------------------------------------------------------------+ -// $Id: result.php,v 1.10 2002/06/07 23:17:21 dallen Exp $ +// $Id: result.php,v 1.11 2002/06/12 21:23:55 dallen Exp $ // }}} // {{{ description @@ -35,9 +35,6 @@ define('XML_XPATH_SORT_NATURAL_DESCENDING', 6); // }}} -/** - I need to handle sort when result is retrieved using childNodes() - */ // {{{ class XML_XPath_result @@ -86,15 +83,21 @@ */ var $ctx; + /** + * domxml object, need for many common functions + * @var object $xml + */ + var $xml; // }}} // {{{ constructor - function XML_XPath_result($in_data, $in_type, $in_query, &$in_ctx) + function XML_XPath_result($in_data, $in_type, $in_query, &$in_ctx, &$in_xml) { $this->query = $in_query; $this->type = $in_type; $this->data = $in_data; $this->ctx = &$in_ctx; + $this->xml =&$in_xml; // move the pointer to the first node if at least one node in the result exists // for convience, just so we don't have to call nextNode() if we expect only one $this->rewind(); @@ -115,14 +118,21 @@ switch($this->type) { case XPATH_BOOLEAN: return $this->data ? true : false; - break; + break; + case XPATH_NODESET: - return $this->pointer->node_type() == XML_ATTRIBUTE_NODE ? $this->pointer->value() : $this->substringData(); - break; + if (!$this->pointer) { + return null; + } + else { + return $this->pointer->node_type() == XML_ATTRIBUTE_NODE ? $this->pointer->value() : $this->substringData(); + } + break; + case XPATH_STRING: case XPATH_NUMBER: return $this->data; - break; + break; } } @@ -423,6 +433,27 @@ } } + return false; + } + + // }}} + // {{{ object current() + + /** + * Retrieve current pointer + * + * If the result is a nodeset (which is the most common use of the result object) than + * this function returns the current pointer in the result array. + * + * @return object XML_XPath pointer + * @access public + */ + function current() + { + if (is_array($this->data)) { + return current($this->data); + } + return false; } Index: pear/XML_XPath/docs/XML_XPath_example.php diff -u pear/XML_XPath/docs/XML_XPath_example.php:1.1 pear/XML_XPath/docs/XML_XPath_example.php:1.2 --- pear/XML_XPath/docs/XML_XPath_example.php:1.1 Thu Jun 6 20:14:21 2002 +++ pear/XML_XPath/docs/XML_XPath_example.php Wed Jun 12 17:23:55 2002 @@ -1,4 +1,5 @@ <?php +error_reporting(E_ALL); require_once '../XPath.php'; $xml = new XML_XPath(); $string = '<?xml version="1.0"?> @@ -13,15 +14,34 @@ </report> </doc>'; $xml->load($string, 'string'); -$children = $xml->childNodes(); echo '----------' . "\n"; +/* Begin test for childNodes() +$children = $xml->childNodes(); echo 'Showing each child of <doc> (Please note blank nodes are skipped)' . "\n"; while ($children->next()) { echo 'Index: ' . $children->getIndex() . ' Name: ' . $children->nodeName() . "\n"; } echo '----------' . "\n"; -$xml->evaluate('//report[@id = "5"]', true); -$xml->evaluate('title', true, true); -echo 'The report title is: ' . $xml->substringData() . "\n"; +*/ +$reports = $xml->evaluate('//report[@id = "5"]'); +echo 'The report title is: ' . $reports->substringData(0, 0, array('.', 'title')) . "\n"; +echo '----------' . "\n"; +echo 'Here we want to generate a null pointer for an empty result misuse' . "\n"; echo '----------' . "\n"; +$result = $xml->evaluate(array('.', 'i/dont/exist')); +print_r($result); +print_r($result->substringData()); +$now = systime(); +foreach (range(0, 50) as $i) { + $xml->nodeName(); +} +echo 'Timing:' . "\n"; +echo systime() - $now; +echo "\n"; +if ($reports->evaluate(array('.', '..'), true)) { + echo $reports->nodeName(); +} +else { + echo 'Invalid query'; +} ?>
Index: pear/XML_XPath/TODO diff -u pear/XML_XPath/TODO:1.4 pear/XML_XPath/TODO:1.5 --- pear/XML_XPath/TODO:1.4 Thu Jun 6 06:54:48 2002 +++ pear/XML_XPath/TODO Wed Jun 12 17:23:55 2002 @@ -19,21 +19,18 @@ [@] make an example.php and example.out for people to read through (Cooker) - [@] implement childNodes() to return an xml_xpath_result object + [@] implement childNodes() to return an xml_xpath_result object, Fix sorting in result + object for DOM queries [*] make a short test harness just so I know I don't commit bad shit [*] finally figured out substitute entities default, so see if we can make this usable in the class - [*] implement getElementsByTagName and getElementById since there are domxml functions - now...return as getElementsByTagName as XML_XPath_result objects..getElementById - will be a move function + [@] implement getElementsByTagName since there are domxml functions + now...return as getElementsByTagName as XML_XPath_result objects - [*] considering doing substringData for attribute nodes...just seems to make sense - - [*] in _quick_evaluate_init, make sure the pointer is not null, could happen coming - off of a results object + [@] always check that pointer is not NULL [@] functions for result object are now next() @@ -48,7 +45,19 @@ it moves from off the stage to on the stage the first time, then it moves to the next node after that - [@] relative xpath queries + [@] relative xpath queries in evaluate [*] fix this stdClass hack we have to do to get the result object to work with non xpath queries (in cases of using dom queries) + + [*] waiting on stripping of formatting on the way in + + [*] insert_before() now copies node before inserting, does this affect us? + + [@] when using getAttribute with the move = true, we should sit on the attribute node, not the parent element + + [@] manually do the xpath_eval in the quick xpath query function, much faster + + [@] elimiate _quick_evaluate_shutdown? to slow in critical areas + + [*] figure out a way to implement the create functions Index: pear/XML_XPath/XPath.php diff -u pear/XML_XPath/XPath.php:1.13 pear/XML_XPath/XPath.php:1.14 --- pear/XML_XPath/XPath.php:1.13 Fri Jun 7 19:17:21 2002 +++ pear/XML_XPath/XPath.php Wed Jun 12 17:23:55 2002 @@ -17,7 +17,7 @@ // | Authors: Dan Allen <dan@mojavelinux.com> | // +----------------------------------------------------------------------+ -// $Id: XPath.php,v 1.13 2002/06/07 23:17:21 dallen Exp $ +// $Id: XPath.php,v 1.14 2002/06/12 21:23:55 dallen Exp $ // }}} // {{{ description @@ -140,7 +140,7 @@ // not a valid xml instance, so throw error else { ob_end_clean(); - return PEAR::raiseError(null, XML_XPATH_INVALID_DOCUMENT, null, E_USER_ERROR, "The xml data '$in_xml' could not be parsed to xml dom", 'XML_XPath_Error', true); + return PEAR::raiseError(null, XML_XPATH_INVALID_DOCUMENT, null, E_USER_ERROR, "The xml $in_type '$in_xml' could not be parsed to xml dom", 'XML_XPath_Error', true); } $loadError = ob_get_contents(); ob_end_clean(); @@ -158,80 +158,25 @@ } // }}} - // {{{ mixed getOne() + // {{{ void registerNamespace() /** - * A quick version of the evaluate, where the results are returned immediately. This - * function is equivalent to xsl:value-of select in every way. + * This function is sort of temporary hack for registering namespace prefixes * - * @param string $in_xpathQuery (optional) quick xpath query - * @param boolean $in_movePointer (optional) move internal pointer + * The domxml functions should do this automatically when a domxml object is read in. + * For now, you can use this function. * - * @access public - * @return mixed number of nodes or value of scalar result {or XML_XPath_Error exception} - */ - function getOne($in_xpathQuery, $in_movePointer = false) - { - // Execute the xpath query and return the results, then reset the result index - if (XML_XPath::isError($result = $this->evaluate($in_xpathQuery, $in_movePointer))) { - return $result; - } - return $result->getData(); - } - - // }}} - // {{{ void evaluate() - - /** - * Evaluate the xpath expression on the loaded xml document. An XML_XPath_Result object is - * returned which can be used to manipulate the results - * - * @param string $in_xpathQuery xpath query - * @param boolean $in_movePointer (optional) move internal pointer + * @param array $in_namespaces * + * @return void * @access public - * @return object result object {or XML_XPath_Error exception} */ - function &evaluate($in_xpathQuery, $in_movePointer = false) + function registerNamespace($in_namespaces) { - // Make sure we have loaded an xml document and were able to create an xpath context - if (get_class($this->ctx) != 'XPathContext') { - return PEAR::raiseError(null, XML_XPATH_NOT_LOADED, null, E_USER_ERROR, null, 'XML_XPath_Error', true); + settype($in_namespaces, 'array'); + foreach($in_namespaces as $localName => $namespace) { + $this->ctx->xpath_register_ns($localName, $namespace); } - - // enable relative xpath queries (I don't check a valid dom object yet) - settype($in_xpathQuery, 'array'); - if (isset($in_xpathQuery[1])) { - $sep = '/'; - // those double slashes cause an anomally - if (substr($in_xpathQuery[0], 0, 2) == '//') { - $sep = ''; - } - - if ($in_xpathQuery[1] == 'current()') { - $in_xpathQuery[1] = $this->getNodePath($this->pointer); - } - else { - $in_xpathQuery[1] = $this->getNodePath($in_xpathQuery[1]); - } - - $xpathQuery = $in_xpathQuery[1] . $sep . $in_xpathQuery[0]; - } - else { - $xpathQuery = reset($in_xpathQuery); - } - - if (!$result = @xpath_eval($this->ctx, $xpathQuery)) { - return PEAR::raiseError(null, XML_XPATH_INVALID_QUERY, null, E_USER_WARNING, "XML_XPath query: $xpathQuery", 'XML_XPath_Error', true); - } - - $resultObj =& new XML_XPath_result($result->type == XPATH_NODESET ? $result->nodeset : $result->value, $result->type, $xpathQuery, $this->ctx); - - if ($in_movePointer && $result->type == XPATH_NODESET && !empty($result->nodeset)) { - $this->pointer = reset($result->nodeset); - } - - return $resultObj; } // }}} Index: pear/XML_XPath/XPath/common.php diff -u pear/XML_XPath/XPath/common.php:1.16 pear/XML_XPath/XPath/common.php:1.17 --- pear/XML_XPath/XPath/common.php:1.16 Fri Jun 7 19:17:21 2002 +++ pear/XML_XPath/XPath/common.php Wed Jun 12 17:23:55 2002 @@ -17,7 +17,7 @@ // | Authors: Dan Allen <dan@mojavelinux.com> | // +----------------------------------------------------------------------+ -// $Id: common.php,v 1.16 2002/06/07 23:17:21 dallen Exp $ +// $Id: common.php,v 1.17 2002/06/12 21:23:55 dallen Exp $ // }}} // {{{ description @@ -216,7 +216,7 @@ } $nodeset[] = $childNode; } - return new XML_XPath_result($nodeset, XPATH_NODESET, array($this->pointer, '/*'), $this->ctx); + return new XML_XPath_result($nodeset, XPATH_NODESET, array($this->pointer, '/*'), $this->ctx, $this->xml); } // }}} @@ -236,7 +236,7 @@ { // since we can't do an actual xpath query, we need to create a pseudo xpath result $nodeset = $this->xml->get_elements_by_tagname($in_tagName); - return new XML_XPath_result($nodeset, XPATH_NODESET, array(null, '//' . $in_tagName), $this->ctx); + return new XML_XPath_result($nodeset, XPATH_NODESET, array(null, '//' . $in_tagName), $this->ctx, $this->xml); } // }}} @@ -1286,7 +1286,12 @@ $occur++; if ($type == XML_ELEMENT_NODE) { - $name = $name; + // this is a hack and only works for some cases + // we can have to nodes with the same name but different namespace, + // so this should actually go above + if (($prefix = $cur->prefix()) != '') { + $name = $prefix . ':' . $name; + } } // fix the names for those nodes where xpath query and dom node name don't match elseif ($type == XML_COMMENT_NODE) { @@ -1305,6 +1310,7 @@ $occur = 0; } } + if ($occur == 0) { $buffer = $sep . $name . $buffer; } @@ -1320,6 +1326,88 @@ } // }}} + // {{{ mixed getOne() + + /** + * A quick version of the evaluate, where the results are returned immediately. This + * function is equivalent to xsl:value-of select in every way. + * + * @param string $in_xpathQuery (optional) quick xpath query + * @param boolean $in_movePointer (optional) move internal pointer + * + * @access public + * @return mixed number of nodes or value of scalar result {or XML_XPath_Error exception} + */ + function getOne($in_xpathQuery, $in_movePointer = false) + { + // Execute the xpath query and return the results, then reset the result index + if (XML_XPath::isError($result = $this->evaluate($in_xpathQuery, $in_movePointer))) { + return $result; + } + + return $result->getData(); + } + + // }}} + // {{{ void evaluate() + + /** + * Evaluate the xpath expression on the loaded xml document. An XML_XPath_Result object is + * returned which can be used to manipulate the results + * + * @param string $in_xpathQuery xpath query + * @param boolean $in_movePointer (optional) move internal pointer + * + * @access public + * @return object result object {or XML_XPath_Error exception} + */ + function &evaluate($in_xpathQuery, $in_movePointer = false) + { + // Make sure we have loaded an xml document and were able to create an xpath context + if (get_class($this->ctx) != 'XPathContext') { + return PEAR::raiseError(null, XML_XPATH_NOT_LOADED, null, E_USER_ERROR, null, 'XML_XPath_Error', true); + } + + // enable relative xpath queries (I don't check a valid dom object yet) + settype($in_xpathQuery, 'array'); + if (isset($in_xpathQuery[1])) { + $sep = '/'; + // those double slashes cause an anomally + if (substr($in_xpathQuery[1], 0, 2) == '//') { + $sep = ''; + } + + if ($in_xpathQuery[0] == 'current()' || $in_xpathQuery[0] == '.') { + $in_xpathQuery[0] = $this->getNodePath($this->pointer); + } + else { + $in_xpathQuery[0] = $this->getNodePath($in_xpathQuery[0]); + } + + $xpathQuery = $in_xpathQuery[0] . $sep . $in_xpathQuery[1]; + } + else { + $xpathQuery = reset($in_xpathQuery); + } + + // we don't care if this messes up, we will let the result object handle no results + $result = @xpath_eval($this->ctx, $xpathQuery); + + // if we are moving the pointer, return boolean success just like the step functions + if ($in_movePointer) { + if ($result->type == XPATH_NODESET && !empty($result->nodeset)) { + $this->pointer = reset($result->nodeset); + return true; + } + else { + return false; + } + } + + return new XML_XPath_result($result->type == XPATH_NODESET ? $result->nodeset : $result->value, $result->type, $xpathQuery, $this->ctx, $this->xml); + } + + // }}} // {{{ _build_fragment() /** @@ -1461,18 +1549,18 @@ if (isset($in_xpathQuery[1])) { $sep = '/'; // those double slashes cause an anomally - if (substr($in_xpathQuery[0], 0, 2) == '//') { + if (substr($in_xpathQuery[1], 0, 2) == '//') { $sep = ''; } - if ($in_xpathQuery[1] == 'current()') { - $in_xpathQuery[1] = $this->getNodePath($this->pointer); + if ($in_xpathQuery[0] == 'current()' || $in_xpathQuery[0] == '.') { + $in_xpathQuery[0] = $this->getNodePath($this->pointer); } else { - $in_xpathQuery[1] = $this->getNodePath($in_xpathQuery[1]); + $in_xpathQuery[0] = $this->getNodePath($in_xpathQuery[0]); } - $xpathQuery = $in_xpathQuery[1] . $sep . $in_xpathQuery[0]; + $xpathQuery = $in_xpathQuery[0] . $sep . $in_xpathQuery[1]; } else { $xpathQuery = reset($in_xpathQuery); Index: pear/XML_XPath/XPath/result.php diff -u pear/XML_XPath/XPath/result.php:1.10 pear/XML_XPath/XPath/result.php:1.11 --- pear/XML_XPath/XPath/result.php:1.10 Fri Jun 7 19:17:21 2002 +++ pear/XML_XPath/XPath/result.php Wed Jun 12 17:23:55 2002 @@ -17,7 +17,7 @@ // | Authors: Dan Allen <dan@mojavelinux.com> | // +----------------------------------------------------------------------+ -// $Id: result.php,v 1.10 2002/06/07 23:17:21 dallen Exp $ +// $Id: result.php,v 1.11 2002/06/12 21:23:55 dallen Exp $ // }}} // {{{ description @@ -35,9 +35,6 @@ define('XML_XPATH_SORT_NATURAL_DESCENDING', 6); // }}} -/** - I need to handle sort when result is retrieved using childNodes() - */ // {{{ class XML_XPath_result @@ -86,15 +83,21 @@ */ var $ctx; + /** + * domxml object, need for many common functions + * @var object $xml + */ + var $xml; // }}} // {{{ constructor - function XML_XPath_result($in_data, $in_type, $in_query, &$in_ctx) + function XML_XPath_result($in_data, $in_type, $in_query, &$in_ctx, &$in_xml) { $this->query = $in_query; $this->type = $in_type; $this->data = $in_data; $this->ctx = &$in_ctx; + $this->xml =&$in_xml; // move the pointer to the first node if at least one node in the result exists // for convience, just so we don't have to call nextNode() if we expect only one $this->rewind(); @@ -115,14 +118,21 @@ switch($this->type) { case XPATH_BOOLEAN: return $this->data ? true : false; - break; + break; + case XPATH_NODESET: - return $this->pointer->node_type() == XML_ATTRIBUTE_NODE ? $this->pointer->value() : $this->substringData(); - break; + if (!$this->pointer) { + return null; + } + else { + return $this->pointer->node_type() == XML_ATTRIBUTE_NODE ? $this->pointer->value() : $this->substringData(); + } + break; + case XPATH_STRING: case XPATH_NUMBER: return $this->data; - break; + break; } } @@ -423,6 +433,27 @@ } } + return false; + } + + // }}} + // {{{ object current() + + /** + * Retrieve current pointer + * + * If the result is a nodeset (which is the most common use of the result object) than + * this function returns the current pointer in the result array. + * + * @return object XML_XPath pointer + * @access public + */ + function current() + { + if (is_array($this->data)) { + return current($this->data); + } + return false; } Index: pear/XML_XPath/docs/XML_XPath_example.php diff -u pear/XML_XPath/docs/XML_XPath_example.php:1.1 pear/XML_XPath/docs/XML_XPath_example.php:1.2 --- pear/XML_XPath/docs/XML_XPath_example.php:1.1 Thu Jun 6 20:14:21 2002 +++ pear/XML_XPath/docs/XML_XPath_example.php Wed Jun 12 17:23:55 2002 @@ -1,4 +1,5 @@ <?php +error_reporting(E_ALL); require_once '../XPath.php'; $xml = new XML_XPath(); $string = '<?xml version="1.0"?> @@ -13,15 +14,34 @@ </report> </doc>'; $xml->load($string, 'string'); -$children = $xml->childNodes(); echo '----------' . "\n"; +/* Begin test for childNodes() +$children = $xml->childNodes(); echo 'Showing each child of <doc> (Please note blank nodes are skipped)' . "\n"; while ($children->next()) { echo 'Index: ' . $children->getIndex() . ' Name: ' . $children->nodeName() . "\n"; } echo '----------' . "\n"; -$xml->evaluate('//report[@id = "5"]', true); -$xml->evaluate('title', true, true); -echo 'The report title is: ' . $xml->substringData() . "\n"; +*/ +$reports = $xml->evaluate('//report[@id = "5"]'); +echo 'The report title is: ' . $reports->substringData(0, 0, array('.', 'title')) . "\n"; +echo '----------' . "\n"; +echo 'Here we want to generate a null pointer for an empty result misuse' . "\n"; echo '----------' . "\n"; +$result = $xml->evaluate(array('.', 'i/dont/exist')); +print_r($result); +print_r($result->substringData()); +$now = systime(); +foreach (range(0, 50) as $i) { + $xml->nodeName(); +} +echo 'Timing:' . "\n"; +echo systime() - $now; +echo "\n"; +if ($reports->evaluate(array('.', '..'), true)) { + echo $reports->nodeName(); +} +else { + echo 'Invalid query'; +} ?>