multi-dimensional stdClass Object - php

I've got a rather large multidimensional stdClass Object being outputted from a json feed with PHP.
It goes about 8 or 9 steps deep and the data that I need is around 7 steps in.
I'm wondering if I can easily grab one of the entires instead of doing this:
echo $data->one->two->anotherone->gettinglong->omg->hereweare;
I'm saying this because the data structure may change over time.
Is this possible?

You could try to parse the object into an array and search the array for the wanted values, it just keeps looping through each level of the object.
function parseObjectArrayToArray($objectPassed, &$in_arr = array()) {
foreach($objectPassed as $element) {
if(is_object($element) || is_array($element)) {
parseObjectArrayToArray($element,$in_arr);
} else {
// XML is being passed, need to strip it
$element = strip_tags($element);
// Trim whitespace
$element = trim($element);
// Push to array
if($element != '' && !in_array($element,$in_arr)) {
$in_arr[] = $element;
}
}
}
return $in_arr;
}
How to call
$parsed_obj_arr = parseObjectArrayToArray($objectPassed);

Not without searching through whats probably inefficient.
Json is a structured data object with the purpose of eliminating something like this.

If the datastructure can change, but doesn't very often, your best bet is to write a wrapper object so you will only have to change a path at a single point on change:
class MyDataWrapp {
public $json;
function __construct($jsonstring){
$this->json = json_decode($jsonstring);
}
function getHereWeAre(){
return $this->json->one->two->anotherone->gettinglong->omg->hereweare;
}
}
If the datastructure changes dramatically and constantly, I'd json_decode as an array of arrays, and probably use RecursiveFilterIterator.

Related

Order alphabetically SimpleXML array by attribute

I need a bit of help ordering a SimpleXML array in an alphabetical order using one of it's attribute ($url). I have the following code that generate a page:
<?php
$xml = simplexml_load_file($xml_url);
$ids = array();
foreach($xml->list as $id) {
$ids[] = $id;
}
//It works but randomize instead of sorting alphabetically
usort($ids, function ($a, $b) {
return strnatcmp($a['url'], $b['url']);
});
foreach ($ids as $id) {
PopulatePage($id);
}
function PopulatePage($id) {
$url = $id->url;
$img = $id->img;
//OTHER CODES TO GENERATE THE PAGE
}?>
QUESTION RESOLVED!
There is no conversion needed, you already have an array which you can sort, you just need to understand how usort callbacks work. Each of the items in your $ids array is a SimpleXMLElement object, and each time your callback is run, it will be given two of those objects to compare. Those objects will be exactly the same as in the existing PopulatePage function, so accessing the URL needs to happen exactly as it does there ($url = $id->url;) not using a different notation ($id['url']).
To make it more explicit, let's write a named function with lots of clear variable names:
function CompareTwoIds(SimpleXMLElement $left_id, SimpleXMLElement $right_id): int {
$left_url = $left_id->url;
$right_url = $right_id->url;
return strncatcmp($left_url, $right_url);
}
Now you can test calling that function manually, and use it as the callback when you're happy:
usort($ids, 'CompareTwoIds');
Once you're comfortable with the principles, you may decide to skip the extra verbosity and just write this, which is completely equivalent:
usort($ids, fn($a,$b) => strncatcmp($a->url, $b->url));

Handling simplexml to array elegantly

I have an xml structure:
<node1><node2><child_1/><child_2/><child_3/></node2></node1>
And i would want to get an array like this:
['child_1', 'child_2', 'child_3']
But to make my method for creating this handle errors elegantly and return an empty array when nothing found i am having to do this:
public function testXmlParse()
{
$config = new SimpleXMLElement("<node1><node2><child_1/><child_2/><child_3/></node2></node1>");
$result = $config->xpath('/node1/node2');
if (! count($result)) {
return [];
}
$result = $result[0]->children();
}
But i have even more code to write to check for arrays and valid etc.
Is there an elegantly way to get the correct result and return 0 on nothing finding?
The code you have written won't return an array anyway - the result of ->children() is an iterable SimpleXMLElement object. However, you can take advantage of the fact that a zero-element object is still iterable with foreach, and will simply go round zero times.
Since you are always looking for the first match, your example can also use SimpleXML access instead of XPath, to avoid the extra logic there.
$config = new SimpleXMLElement("<node1><node2><child_1/><child_2/><child_3/></node2></node1>");
// Start with an empty array; if no children are found, it will stay empty
$results = [];
// Note: $config represents the <node1> element, not the document
foreach ( $config->node2->children() as $name => $element ) {
$results[] = $name;
}
If <node2> is not always present, you may need to add an extra if ( isset($config->node2) ) around the loop, to avoid PHP throwing you warnings.

Remove Object from array by object value in php

$followers = [['id'=>0], ['id'=>1]];
So, assuming I have this array, what would be the best way to remove the object by it's id?
That's what i came up with
foreach($followers as $key=>$follower){
if($follower->id == 0){unset $followers[$key]}
}
is there a better more efficient way?
It is an array not an object, so why are you accessing as an object?
Try this,
foreach ($followers as $key => $follower) {
if($followers[$key] == 0) {
unset($followers[$key]);
}
}
You were accessing values of an array like an object.
You were using unset incorrectly.
You were missing the ; at the end of your unset part.
What I really need is removing an item from array of arrays in my Cache I tried this:
if(Cache::has('user_follower'.$user_id)){
$user_follower = Cache::get('user_follower'.$user_id);
foreach($user_follower as $key=>$follower){
if($follower->user_id == Auth::id()){
unset($user_follower[$key]);
Cache::put('user_follower'.$user_id, $user_follower, 30);
return;
}
}
}
I failed sadly, so I'm for now I'm using Cache::forget.

How can I convert an array of strings to an array of integers?

I'm getting this error with my current PHP code:
Notice: TO id was not an integer: 1, 2. 1) APNS::queueMessage ->
How can I convert to an array of strings to an array of integers like in this other question: ID not integer... EasyAPNS ?
I'm basically trying to pass ids (from my database,) to newMessage() like this Apple example:
// SEND MESSAGE TO MORE THAN ONE USER
// $apns->newMessage(array(1,3,4,5,8,15,16));
// ($destination contain a string with the Ids like "1,2,3")
Here is my code below:
if (isset($destination))
{
//$destination = 1,2,..
$dest = explode(",", $destination);
if (isset($time))
{
$apns->newMessage($dest, $time);
}
else
{
$apns->newMessage($dest);
}
$apns->addMessageAlert($message);
$apns->addMessageBadge($badge);
$apns->addMessageSound('bingbong.aiff');
$apns->queueMessage();
header("location:$url/index.php?success=1");
}
I would create a wrapper function that accepts an array, and then call it.
function newMessageArray($array) {
foreach ($array as $element) {
$apns->newMessage($element);
}
}
This way, you can call newMessageArray() with an array of integers, such as array(1,2,3,4,5), and they will all be sent.
Also, you should change the variable names (from $array and $element) to something more meaningful. I don't know what you're trying to do, so I wasn't sure what names to use.
Your Question is not clear, It's only a guess work.
It seems to be error in convert integer, try
$dest = explode(",", $destination);
$destArray = array();
foreach($dest as $key => $val) {
$destArray[$key] = intval($val);
}
if (isset($time))
{
$apns->newMessage($destArray, $time);
}
else
{
$apns->newMessage($destArray);
}
Convert where the string is not integer using 'intval'.
I believe what you may be looking for is how to do this:
You have ids in your database table, right? And you are trying to get multiple ids into an array so that the array can be used in $apns->newMessage() call, right? (I checked the source for this...) But, the ids are somehow coming over as strings instead of ints.
So, you probably want to just make sure that the new array is made up of ints, like this:
function to_int($x) {
return (int)$x;
}
$dest = array_map("to_int", $dest);
There are probably other ways to do this, but this way, you at least know that you have int variables in that array. Hope that helps!

detecting infinite array recursion in PHP?

i've just reworked my recursion detection algorithm in my pet project dump_r()
https://github.com/leeoniya/dump_r.php
detecting object recursion is not too difficult - you use spl_object_hash() to get the unique internal id of the object instance, store it in a dict and compare against it while dumping other nodes.
for array recursion detection, i'm a bit puzzled, i have not found anything helpful. php itself is able to identify recursion, though it seems to do it one cycle too late. EDIT: nvm, it occurs where it needs to :)
$arr = array();
$arr[] = array(&$arr);
print_r($arr);
does it have to resort to keeping track of everything in the recursion stack and do shallow comparisons against every other array element?
any help would be appreciated,
thanks!
Because of PHP's call-by-value mechanism, the only solution I see here is to iterate the array by reference, and set an arbitrary value in it, which you later check if it exists to find out if you were there before:
function iterate_array(&$arr){
if(!is_array($arr)){
print $arr;
return;
}
// if this key is present, it means you already walked this array
if(isset($arr['__been_here'])){
print 'RECURSION';
return;
}
$arr['__been_here'] = true;
foreach($arr as $key => &$value){
// print your values here, or do your stuff
if($key !== '__been_here'){
if(is_array($value)){
iterate_array($value);
}
print $value;
}
}
// you need to unset it when done because you're working with a reference...
unset($arr['__been_here']);
}
You could wrap this function into another function that accepts values instead of references, but then you would get the RECURSION notice from the 2nd level on. I think print_r does the same too.
Someone will correct me if I am wrong, but PHP is actually detecting recursion at the right moment. Your assignation simply creates the additional cycle. The example should be:
$arr = array();
$arr = array(&$arr);
Which will result in
array(1) { [0]=> &array(1) { [0]=> *RECURSION* } }
As expected.
Well, I got a bit curious myself how to detect recursion and I started to Google. I found this article http://noteslog.com/post/detecting-recursive-dependencies-in-php-composite-values/ and this solution:
function hasRecursiveDependency($value)
{
//if PHP detects recursion in a $value, then a printed $value
//will contain at least one match for the pattern /\*RECURSION\*/
$printed = print_r($value, true);
$recursionMetaUser = preg_match_all('#\*RECURSION\*#', $printed, $matches);
if ($recursionMetaUser == 0)
{
return false;
}
//if PHP detects recursion in a $value, then a serialized $value
//will contain matches for the pattern /\*RECURSION\*/ never because
//of metadata of the serialized $value, but only because of user data
$serialized = serialize($value);
$recursionUser = preg_match_all('#\*RECURSION\*#', $serialized, $matches);
//all the matches that are user data instead of metadata of the
//printed $value must be ignored
$result = $recursionMetaUser > $recursionUser;
return $result;
}

Categories