getting items from a SimpleXMLElement object - php

From what I can tell, a SimpleXMLElement is just an array of other SimpleXMLElements, plus some regular array values if there wasn't a tag nested in another tag.
I have a SimpleXMLElement in a variable $data, and var_dump($data) gives me this:
object(SimpleXMLElement)#1 (33) {
["buyer-accepts-marketing"]=>
string(4) "true"
...
...
but calling var_dump($data->buyer-accepts-marketing) gives me an error, and var_dump($data["buyer-accepts-marketing"]) gives me NULL. Calling var_dump($data->shipping-address->children()) gives me an error.
going like this:
foreach($data as $item) {
var_dump($item);
}
gives a whole bunch of SimpleXMLElement objects, but oddly enough, no strings or ints.
What am I missing here? I want to take specific portions of it and pass them to a function, so for example, I don't have to go
$data->billing-address->postal-code;
...
$data->shipping-address->postal-code;
...
and can just go
address($data->billing-address);
address($data->shipping-address);
etc.

SimpleXMLElement is not just an array. To access child elements, you must use object notation ($a->b) and to access attributes you must use array notation ($a['b']).
Problem is, with object notation, valid tag names can be illegal PHP code.
You need to do this:
$data->{'buyer-accepts-marketing'};
Note that this returns a SimpleXMLElement! The reason for this is that it can contain either just text, more child elements, or both. The output of var_dump() is very misleading for SimpleXMLElements. If you want to the text content of a single <buyer-accepts-marketing> tag, you have to do this:
(string)$data->{'buyer-accepts-marketing'};
Of course it is also perfectly legal to do this:
(int)$data->{'buyer-accepts-marketing'};
The reason this appears to work in some cases (such as echoing a SimpleXMLElement) is that the type conversions are implicit and automatic. You can't echo an object, so PHP automatically converts it to a string.
I have a love/hate relationship with SimpleXML. It makes things very easy only after you understand how complex the actual API is.
Read up on the so-called "basic" examples to get a good handle on it.

Related

Print value from stdClass Object/array with curly brackets

Using PHP, I printed out an array ($result) using print_r. But the format in which the array has been returned is a new one on me. Specifically, how do I retrieve the text found in the field "reviews_review" ("Wow, what a great...") when it's found in this particular format with its multiple values? Example:
stdClass Object
(
[node_reviews_nid] => 5270
[reviews_review] => a:2:{s:5:"value";s:38:"Wow, what a great place. The bestest!";s:6:"format";s:13:"filtered_html";}
Those extra values and curly brackets have got me stumped. If I wanted the node_reviews_nid I could simply use $result->node_reviews_nid" - but doing that to get reviews_review just retrieves too much ie,
a:2:{s:5:"value";s:38:"Wow, what a great place. The bestest!";s:6:"format";s:13:"filtered_html";})
The odd curly brackets you're seeing are the result of PHP's serialize function.
Basically it's intended to convert complex structures like objects, nested arrays and so on into simple strings which are easier and safer to transfer, for example over HTTP. Think of it as JSON representation, but specific to PHP (do not attempt to json_decode() a serialized value).
Also worth noting is that serialize-d string has a maximum length, beyond which it's simply truncated, so avoid using it for very large structures - you'll lose data.
The reverse of that is unserialize. So to read the review's text, you would first have to unserialize the "reviews_review" property, then reference the "value" index of the resulting array.
$review_data = unserialize($result->reviews_review);
$review_text = $review_data['value'];
print($review_text);
The data in reviews_review looks like a serialized array i.e. written using the serialize() function
So you will need to unserialize it using unserialize() before you can use it
$review = unserialize($obj->reviews_review);
print_r($review);

Accessing PHP objects using both naming with reserved characters (-,#) and indexes (0)

In PHP figured out how to use {name-with-reserved-chars} notation to access object data after JSON decode but I also have index values as well in the path (like [0]). My attempts to add the index value has returned nothing despite all my attempts to vary the pattern. Here is an example based on a big JSON object:
["ops:world-patent-data"]["exchange-documents"]["exchange-document"]
[0]["bibliographic-data"]["publication-reference"]["document-id"][0].date.$
my attempt gets nothing:
print $result->{'ops:world-patent-data'}->{'exchange-documents'}->{'exchange-document'}->
{0}->{'bibliographic-data'}->{'publication-reference'}->{'document-id'}->{0}->date;
wondering how to place the 0 and 1 indexes in the path ...
When deserializing an actual JSON list (i.e, not an object but an actual array with numerical indices; something like [1,2,3,...] or also [{...},{...},...]), PHP's parse_json function builds a corresponding PHP array with numerical indices (while on the other hands, it maps JSON objects to instances of the stdClass class -- unless you use the $assoc parameter of the parse_json function).
So, because you are accessing arrays instead of objects, your code would probably have to look like this:
print $result->{'ops:world-patent-data'}->{'exchange-documents'}->
{'exchange-document'}[0]->{'bibliographic-data'}->{'publication-reference'}->
{'document-id'}[0]->date;
It would probably easier to use the $assoc parameter, forcing json_decode to map the entire JSON object with associative arrays, allowing you to use the square bracket notation for object access also.

PHP SimpleXML #attributes behavior for nodelists

I've seen this problem a few times, and would like a definitive answer.
Given this structure in xml:
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
I am not able to access the attribute 'order'. var_dump (unsurprisingly) does not show any attributes for ByteListSet. Indeed, foreach iteration does not produce a #attributes item.
However, the following structure:
<ByteListSet>
<Byte Order="0"><Value>3729</Value></Byte>
</ByteListSet>
Results properly in ByteListSet having a child Byte that is a SimpleXmlObject which has #attributes.
I would assume that SimpleXML is indeed picking up the #attributes from the first case, but where is it keeping them? The trouble is that in the former structure, ByteListSet produces this on var_dump of ->children():
object(SimpleXMLElement)[25]
public 'Byte' => string '14' (length=2)
if I get_object_vars() on it and var_dump each, I simply get:
string '14' (length=2)
Indicating that Byte is not being returned to me as an xml object, but just as a string; as a property of the ByteList object above it.
Order="0" is there somewhere, but I don't have access to it. How do I get to it? NOTE: ->attributes() returns, as you would expect, a blank array.
(We do not control this schema, so it can't be restructured.)
You wrote:
Given this structure in xml:
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
I am not able to access the attribute 'order'.
Sure, because the attribute order does not exist. XML is case-sensitive, the attribute is named Order with uppercase O at the beginning.
Using the right attribute name allows you to access the value as outline in Example #5 in the basic examples of the SimpleXML extension you can find in the manual:
$ByteListSet = simplexml_load_string(<<<XML
<ByteListSet>
<Byte Order="0">14</Byte>
</ByteListSet>
XML
);
echo $ByteListSet->Byte['Order']; # 0
Please keep in mind that what you see in var_dump or print_r is not always what you get. This is espeically true for Simplexml (compare: How to get values of xml elements?; SimpleXML and print_r() - why is this empty?), however, also for other objects in PHP that make use of ArrayAccess, __toString() or IteratorIterator / IteratorAggregate.
Please let me know if that answers your question. You were not very specific which existing solutions you have not understood so far, so I answered your question, but I'm not 100% if I hit the nail.

How to find if a perversely constructed object is empty in PHP

I'm working with an object fed back from a class. For some reason, the class spits out an object with numbered properties (0, 1, 2, etc.). I need to check if the object is empty. The standard trick, empty(get_object_vars($obj)), won't work, because get_object_vars returns an empty array even when the object has (numbered) properties.
For reference, the object I'm working with is the one returned by the legislatorsZipCode method of the PHP interface for Sunlight's API. You can see a print_r of a sample response here.
Judging by the code, the author made the mistake of casting a numerically indexed array to an object. This makes getting object properties by name impossible, though you should still be able to foreach over it. You can also simply cast it back to an array: $results = (array) $obj;. Then, count the array.
This appears to work:
if (current($obj) === false)
echo "is empty\n";
It probably is doing an implicit cast to an array.

php array or not array?

I don't understand... I'm doing something and when I do print_r($var); it tells me that I have an array, so naturally I think I have an array yet when I do
if(is_array($xml->searchResult->item))
it returns false
I use this array with foreach(); in documentation it says that foreach() won't work with anything else but array, so assuming that this is an array that I'm working...
plus, if I try to access it via
echo $xml->searchResult->item[3];
i will get 4th element of my array
print_r will also print objects as though they are arrays.
well, is_array() returns true if your variable is an array, otherwise, it returns false. In your case, $xml->searchResult->item seems not to be an array. What is the output for
var_dump($xml->searchResult->item)
? Another hint: You can determine the type of a variable via gettype().
is_array() returns true only for real php arrays. It is possible to create a "fake" array by using the ArrayAccess class. That is, you can use normal array semantics (such as item[3]) but it is not a real array. I suspect your $item is an object. So use
if($x instanceof ArrayAccess || is_array($x))
Instead.
plus, if I try to access it via echo $xml->searchResult->item[3]; i will get 4th element of my array
That's right, the first element is always 0 unless you specifically change it.
The manual does mention that foreach works on objects as well, and it will iterate over properties.
In your case, the situation is slightly different because I guess you're using SimpleXML, which is yet another special case. SimpleXMLElement has its own iterator, which I assume is hardcoded as it doesn't seem to implement any of SPL's Iterator interfaces.
Long story short, some objects can be used as an array, but they are not one.
Just be careful to check what has been returned by your array. You might have an object with class or stdClass which is empty and you cannot get element from it.

Categories