Permalink
Browse files

- 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


git-svn-id: http://svn.php.net/repository/pear/packages/XML_XPath/trunk@85522 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information...
1 parent c288feb commit 88f9556d64986ffa08d05cd23039840527f48e1b Daniel Allen committed Jun 12, 2002
Showing with 188 additions and 95 deletions.
  1. +18 −9 TODO
  2. +11 −66 XPath.php
  3. +96 −8 XPath/common.php
  4. +39 −8 XPath/result.php
  5. +24 −4 docs/XML_XPath_example.php
View
27 TODO
@@ -19,21 +19,18 @@ XML_XPath Inteface TODO:
[@] 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 @@ XML_XPath Inteface TODO:
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
View
@@ -140,7 +140,7 @@ function load($in_xml, $in_type = 'string')
// 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 @@ function load($in_xml, $in_type = 'string')
}
// }}}
- // {{{ 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);
- }
-
- // 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);
+ settype($in_namespaces, 'array');
+ foreach($in_namespaces as $localName => $namespace) {
+ $this->ctx->xpath_register_ns($localName, $namespace);
}
-
- 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;
}
// }}}
View
@@ -216,7 +216,7 @@ function &childNodes()
}
$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 @@ function getElementsByTagName($in_tagName)
{
// 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 @@ function getNodePath($in_node)
$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 @@ function getNodePath($in_node)
$occur = 0;
}
}
+
if ($occur == 0) {
$buffer = $sep . $name . $buffer;
}
@@ -1320,6 +1326,88 @@ function getNodePath($in_node)
}
// }}}
+ // {{{ 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 @@ function _quick_evaluate_init($in_xpathQuery = null, $in_movePointer = false, $i
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);
View
@@ -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 @@ class XML_XPath_result extends XML_XPath_common {
*/
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 @@ function getData()
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;
}
}
@@ -427,6 +437,27 @@ function nextByNodeType($in_type)
}
// }}}
+ // {{{ 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;
+ }
+
+ // }}}
// {{{ boolean end()
/**
Oops, something went wrong.

0 comments on commit 88f9556

Please sign in to comment.