foreach stdClass object converted to string - php

I am working response from an API which returns data in JSON in a fairly straight forward structure. Pseudo structure is best represented as:
services -> service -> options -> option -> suboptions -> option
As this is decoded JSON, all of these are stdClass objects. I have a number of foreach statements iterating through the various services and options, which all work without issue. However, when I use a foreach statement at the suboption level, the object is serialized into a string. For your information suboptions has a structure like this.
[suboptions] => stdClass Object
(
[option] => stdClass Object
(
[code] => SOME_CODE
[name] => Some Name
)
)
When using a foreach such as this (where $option is an option in services -> service -> options -> option):
foreach($option->suboptions->option as $suboption) {
print_r($suboption);
}
It outputs
SOME_CODESome Name
Not the expected
stdClass Object
(
[code] => SOME_CODE
[name] => Some Name
)
I am not aware of any reasons foreach would do this in terms of depth or other conditions.
I've tried everything I can think of and searched through SO and can't find any case of this happening. If anyone has any ideas I'd love to hear them! Cheers.
Apologies if it has been answered elsewhere or I am missing something obvious. If I have, it has escaped me thus far.
Edit: For those asking it is indeed the AusPost PAC API. Changing over from the DRC as it is being removed ~2014.

I can't comment, because I don't have that amount of reputation, but I'll try to answer...
I'm assuming you want to remove the stdClass from the PHP array?
If so, then make sure you are using true when decoding the JSON.
Example: $array = json_decode($json, true);
For more information on stdClass, see this SO post: What is stdClass in PHP?
Again, forgive me if I'm misinterpreting your question, I just wish I could comment...

I'm assuming this is from a response from the Australia Post postage calculation API. This API makes the horrible decision to treat collection instances with only one item as a single value. For example, service -> options -> option may be an array or it may be a single option.
The easiest way I've found to deal with this is cast the option to an array, eg
$options = $service->options->option;
if (!is_array($options)) {
$options = array($options);
}
// now you can loop over it safely
foreach ($options as $option) { ... }
You would do the same thing with the sub-options. If you're interested, I've got an entire library that deals with the AusPost API, using Guzzle to manage the HTTP side.

The values of the last object are not arrays so you would need both the key and value.
foreach($option->suboptions->option as $key => $value) {
echo '<p>KEY:'.$key.' VALUE:'.$value.'</p>';
}

Related

Array_push pushing empty values

I'm trying to push data to an array inside a foreach loop. Empty values get pushed into the array.
I tried logging out the values to see if they were empty but they were there, they only go missing when pushing to the array. I also tried to just assign the value to another variable and that worked fine.
$winners = \App\Winner::where('gameid', 577)->pluck('prizes_web_1');
$xml = simplexml_load_string(stripslashes($winners));
$winners_1 = [];
foreach($xml->Winner as $v) {
$out->writeln($v); //when logging here every value gets logged correctly
array_push($winners_1, $v);
}
$out->writeln($winners_1); //here an array with 4 empty values gets logged
I tried declaring the array as array(), the issue stayed. Tried assigning the value like so winners_1[] = $v still everything remained the same. Also tried using strval($v) but that didn't help either
I can't figure out what is causing the problem, never have I come across something like that when pushing to arrays.
EDIT
Here's an example of xml:
<?xml version='1.0' encoding='UTF-8'?>
<Winners>
<Winner><name>Robb Stark</name></Winner>
<Winner><name>Jon Snow</name></Winner>
<Winner><name>Aria Stark</name></Winner>
<Winner><name>Theon Greyjoy</name></Winner>
</Winners>
Also the $log->writeln() line are logging things to the console, when loging the $v I can see the values Robb Stark, Jon Snow etc logged, they dissapear when pushing to the array.
EDIT 2
Added more context to the sample code above.
EDIT 3 SOLUTION
Thank you #misorude
I just needed to cast my xml name element and it worked.
$winners_1[] = (string)$v->name;
The issue here wasn’t really adding the elements into the array, but what was actually added - and how it got processed / interpreted later on.
SimpleXML is what you’d traditionally call a “fickle mistress”. Often SimpleXMLElement instances behave like strings in certain contexts - but then don’t in a slightly different one.
I didn’t go look up the inner workings of Symfony’s ConsoleOutput, but how exactly that creates output from the input objects has probably played a role here.
Casper’s advice to cast them into strings was a good idea - if you don’t need any properties / methods a SimpleXMLElement object offers later on any more, and you just need their contained “data” - then casting them as soon as possible is a good way to avoid further troubles.
You can not directly cast $v into a string here though - because the Winner element did not contain the text directly, but it was wrapped in an additional name element. Casting a SimpleXMLElement that in turn contains other elements into a string would just result in an empty string again.
So the name element itself needs to be accessed and cast into a string here - (string) $v->name
Edit
You only needed to define winners array like this $winners_1 = array(); not $winners_1 = [];//this is used in javascript i guess I'm new myself.
i tested this and it's working fine over here.
try this,
$winners_1 = array();
foreach($xml->Winner as $v) {
$out->writeln($v); //when logging here every value gets logged correctly
$winners_1[] = $v;
}
$out->writeln($winners_1);
I think you are trying to add SimpleXMLElements elements to array. If you want to add the string value, you must cast the SimpleXMLElement to a string. Below I tried to reproduce your problem based on above discussion.
$myXMLData = "<?xml version='1.0' encoding='UTF-8'?>
<Winners>
<Winner><name>Robb Stark</name></Winner>
<Winner><name>Jon Snow</name></Winner>
<Winner><name>Aria Stark</name></Winner>
<Winner><name>Theon Greyjoy</name></Winner>
</Winners>";
Based on PHP documentation xml to array https://www.php.net/manual/en/book.simplexml.php
$xml = simplexml_load_string($myXMLData);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
Then loop,
$winners_1 = [];
foreach($array['Winner'] as $v) {
array_push($winners_1, $v['name']);
}
print_r($winners_1);
Answer:
Array ( [0] => Robb Stark [1] => Jon Snow [2] => Aria Stark [3] => Theon Greyjoy )

How to access properties of a nested PHP object?

I'm trying to do an web app which people can see their status in League of Legends, but I don't even know how to do some things. I've got this class:
stdClass Object
(
[player] => stdClass Object
(
[id] => xxxxxx
[name] => yyyy
[profileIconId] => 627
[summonerLevel] => 30
[revisionDate] => 1422798145000
)
)
and im using this php code:
<?php
$summoner = 'yohanbdo';
$summonerdata = $leagueclass->getsummoner($summoner);
?>
I want to take only the id, name and profileIconId and show it. I don't know how to do this.
PD: My english isn't that good so thanks you all for the edits.
Weird, I was just looking that Riot API a little while ago.
I feel like you're pretty new to this type of notation, so I'll try and be quick but concise with my explanation.
What you have there, as Gerifield stated, are objects. You access their properties by using the -> operator. For example, if I assume that the object $main is what you're var_dumping out, then you could simply get the objects like so:
$main = json_decode($some_json_string);
//Now that we have the object set, we can deal with the properties.
echo $main->player->name;
//This will output the player name.
echo $main->player->id;
//Will output the player ID.
Notice in each case that since the player key of the $main object is also an object, it's properties must be accessed via the -> operator.
However, you could also simply use associative arrays by passing the second parameter to json_decode, like so:
$main = json_decode($some_json_string,TRUE);
echo $main['player']['id'];
echo $main['player']['name'];
Hopefully this helps.

How to get ALL elements of simplexml object

OK, I'm totally stumped here. I've found similar questions, but the answers don't seem to work for my specific problem. I've been working on this on and off for days.
I have this here simplexml object (it's actually much, much, MUCH longer than this, but I'm cutting out all the extraneous stuff so you'll actually look at it):
SimpleXMLElement Object
(
[SubjectClassification] => Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Authority] => Category Code
[Value] => s
[Id] => s
)
)
[1] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Authority] => Subject
[Value] => Sports
[Id] => 54df6c687df7100483dedf092526b43e
)
)
[2] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Authority] => Subject
[Value] => Professional baseball
[Id] => 20dd2c287e4e100488e5d0913b2d075c
)
)
)
)
I got this block of code by doing a print_r on a variable containing the following:
$subjects->SubjectClassification->children();
Now, I want to get at all the elements of the subjectClassification array. ALL of them! But when I do this:
$subjects->SubjectClassification;
Or this:
$subjects->SubjectClassification->children();
OR if I try to get all the array elements via a loop, all I get is this:
SimpleXMLElement Object
(
[#attributes] => Array
(
[Authority] => Category Code
[Value] => s
[Id] => s
)
)
Why? How can I get everything?
You can use xpath to do this. Its the easiest way and most efficient I find and cuts down the need for lots of for loops and such to resolve items. To get all the nodes you want you can use:
if your xml is like this:
<Subjects>
<SubjectClassification>
</SubjectClassification>
<SubjectClassification>
</SubjectClassification>
<SubjectClassification>
</SubjectClassification>
</Subjects>
Then to get all subject classifications in an array you can do the following:
$subject_classifications = $xml->xpath("//SubjectClassification");
The xml variable refers to your main simplexml object i.e. the file you loaded using simplexml.
Then you can just iterate through the array using a foreach loop like this:
foreach($subject_classifications as $subject_classification){
echo (string) $subject_classification->Authority;
echo (string) $subject_classification->Value;
echo (string) $subject_classification->Id;
}
Your structure may vary but you get the idea anyway. You can see a good article from IBM here "Using Xpath With PHP":
Because of the extent to which SimpleXML overloads PHP syntax, relying on print_r to figure out what's in a SimpleXML object, or what you can do with it, is not always helpful. (I've written a couple of debugging functions intended to be more comprehensive.) Ultimately, the reference should be to the XML structure itself, and knowledge of how SimpleXML works.
In this case, it looks from the output you provide that what you have is a list of elements all called SubjectClassification, and all siblings to each other. So you don't want to call $subjects->SubjectClassification->children(), because those nodes have no children.
Without a better idea of the underlying XML structure, it's hard to say more, so I'll save this incomplete answer for now.
For all descendants (that are children, grand-children, grand-grand-children, grand-grand-... (you get the idea)) of <subjectClassification>s ("all the elements [...] ALL of them!" as you named it), you can make use of Xpath which supports such more advanced queries (at least I assume that is what you're looking for, your question does not give any detailed discription nor example what you mean by "all" specifically).
As for SimpleXML you can query elements (and attributes) only with Xpath, but as you need elements only, this is no show stopper:
$allOfThem = $subjects->xpath('./SubjectClassification//*');
The key point here is the Xpath expression:
./SubjectClassification//*
Per the dot . at the beginning it is relative to the context-node, which is $subjects in your case. Then looking for all elements that are descending to the direct child-element named SubjectClassification. This works per // (unspecified depth) and * (any element, star acts as a wildcard).
So hopefully this answers your question months after. I just stumbled over it by cleaning up some XML questions and perhaps this is useful for future reference as well.
I have added this second answer in case whats actually throwing you is retrieving the attributes array as opposed to the nodes. This is how you could print out the attributes for each SubjectClassification in your main $xml object.
foreach($xml->SubjectClassification->attributes() as $key => $value) {
echo $key . " : " . $value "\n";
}
I've found that count returns the proper number of elements, and you can then use a standard for loop to iterate over them:
$n = count($subjects->SubjectClassification);
for ($i = 0; $i < $n; $i++) {
var_dump($subjects->SubjectClassification[$i]);
}
I'm not sure why the foreach loop doesn't work, nor why dumping $subjects->SubjectClassification directly only shows the first node, but for any who stumble across this ancient question as I have, the above is one way to find more information without resorting to external libraries.

How to reference this variable (nested objects/arrays)?

I'm learning PHP and Drupal. I need to reference a variable contained in an array called $contexts.
So print_r($contexts) gives me this:
Array (
[context_view_1] => ctools_context Object (
[type] => view
[view] => view Object (
[db_table] => views_view
[result] => Array (
[0] => stdClass Object (
[nid] => 28
[node_data_field_display_field_display_value] => slideshow
)
)
eek confusing. I want to work with the node_data_field_display_field_display_value variable. I think my code needs to be like this, but I know this isn't right:
if ($contexts['context_view_1']['view']['result'][0]
['node_data_field_display_field_display_value'] == 'slideshow') then do whatever...
Thanks!
You suggested the following array reference to get to the variable you want:
$contexts['context_view_1']['view']['result'][0]['node_data_field_display_field_display_value']
The reason this doesn't work is because some of the structures in the chain are actually objects rather than arrays, so you need a different syntax for them to get at their properties.
So the first layer is correct, because $contexts is an array, so context_view_1 is an array element, so you'd get to it with $contexts['context_view_1'] as you did.
But the next level is an object, so to get to view, you need to reference it as an object property with -> syntax, like so: $contexts['context_view_1']->view
For each level down the tree, you need to determine whether it's an object or an array element, and use the correct syntax.
In this case, you'll end up with something that looks like this:
$context['context_view_1']->view->result[0]->node_data_field_display_field_display_value
That's a mess of a variable. The issue you're having is that you're using the bracketed notation, e.g. "['view']", for each "step" in the navigation through your variable. That would be fine if each child of the variable were an array, but not every one is.
You'll note, for example, that $contexts['context_view_1'] is actually an object, not an array (take note that it says "[context_view_1] => ctools_context Object"). Whereas you would use that bracketed notation to address the elements of an array, you use the arrow operator to address the properties of an object.
Thus, you would address the field you are trying to reach with the following expression:
$contexts['context_view_1']->view->result[0]->node_data_field_display_field_display_value
For properties listed as "Object", you need to use -> to get into it, and "Array", you need to use []. So:
$contexts['context_view_1']->view->result[0]->node_data_field_display_field_display_value
$contexts['context_view_1']->view->result[0]->node_data_field_display_field_display_value
echo $context['context_view_1']->view->result[0]->node_data_field_display_field_display_value;
Do not mistake objects with arrays. A memeber of an array can be accesed by $array['member'], but fields of an object can be accessed as $object->fieldname.

Working with Delicious API json response

When I try to find out the total count of delicious bookmarks, Delicious returns a json file which when decoded is:
Array (
[0] => stdClass Object (
[hash] => e60558db2d649c8a1933d50f9e5b199a
[title] => ISRAEL: Thousands march in Jerusalem rally, Israel still kicking
new families out of land they've owned for 60+ years
[url] => http://english.aljazeera.net/news/middleeast/2010/03/2010362141312196.html
[total_posts] => 2
[top_tags] => Array ()
)
)
The array is a stdClass Object. How can I extract [total_count] using PHP.
P.S Since I could not figure out how to extract it, I am using strpos to find 'total_count' and then I am cutting the string from that position and then extracting integers out of it. :)
BUt it is way too long.
Solved
If you pass a second argument, true, in your json_decode, it will return a regular array as opposed to an object. You might find this easier to work with.
$array = json_decode($json, true);
If you pass a second argument, true, in your json_decode, it will return a regular array as opposed to an object. You might find this easier to work with.
$array = json_decode($json, true);
Have you tried
echo $stdClass->total_posts;
Do you mean [total_posts]? If so, you should use $delArray[0]->total_posts.
See http://us.php.net/manual/en/language.types.object.php for samples on accessing object properties.

Categories