Reputation: 6132
I have an XML file with orders.
<?xml version="1.0" encoding="UTF-8"?>
<orders>
<order order-no="1234567">
<order-date>2012-09-15T12:28:45.000Z</order-date>
<invoice-no>123</invoice-no>
<customer>
<customer-no>1234</customer-no>
<customer-name>test</customer-name>
</customer>
<payments>
<payment>
<custom-method>
<method-name>example</method-name>
</custom-method>
<amount>12334</amount>
</payment>
</payments>
</order>
<order>
.......
</order>
</orders>
Many <order>
nodes in one file. Everything is correct but then out of the blue I get an order without any payment information like this
<orders>
<order order-no="1234567">
<order-date>2012-09-15T12:28:45.000Z</order-date>
<invoice-no>123</invoice-no>
<customer>
<customer-no>1234</customer-no>
<customer-name>test</customer-name>
<billing-address/>
</customer>
<payments/>
</order>
Notice the empty <payments/>
node.
My PHP script looks like this:
$file = file_get_contents('orders.xml');
$orders = new SimpleXMLElement($file);
foreach ($orders as $order) {
$var = $order->payments->payment->{'custom-method'}->{'method-name'};
}
With the first XML example everything is fine but when I stumble upon an order from a second XML example my script is throwing an exception Trying to get property of non-object
and obviously stops parsing the rest of the file.
I'd like my script to not stop and just go on with parsing the rest of the file. I don't want to you the "shut up" @
operator, but I don't see any other way.
Upvotes: 0
Views: 740
Reputation: 198214
SimpleXML comes with some bunch of magic. Let's see how you access the (existing or non-existing) <payment>
element:
$order->payments->payment
This will (always!) result in a SimpleXMLElement
regardless if the count of <payment>
elements inside <payments>
elements is zero or larger.
However, if the count is actually zero, accessing the first <payment>
element will return NULL
:
$order->payments->payment[0]
This is accessing the first (zero-based index in SimpleXML) payment element. As it does not exist, this results in NULL
. So you can if-check this:
foreach ($orders as $order)
{
if (!$order->payments->payment[0])
continue;
$var = $order->payments->payment->{'custom-method'}->{'method-name'};
}
The other answer suggested to use the SimpleXMLElement::count()
method. It counts the children of an element and it works as well, however the other answer suggested the wrong place to use it, the correct place is with the <payment>
element(s), not the <payments>
element:
$order->payments->payment->count()
This returns the 0
(zero) number of elememnts you wanted to check for. So this is working equally well:
foreach ($orders as $order)
{
if (!$order->payments->payment->count())
continue;
$var = $order->payments->payment->{'custom-method'}->{'method-name'};
}
However this excursion would not be complete to actually show you a way that is probably most intended with SimpleXMLElement
. It is based on the fact that an empty SimpleXMLElement
equals false:
foreach ($orders as $order)
{
if (!$order->payments->payment)
continue;
$var = $order->payments->payment->{'custom-method'}->{'method-name'};
}
As all these examples show, you need explore SimpleXML a little to understand it's magic so you can benefit from it.
If you prefer an interface with less magic, checkout the sister DOMDocument
.
And independent to SimpleXMLElement
, whenever you've got a Traversable
(which it is as well), you can make use of a function called iterator_count
to give back the count of iterations. For a SimpleXMLElement
it means the number of children.
Upvotes: 0
Reputation: 73031
You need to add logic to ensure <payments>
has payment data (i.e. payment
nodes).
foreach ($orders as $order) {
if ($order->payments->count()) {
// has payment data... run your payment code
}
}
Even though the docs claim the above should work with PHP > 5.3, it returned 1
in the OP's case (not sure why).
Ultimately the following is needed:
count($order->payments->children())
Upvotes: 1