Why is variable not changing in a PHP array of objects? - php

I am trying to assemble an array of objects from a variety of sources in PHP (I'm new to the language). The problem is that I am not able to store the data within the $bandwidthData array in the foreach loop I am trying to write.
private function _getData($startDay, $totalDays)
{
$devices = ubntDevices::getDevices();
$data = [];
$bandwidthData = [];
$count = 0;
foreach ($devices as $device) {
$bandwidthData[$count]['device'] = $device;
$bandwidthData[$count]['bandwidth'] = new ubntModel($device->file);
$bandwidthData[$count]['bandwidth']->getMonthData();
$bandwidthData[$count]['tree'] = new graphTree($device->hostid);
$bandwidthData[$count]['graphid'] = ubntGraph::getGraphByHostId($device->hostid);
$bandwidthData[$count]['hostname'] = $device->host_name;
$count++;
}
return $bandwidthData;
}
If I return from within the foreach loop, I get the correct output (but obviously for only the first device). I have tested all of the other function sources, and they seem to be returning the right data. Any idea what I could be doing wrong? Thank you in advance.

Your PHP error log should indicate what's going wrong. XDebug is very highly recommended as well.
However, nowadays it is more common to use an associative array like this:
private function _getData($startDay, $totalDays)
{
$devices = ubntDevices::getDevices();
$bandwidthData = [];
foreach ($devices as $device) {
$ubntModel = new ubntModel($device->file);
$deviceData = array('device' => $device,
'ubntModel' => $ubntModel,
'bandwidth' => $ubntModel->getMonthData(),
'tree' => new graphTree($device->hostid),
'graphid' => ubntGraph::getGraphByHostId($device->hostid),
'hostname' => $device->host_name);
$bandwidthData[] = $deviceData;
}
return $bandwidthData;
}

Some things I'm seeing:
Is this variable in use?
$data = [];
Also, this assignment runs but is a discouraged approach because at this point $bandwidthData[$count] does not exist:1
$bandwidthData[$count]['device'] = $device;
That can be converted to:
$bandwidthData[$count] = [ 'device' => $device ];
Also, this just a getter returning nowhere. Isn't it?
$bandwidthData[$count]['bandwidth']->getMonthData();
Also to further learn PHP I can suggest such cleaner approach for that code snippet, just to improve readability:
private function _getData( $startDay, $totalDays ) {
$bandwidthData = [];
foreach ( ubntDevices::getDevices() as $device ) {
$bandwidthData[] = [
'device' => $device,
'bandwidth' => new ubntModel( $device->file ),
'tree' => new graphTree( $device->hostid ),
'graphid' => ubntGraph::getGraphByHostId( $device->hostid ),
'hostname' => $device->host_name,
];
}
return $bandwidthData;
}
Anyway you have to learn how to debug that simple small block of code just looking at your server logs, or with a lazy var_dump( $bandwidthData[ $count ] ) in your suspected line (do not fight about this debugging approach: it's super stupid, but dramatically simple and effective, and friendly for newcomers - if you have not the possibility to setup a debugger because maybe the server is not yours etc.) or setting up a debugger.
1 from https://www.php.net/manual/en/language.types.array
If $arr doesn't exist yet, it will be created, so this is also an alternative way to create an array. This practice is however discouraged because if $arr already contains some value (e.g. string from request variable) then this value will stay in the place and [] may actually stand for string access operator. It is always better to initialize a variable by a direct assignment

Related

Same object property with different values

I have a problem occurring in one function, for me it's strange because I thought that the behaviour of objects were different. The function is:
function getMessages($imapCon)
{
$messageHeaders = array();
$tempObj = new stdClass();
$totalMessages = imap_num_msg($imapCon);
for ($i = $totalMessages; $i > 0; $i--)
{
$headers = imap_headerinfo($imapCon, $i);
$tempObj->Unseen = $headers->Unseen;
$tempObj->fromaddress = $headers->fromaddress;
$tempObj->Date = $headers->Date;
$tempObj->Subject = $headers->Subject;
$tempObj->uid = imap_uid($imapCon, $i);
array_push($messageHeaders, $tempObj);
}
return json_encode($messageHeaders);
}
In the json encoded, I get the same values for all properties (Unseen, fromaddress, Date...). The properties are set correct, but the values are duplicated. Why?
If I do something like:
for ($i = $totalMessages; $i > 0; $i--)
{
$tempObj = new stdClass();
...
array_push($messageHeaders, $tempObj);
unset($tempObj);
}
return json_encode($messageHeaders);
}
declaring the object inside the for, it works. But I believe this is a poor fix and nor the right thing to do...
Creating a new $tempObj inside the loop is the right way. When you add this object into array, it's not copied, but a pointer to it is added into the array. Check this documentation - http://php.net/manual/en/language.oop5.basic.php#example-191 (look at the simple assignment, ignore the $reference variable for now).
So you end up with array containing the whole bunch of links pointing to the same object. But if you create a new $tempObj inside the loop, all objects will be different.
Alternatively, you can do this:
array_push($messageHeaders, clone($tempObj));
but although this will equally work in this case, it may not work if the $tempObj would have more complex structure, such as instances of othe classes as properties of $tempObj.
On a side note, in PHP4 your code would work as you expected as PHP4 was copying actual objects on assignment instead of just pointers as PHP5 does.
It looks like you are just pushing a reference into the $messageHeaders array each time, but you are reassigning the data on the same instance. Not sure where you got the idea that it was wrong to create a new $tempObj on each iteration but that is one way you could solve this. Another way would be to just add to the $messageHeaders array directly like this:
$messageHeaders[] = array(
'Unseen' => $headers->Unseen,
'fromaddress' => $headers->fromaddress,
'Date' => $headers->Date,
'Subject' => $headers->Subject,
'uid' => imap_uid($imapCon, $i)
);

Fatal error: Using $this when not in object context using array_walk

I have made a class that should replace data at the hand of an array.
I insert
$data = array(
'bike',
'car',
'pc'
);
And it should translate the array to :
$data = array(
'[#bike]',
'[#car]',
'[#pc]'
);
I have written the following piece of code.
array_walk($PlaceHolders , function(&$value, $key) { $value = $this->Seperators['plcstart'].$value.$this->Seperators['plcend']; });
Which I am using in a class context.
The two sperators have the following value:
$this->Seperators['plcstart'] = '[#';
$this->Seperators['plcend'] = ']';
The problem is. At localhost it works superb!!
Now when I upload the system to a unix environment it stops working!
I have no clue why this is the result.
Would anyone here know what I am doing wrong?
I would recommend you to use array_map() when you want to modify array elements using a callback.
You could end with something like this
class TransformerClass {
private $Seperators = array(
'plcstart' => '[#',
'plcend' => ']'
);
public function transform(array $data) {
return array_map(
function($text) {
return $this->Seperators['plcstart'].$text.$this->Seperators['plcend'];
},
$data
);
}
}
Example:
$transformer = new TransformerClass();
$items = array(
'bike',
'car',
'pc'
);
$result = $transformer->transform($items);
And $result will contain your desired resulting data.
The most bizare thing is... This morning I opened the same page again.
And it worked like the offline environment :-S....
This is quite frustrating though!!
However I would like to make a few small notes:
///######## LOADING ALL PLACEHOLDERS
$PlaceHolders = array_keys($this->TplObjects);
///######## SEPERATOR START
$SeperatorStart = $this->Seperators['plcstart'];
$SeperatorEnd = $this->Seperators['plcend'];
///######## ADDING THE START AND END TAGS TO EACH ARRAY VALUE
array_walk($PlaceHolders , function(&$value, $key) { $value =
$SeperatorStart.$value.$SeperatorEnd; });
This is what I thought to be a solution but it is NOT!!
Why?? Because the way it was worked:
///######## LOADING ALL PLACEHOLDERS
$PlaceHolders = array_keys($this->TplObjects);
///######## ADDING THE START AND END TAGS TO EACH ARRAY VALUE
array_walk($PlaceHolders , function(&$value, $key) { $value = $this->Seperators['plcstart'].$value.$this->Seperators['plcend']; });
Because it got it's data directly out of the class.
By using function I placed the variables into it's own individual scope, hence the variable $SeperatorStart and $SeperatorEnd do not exist in this scope.
I could ofcourse import these two into that function. But I do not know how to do this with array_walk. I have not used this function this often hence I know only a few basic manners to use this function.
The second option as opted for above by #Falc works great! And is the method I was looking for. Hence thanks a million!!

Find nested array element, return node (most efficient way) [duplicate]

This question already has answers here:
How to search by key=>value in a multidimensional array in PHP
(17 answers)
Closed 9 years ago.
What would be the most efficient way of looking at an array of associative arrays, to find the node which meets a parameter?
I would like to have a more efficient way of looking through the array to find and return the parent node, that just looping through - looking at each element and returning if matched. (it is also safe to assume, that there are no duplicates of data - so the first found, is the only one found)
Or is a for loop the best thing ive got?
e.g.
array(
[0] => array('name' => 'fred'),
[1] => array('name' => 'dave'),
[2] => array('name' => 'mike)
)
And wanting to get the node of data where the name == 'dave' or to see if there is in fact a node which has a element name set as 'dave'.
e.g. somthing like
isset($data[]['name'] == 'dave')
$info = getdata($data[]['name'] == 'dave')
(Apologies if I'm not using the correct technical terms, please do correct me as I do like to learn!)
Many thanks in advance for any advice! =)
There is no better way than looping. PHP can't perform any magic that does not involve looking at each element in turn.
If you're doing this often, it helps to index your arrays by the search criterion:
$data = array(
array('name' => 'Dave'),
array('name' => ...)
);
$indexedData = array();
foreach ($data as $datum) {
$indexedData[$datum['name']] = $datum;
}
$info = $indexedData['Dave'];
As long as your data structure is sub-optimal, there's only sub-optimal ways to access it.
Here's a function for array recursion to one level. We use foreach() to loop through each second layer of child arrays, then use the built-in function array_search to see if it exists.
function as_nested($needle,$haystack){
$val;
foreach($haystack as $key=>$arr){
$arr_key = array_search($needle,$haystack[$key]);
if(!empty($arr_key)){
$val = $key;
}
}
return $val;
}
To execute, you supply the needle, then the haystack.
echo as_nested('dave',$myArray);
Output using your initial array is 1.
$myArray[0] = array('name'=>'fred');
$myArray[1] = array('name' => 'dave');
$myArray[2] = array('name' => 'mike');
There is a function in php called in_array() that looks for a value in an array.
//Code credit to #deceze
$data = array(
array('name' => 'Dave'),
array('name' => ...)
);
function getData($data, $searchValue) {
foreach ($data as $datum) {
if (in_array($searchValue, $datum)) {
return $datum;
}
}
//array returned when $searchValue is found.
You can use the getData function to search for a value in an array (this is not index specific. ie not restricted by only name, etc.)

Need help to grouping php arrays to make it better

First of all, my grouping is working but I feel it is dirty. Need someone to make it looks
clean and better.
I have following foreach
$data['new_array'] = array(); //I have to use $data['new_array'] because I need to pass it to template.
foreach ($get_topics as $topic) {
//Is that possible to make these 4 lines shorter?
$data['new_array'][$topic['tid']]['tid'] = $topic['tid'];
$data['new_array'][$topic['tid']]['title'] = $topic['title'];
$data['new_array'][$topic['tid']]['yes'] = $topic['yes'];
$data['new_array'][$topic['tid']]['no'] = $topic['no'];
//The belows are subarray grouping, it actually works but I need better solutions
//$new_array[$topic['tid']]['vid'][$topic['vid']][] = $topic['vid'];
//$new_array[$topic['tid']]['vid'][$topic['vid']][] = $topic['yesno'];
}
I wouldn't even try to make it shorter, but here's your code in a good looking version.
$data['new_array'] = array();
foreach ($get_topics as $topic) {
$data['new_array'][$topic['tid']] = array(
'tid' => $topic['tid'],
'title' => $topic['title'],
'yes' => $topic['yes'],
'no' => $topic['no']
);
}
Not sure what type is $topic['tid'] but you should be careful when using non-consecutive numbers as array keys.

PHP loop, date sorting challenge

I'm having trouble trying to figure out how to achieve this programming challenge in my Zend Framework application:
I need to create an array that looks like this:
$array = array(
0 => stdClass()->monthName
->monthResources = array()
1 => stdClass()->monthName
->monthResources = array()
);
This is the original array I have to work with:
$resources = array(
0 => Resource_Model()->date (instance of Zend_Date)
1 => Resource_Model()->date
2 => Resource_Model()->date
//etc...
);
The original array ($resources) is already sorted by date (descending) so I need to create an array where the resources are grouped by month. I only want the months that have resources, so if the resources skip a month, there shouldn't be a stdClass object for that month in the final array.
I also want this to process quickly, so any advice on optimizing the code (and still being readable) would be great. How can I achieve this?
My offering. No guarantees on its speed however it is O(n) and should in theory be faster than your method. This may not be true in any or all cases. However if you want something optimized you should have used a profiler to ensure that this is the function that is causing the speed problems rather than trying to make sections of code fast when they only account for .001% of the execution time. (In which case the maximum gain from optimizing the function would be .001%)
$resources = $this->fetchAll();
$sortedresources = array();
foreach ($resources as $resource) {
$monthName = $resource->getDate()->get(Zend_Date::MONTH_NAME);
if ( !isset($sortedresources[$monthName]) ){
//setup new data for this month name
$month = new stdClass();
$month->name = $monthName;
$month->monthResources = array();
$sortedresources[$monthName] = $month;
}
$sortedresources[$monthName]->monthResources[] = $resource;
}
//return the values of the array, disregarding the keys
//so turn array('feb' => 'obj') to array(0 => 'obj)
return array_values($sortedresources);
Maybe this helps (pseudo-code)
$finalArray = new array();
$tempStdClass = null;
foreach ($resObj in $resources)
{
if ($tempStdClass == null)
$tempStdClass = new StdClass($resObj->date);
if (tempStdClass->monthName != $resObj->date)
{
array_push($finalArray, $tempStdClass);
$tempStdClass = new StdClass($resObj->date);
}
array_push($tempStdClass->monthResources, $resObj);
}

Categories