PHP Doku:: Evaluates the XPath Location Path in the given string - function.xpath-eval.html

Verlauf / Chronik / History: (1) anzeigen

Sie sind hier:
Doku-StartseitePHP-HandbuchFunktionsreferenzXML-ManipulationDOM-XMLDOM-XML-Funktionenxpath_eval

Ein Service von Reinhard Neidl - Webprogrammierung.

DOM-XML-Funktionen

<<xpath_eval_expression

xpath_new_context>>

xpath_eval

(PHP 4)

xpath_eval Evaluates the XPath Location Path in the given string

Beschreibung

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


15 BenutzerBeiträge:
- Beiträge aktualisieren...
marius kreis (email to mariuskreis . de)
8.07.2005 16: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.03.2004 17: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
6.09.2003 22: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
6.09.2003 19:05
Querying documents closed inside a namespace can be tricky

http://bugs.php.net/bug.php?id=11903
tuxo at gmx dot net
21.05.2003 20: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.01.2003 2: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.11.2002 11:32
If you want to apply an XPath-Expression to a particular node:

$ctx->xpath_eval("xpath",$node);
arthur at ischium dot net
9.11.2002 0: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
4.10.2002 13: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.03.2002 3: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.10.2001 14: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.08.2001 3: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.06.2001 23: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.05.2001 17: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
7.03.2001 2: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.



PHP Powered Diese Seite bei php.net
The PHP manual text and comments are covered by the Creative Commons Attribution 3.0 License © the PHP Documentation Group - Impressum - mail("TO:Reinhard Neidl",...)