views:

71

answers:

2
+1  Q: 

php xpath problems

I'm doing a cURL POST and get the error response back, parse it into an array but having issues with xpath now.

// XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<errors xmlns="http://host/project"&gt;
   <error code="30" description="[] is not a valid email address."/>
   <error code="12" description="id[] does not exist."/>
   <error code="3" description="account[] does not exist."/>
   <error code="400" description="phone[] does not exist."/>
</errors>

// Function / Class

class parseXML
{
    protected $xml;

    public function __construct($xml) {
        if(is_file($xml)) {
            $this->xml = simplexml_load_file($xml);
        } else {
            $this->xml = simplexml_load_string($xml);
        }
    }

    public function getErrorMessage() {
        $in_arr = false;
        $el = $this->xml->xpath("//@errors");        
        $returned_errors = count($el);

        if($returned_errors > 0) {
            foreach($el as $element) {
                if(is_object($element) || is_array($element)) {
                    foreach($element as $item) {
                        $in_arr[] = $item;
                    }
                }
            }
        } else {
            return $returned_errors;
        }            
        return $in_arr;
    }
}

// Calling function

// $errorMessage is holding the XML value in an array index
// something like: $arr[3] = $xml;
$errMsg = new parseXML($arr[3]); 
$errMsgArr = $errMsg->getErrorMessage();

What I would like is all the error code and description attribute values

EDIT:

OK this is print_r($this->xml,true);

SimpleXMLElement Object
(
    [error] => Array
        (
            [0] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [code] => 30
                            [description] => [] is not a valid email address.
                        )

                )

            [1] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [code] => 12
                            [description] => Id[12345] does not exist.
                        )

                )

            [2] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [code] => 3
                            [description] => account[] does not exist.
                        )

                )

            [3] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [code] => 400
                            [description] => phone[] does not exist.
                        )

                )

        )

)

for the life of me I can't figure out why I can get the code and description, any thoughts?

EDIT #2 Okay so I guess I will break it down.

I'm using cURL to POST a request to one of our servers, I parse out the HTTP response headers and xml (if xml is returned). each line in the header/xml I explode into an array. so if there is an error I see an extra index to the array. I then do something like this.

$if_err_from_header = $http_return_response[10]; 
// I know that index 10 is where if any the error message in xml is (the one posted above).

after that I do this:

$errMsg = new parseXML($if_err_from_header); 
$errMsgArr = $errMsg->getErrorMessage();

still I can not get the code and description from the attributes in error, what am I missing?

EDIT #3 Okay why does this work?

$in_arr = false;
// This returns all the code attributes
$el = $this->xml->xpath("//@code");

# if $el is false, nothing returned from xpath(), set to an empty array
$el = $el == false ? array() : $el;

foreach($el as $element) {
    $in_arr[] = array("code" => $element["code"], "description" => $element["description"]);
}
return $in_arr;

EDIT #4:

Okay this gets that values I want but it's kinda a hack, would like to select specific elements but...

$el = $this->xml->xpath("//*");
+1  A: 

@ in XPath is the attribute selector. You're trying to select the root element so it should be:

  $el = $this->xml->xpath("/errors");

If you want to select all error elements, use

  $el = $this->xml->xpath("/errors/error");

or

  $el = $this->xml->xpath("//error");
Andy E
hmm still not working, but thanks for pointing out I was using the attribute selector
Phill Pafford
if I just pass the array index I get an error, this is due to the index not being a string. if I cast the index into a string I get 0 as xpath finds nothing. any thoughts as to why?
Phill Pafford
added how the SimpleXMLElement Object is coming through, any thoughts?
Phill Pafford
+1  A: 

Make sure you take the namespace into account:

$this->xml->registerXPathNamespace('n', 'http://host/project');
$el = $this->xml->xpath("/n:errors/n:error");
$returned_errors = count($el);

And example of accessing values for lower down..

foreach($el as $element) {
   print "code: " . $element["code"] . "\n";
}
bleh
so just to clarify n is the variable used for the namespace or is the name of the namespace? so I have xmlns, would I use that or n?
Phill Pafford
@Phil, you've defined "http://host/project" as the default namespace, and we're choosing to associate the prefix "n" with it. Without specifying this namespace, PHP attempts to use the null namespace and therefore the XPath expression will return an empty set.As an experiment, create a new error node with the xmlns attribute set to "http://example". You'll see that it is not returned in the output with the others.
bleh
added how the SimpleXMLElement Object is coming through, any thoughts?
Phill Pafford
I'd guess because each of those elements (*$element* in your example) is an object, not an array. Did you try the second part of my example with the *foreach()*? You should be able to use *$element["code"]* and *$element["description"]* rather than creating another loop.
bleh
Posted fully-working example at http://codepad.org/bLC2jzAG
bleh
I've tried you're very nice code but still nothing but an empty array s returned. I have several other functions that I use xpath on and all work without a hitch. but for the life of me I can't figure out why this object doesn't. please see edit #2
Phill Pafford
See edit #3, so why is that working?
Phill Pafford
#3 doesn't work for me; only returns code attributes. #4 neither because it returns ALL elements, including the root node. Are you sure you have the namespace in the PHP and XML matching exactly? Try this, it has XML included in PHP: http://codepad.org/lmSPbRMw
bleh