DOM XML (PHP 4) 函数
在线手册:中文 英文
PHP手册

xpath_eval

(PHP 4)

xpath_eval Evaluates the XPath Location Path in the given string

说明

XPathObject XPathContext::xpath_eval ( string $xpath_expression [, domnode $contextnode ] )
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().


DOM XML (PHP 4) 函数
在线手册:中文 英文
PHP手册
PHP手册 - N: Evaluates the XPath Location Path in the given string

用户评论:

marius kreis (email to mariuskreis . de) (08-Jul-2005 03:26)

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...)

patrikG at home dot net (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 09: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 06: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 07: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)

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)

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)

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 12: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)

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 01: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 02: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 10: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 04: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)

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.