views:

25

answers:

2

I'm using SimpleXMLElement to read xml returned from a remote server. The results are then parsed with xpath like this:

$result = <<<XML
<DataImport2Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.xxx.xxx/Services/DataImport2"&gt; 
    <Number /> 
    <Blocks> 
        <Block>
            <Code>Fbf</Code> 
            <Fields> 
                <Field> 
                    <Code>FinnsIFbf</Code> 
                    <Value>1</Value> 
                </Field>
            </Fields>
        </Block>
    </Blocks> 
</DataImport2Result>
XML;

$xml = new SimpleXMLElement($result);
$xml->registerXPathNamespace("data", array_pop($xml->getNamespaces()));

foreach($xml->xpath("//data:Code[.='Fbf']/..") as $block) {
    foreach($block[0]->Fields->Field as $field) {
        echo "Code: ". $field->Code ."\n"; // SHould return FinnsIFbf
    }
}

The $result is an Array with SimpleXMLElement objects. The actual error occurs when trying to use $block[0]->Fields(SimpleXMLElement object) as an array. So the results are there. It's iterating that is the problem.
This works just fine in PHP 5.3.2, but on the server which is running 5.1.6 it fails with:
Fatal error: Objects used as arrays in post/pre increment/decrement must return values by reference

What's the simplest way to fix this without upgrading the server version (server admin thinks its "unstable" with a newer version)?

One solution could be if I could make the xpath return the Field in the first $result, so I wont have to iterate the $block[0]->Fields->Field but I failed to make such an xpath expression.

A: 

It is probably because $block is not an array. You should use var_dump or print_r to confirm that, and then use is_array in your code to make sure it is an array.

If SimpleXML finds multiple elements with the same name, it returns an array of objects. If it finds a single element, it returns just that element, not an array with a single element. So when you expect multiple elements, you always have to code an exception case for when there is only one element.

Sjoerd
@Sjoerd I updated the question to refer to this
baloo
I'm not sure what you meant with "it returns an array of objects." xpath() always returns an array, and accessing elements through the object notation (e.g. `$xml->Blocks`) always return a SimpleXMLElement no matter how many elements it represents, on which you can use the array notation with numeric indices to access elements in the order they appear in the document. In that example, you could access that block via `$xml->Blocks[0]->Block[0]` even though there's only one of each. Older versions of SimpleXML had some corner cases where it didn't work, though.
Josh Davis
+1  A: 

As was mentionned in Sjoerd's answer, $block is not an array. SimpleXMLElement::xpath() returns an array of objects, each of those objects representing one single element. So basically, you have to replace $block[0] with $block since it already represents the block you're looking for.

Also, I have rewritten your XPath expression. Since you're looking for a <data:Block/> element, that's what you should target. The thing about <data:Code/> is a predicate, so it should go within brackets. Of course in your case the result is the same, but it's good practice to have semantically correct expressions, helps having a clearer idea of what's going on later on when you re-read that code (or if someone else has to maintain it.)

foreach ($xml->xpath('//data:Block[data:Code="Fbf"]') as $block) {
    foreach ($block->Fields->Field as $field) {
        echo "Code: ". $field->Code ."\n"; // SHould return FinnsIFbf
    }
}

Update

I didn't notice that you said that all you were interested in was the <Field/> element. In that case you can get it directly through XPath: (remember that they're all in the data namespace)

foreach ($xml->xpath('//data:Block[data:Code="Fbf"]/data:Fields/data:Field') as $field) {
    echo "Code: ". $field->Code ."\n"; // SHould return FinnsIFbf
}
Josh Davis
Thanks a ton, worked great! My xpath knowledge is limited
baloo