If the namespace is subject to change you can write even more portable code if you extend brandon dot whitehead at orst dot edu's solution like this:
$doc = domxml_open_mem($xml);
$xpath = $doc->xpath_new_context();
$namespace = $xpath->xpath_eval('namespace-uri(//*)')->value; // returns the namespace uri
xpath_register_ns($xpath, "pre", $namespace); // sets the prefix "pre" for the namespace
$obj = $xpath->xpath_eval('//pre:Offer'); // finds all Offer tags
$nodeset = $obj->nodeset;
print_r($nodeset);
This code will determine the namespace of the root element and set a prefix for XPath queries. Thus it doesn't matter if the namespace is changing in your XML (like some webservices do...)
xpath_eval
(PHP 4)
xpath_eval — Evaluates the XPath Location Path in the given string
Description
XPathContext
XPathObject xpath_eval
( XPathContext $xpath_context
, string $xpath_expression
[, domnode $contextnode
] )
The optional contextnode can be specified for doing relative XPath queries.
See also xpath_new_context().

xpath_eval
marius kreis (email to mariuskreis . de)
08-Jul-2005 02:26
08-Jul-2005 02:26
patrikG at home dot net
16-Mar-2004 04:42
16-Mar-2004 04:42
Just an example of how to grab XML attributes with xpath - which took me a while to figure out. I'm filtering the returned object function node_content() which is a somewhat quick'n dirty solution, but I don't always need XML's child-parent relationships.
<?php
$xml='<MY_SERVICE>
<MERCHANDISE>
<SERVICE TYPE="books">
<NAME>Ulysses</NAME>
</SERVICE>
<SERVICE TYPE="books">
<NAME>The Poisonwood Bible</NAME>
</SERVICE>
<SERVICE TYPE="cars">
<NAME>Van</NAME>
</SERVICE>
<SERVICE TYPE="vehicle sans wheels">
<NAME>UFO</NAME>
</SERVICE>
</MERCHANDISE>
</MY_SERVICE>';
echo "<h4>XML</h4><xmp>";print_r(parse_XML($xml));echo"</xmp>";
function node_content($node,$attribute="content"){
foreach($node->nodeset as $content){
$return[] = $content->{$attribute};
}
return $return;
}
function parse_XML($xml){
//needs PHP's xPath extension installed
$dom =domxml_open_mem($xml);
$calcX = &$dom->xpath_new_context();
$xml_parsed["merchandise"]=node_content(
$calcX->xpath_eval("//MERCHANDISE/SERVICE/NAME/text()")
);
$xml_parsed["service"]=node_content(
$calcX->xpath_eval("//MERCHANDISE/SERVICE/attribute::TYPE",$calcX)
,"value");
return $xml_parsed;
}
?>
The code above returns:
XML
Array
(
[merchandise] => Array
(
[0] => Ulysses
[1] => The Poisonwood Bible
[2] => Van
[3] => UFO
)
[service] => Array
(
[0] => books
[1] => books
[2] => cars
[3] => vehicle sans wheels
)
)
brandon dot whitehead at orst dot edu
06-Sep-2003 08:52
06-Sep-2003 08:52
In order to use the default namespace you must understand
how namespace prefixes work. Prefixes are simply convenient mappies to the namespace URI.
For example, if you set the namespace:
xmlns:xm="http://www.someurl.org"
and you have the following document fragment:
<rootnode><xm:childnode>Text</xm:childnode></rootnode>
this is essentially equivalent to:
<rootnode>
<http://www.someurl.org:childnode>
Text
</http://www.someurl.org:childnode>
</rootnode>
because the namespace URI is what matters, not the namespace prefix.
Unfortuantly, if you have a default namespace:
xmlns="http://www.anotherurl.org"
then all elements without a prefix belong to that namespace, and yet, it appears that PHP, and the underlying LIBXML2 don't let you register a default namespace with
"xpath_register_ns(context, prefix, uri)"
i.e. by leaving the prefix = "". Therefore, to get around the problem, simply give the default prefix a simple name, such as "pre".
For example, if you have a default namespace declaration such as the following document:
<?xml version="1.0" encoding="UTF-8"?>
<rootname xmlns="http://www.some.org" xml:lang="en-US">
<childnode>Some text</childnode>
</rootname>
And you want to evaluate the xpath expression:
"/rootname/childnode"
then you need to register the default namespace in PHP like this:
xpath_register_ns(context, "pre", "http://www.some.org");
and then use the following xpath expression:
"/pre:rootname/pre:childnode"
As you can see this is a lot prettier and more intuititive than using the local-name() function. In addition, it makes your code more portable, because you are guaranteed to always be working on nodes that belong to your explicitly stated namespace, uniquely identified by your URI.
fabiostt[X_AT_X]libero[X_DOT_X].it
06-Sep-2003 05:05
06-Sep-2003 05:05
Querying documents closed inside a namespace can be tricky
http://bugs.php.net/bug.php?id=11903
tuxo at gmx dot net
21-May-2003 06:47
21-May-2003 06:47
PHP Version: 4.3.1
I tried out how to get a part of a xml document with the xpath functions in domxml.
Try the following solution:
<?php
// get dom object
$xmldoc = domxml_open_mem($xml);
// init xpath
$xpath = xpath_new_context($xmldoc);
$xpresult = xpath_eval($xpath, "/root/info");
// dump all nodes directly in plain text
foreach ($xpresult->nodeset as $node)
{
$newxml .= $node->dump_node($node);
}
?>
If you wanna get a new dom object of the result just add
$newxmldoc = domxml_open_mem($newxml);
tk dot lists at fastmail dot fm
20-Jan-2003 01:46
20-Jan-2003 01:46
You can indeed use the result object of xpath_eval(). You just have to be careful to pass the result by reference! (note the ampersand's position).
$objXP = xpath_new_context($objDom)
$objTest = &xpath_eval($objXP,"//lalala");
$objTest->nodeset[0]->set_attribute("test","test data");
echo htlentities($objDom->dump_mem());
just be careful that is you pass around values from $objTest then they also need to be passed by reference.
chregu at php dot net
29-Nov-2002 10:32
29-Nov-2002 10:32
If you want to apply an XPath-Expression to a particular node:
$ctx->xpath_eval("xpath",$node);
arthur at ischium dot net
08-Nov-2002 11:54
08-Nov-2002 11:54
If you want to get the XPath for a particular node:
function getXPath($node) {
/* node id is held in a property named '1', this is
illegal in php so we use a workaround */
$one = '1';
$xpath = '';
while ($parent = $node->parent_node()) {
$siblings = $parent->child_nodes();
$index = 1;
foreach ($siblings as $sibling) {
if ($sibling->type != XML_ELEMENT_NODE || $sibling->tagname != $node->tagname) continue;
if ($sibling->$one == $node->$one) {
$xpath = '/' . $node->tagname . '[' . $index . ']' . $xpath;
break;
}
$index++;
}
$node = $parent;
}
return $xpath;
}
bate at php dot net
04-Oct-2002 11:58
04-Oct-2002 11:58
<?
$xml = xmldocfile('file.xml');
$xpath = $xml->xpath_new_context();
/**
* object access
*/
$ret = $xpath->xpath_eval('//tag');
/**
* function access
*/
$ret2 = xpath_eval($xpath, '//tag');
print_r($ret);
print_r($ret2);
?>
sbarnum@pointsystems com
21-Mar-2002 02:33
21-Mar-2002 02:33
This function has come in handy for recursively viewing the results of xpath searches. It iterates through a node and converts it to a big associative array:
/**
* Recursive function to convert xml root node to big assoc array
*/
function xmlnode2array($node) {
if ($node->type==XML_ELEMENT_NODE) {
if ($attrArray = $node->attributes()) {
// parse attributes //
foreach($attrArray AS $attr) {
$out['ATTRIBUTE'][$attr->name] = $attr->value;
}
}
if ($childArray = $node->children()) {
// add child nodes //
foreach($childArray AS $child) {
if ($child->type==XML_ELEMENT_NODE) {
$out[$child->tagname][] = xmlnode2array($child);
} else {
if ($content = xmlnode2array($child))
$out['CONTENT'] = $content;
}
}
}
} else {
// this is a CONTENT NODE //
$out = trim($node->content);
if (!$out) return false;
}
return $out;
}
ziw at ifirst dot ru
22-Oct-2001 12:34
22-Oct-2001 12:34
it seems that namespaces are not yet (PHP 4.06) implemented - xpath_eval($cnx,"/ns:tag") does work on w2k and does NOT on linux
sofnology at xtra dot co dot nz
26-Aug-2001 01:45
26-Aug-2001 01:45
I hope this little example helps someone out. If the XML data doesn't come thru in the post feel free to contact me via email.
<?
$p = xslt_create();
$o += 0;
$s = '';
$s .= "<query type='create'>";
$s .= "<resourceClass id='12345678901234567890' displayName='DAISY'>";
$s .= "<group family='global' id='kind'>";
$s .= "<node id='NODE_A' displayName='Red Ferrari' description='Red always goes faster'/>";
$s .= "</group>";
$s .= "</resourceClass>";
$s .= "<resourceClass id='12345678901234567890' displayName='BETTY'>";
$s .= "<group family='global' id='kind'>";
$s .= "<node id='NODE_B' displayName='Blue Porsche' description='But Porsches are a drivers car'/>";
$s .= "</group>";
$s .= "</resourceClass>";
$s .= "</query>";
$dom=xmldoc($s);
$ctx=xpath_new_context($dom);
$query_xo = xpath_eval($ctx,"count(/query/resourceClass)");
$num_rc = $query_xo->value;
echo("<BR>There are $num_rc classes in this list");
for($x=1; $x <= $num_rc; $x++){
$query_xo = xpath_eval($ctx,"/query/resourceClass[position()=$x]");
$query_ns = $query_xo->nodeset;
$resourceClass_dn = $query_ns[0];
// echo("<PRE>");
// print_r( $query_xo );
// echo("<PRE><HR>");
// print_r( $query_ns );
// echo("<PRE><HR>");
// print_r( $rc_dn );
echo("<BR>[id::".$resourceClass_dn->get_attribute('id')."][displayName::".$resourceClass_dn->get_attribute('displayName')."]");
}
?>
mfkahn2_NOSPAM at yahoo dot com
24-Jun-2001 09:35
24-Jun-2001 09:35
$ctx = xpath_new_context($doc);
$xpath_nodes = xpath_eval($ctx, "//some_element");
$xpath_nodes->nodeset[i]->set_content($string) allows you to set the node content. Try it and then do a $doc->dumpmem, you'll see the nodes in the original document are indeed updated properly.
I've used this feature lots. It does work.
newsforsam at bigfoot dot de
24-May-2001 03:46
24-May-2001 03:46
xpath_eval() returns only a copy of your document. So you cant for example change the $foo->nodeset[id]->content's of the resulting matches. If you want to, you have to do it yourself by going recursive through your doc, which makes xpath_eval at least useless except for checking if you have to ;).
pking at hoovers dot com
07-Mar-2001 01:22
07-Mar-2001 01:22
This is a very (very) minor point, but there is a comma missing in the function definition for xpath_eval. This being my first experience with xpath, I thought "object xpath context" was refering to a single parameter produced by a previous call to xpath_new_context(). Then I couldn't see where you would add the query (which is actually the context parameter)
So the proper definition should be
array xpath_eval (object xpath, context)
Additionally an example would be nice. I found one from a post to phpbuilder.com:
-------------------------------
http://www.phpbuilder.com/annotate/message.php3?id=1002772
-------------------------------
Message # 1002772:
Date: 01/02/01 06:40
By: Luis Argerich
Subject: new DOM features im 4.0.4
Just wanted to add that PHP 4.0.4 has improved DOM support including Xpath and
Xpointer support:
Try this:
$xml='SOME XML ....';
$doc=xmldoc($xml);
$ctx=xpath_new_context($doc);
$foo=xpath_eval($ctx,"//title");
print_r($foo);
It returns an object that contains a property called Nodeset with an array of DomNodes with the result of the Xpath expression. print_r($foo) to see the full structure.
4.0.4 has also added Xpointer support, so with Xpath and Xpointer support we can really do a lot of things from PHP to XML files.
Luis.