Extra loop iteration happening when looping through array of objects - php

I made this object array
$trailheads[] = new StdClass;
Then I was putting an object in there each time it looped
$trailheads[] = $trailhead;
Then I returned the whole thing from the function like this:
$ret->ok = empty($errors);
$ret->errors = $errors;
$ret->o = $trailheads;
return $ret;
Once I got the values back, and loop through the results in the trailheads[] I keep looping 3 times instead of the expected 2. Is there a reason an extra iteration might be happening?
Here is how I try to loop:
foreach ($trailhead_list->o as $key)
{
$objkey = (object) $key;
echo '<p><h3>Trailhead Name: '.$objkey->trailhead_name.'</h3></p>';
}

After this:
$trailheads[] = new StdClass;
your $trailheads array contains on element, an instance of StdClass.
Then you add more elements, but there will be that first one.
Maybe you wanted to initialize the $trailheads[] like that, but instead you gave value to one of the array elements. Use this instead:
$trailheads = array();

This:
I made this object array
$trailheads[] = new StdClass;
... appears to be the source of the extra array element. Instantiate an array empty like this:
$trailheads = array();
Then loop and add objects:
$trailheads[] = $trailhead;
Also a note: I am guessing you don't have notices or warnings enabled. Turn them on while you develop. The line $trailheads[] = new StdClass; would have generated a notice, since you are adding a new element to a variable that hasn't been declared as an array (or anything else). PHP's loose typing is very forgiving, but this line would have generated a notice.
Warnings and notices on while you develop, turn them off for production. It will help you avoid common bugs and it will make you a better programmer to boot.

In the line:
$trailheads[] = new StdClass;
You are assigning a new StdClass object into the $trailheads array. That is to say you aren't declaring it as a variable but actually adding an element.

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)
);

Best practice for clearing an array

I have an array that I would like to clear the values out of and I'm wondering what the best way to accomplish this is.
I tried setting it to nothing:
$array = array();
... later on
$array = "";
Afterwards I'll add new values to it later:
foreach($something as $thing):
$array[] = $thing['item'];
endforeach;
And it seems to have done what I needed it too. But after a quick search online I'm seeing a lot of recommendations to do the following instead:
unset($array);
$array = array();
Is there any difference between this action and the one I performed up top?
Setting $array to "" sets your variable to a string value, and unset removes the variable. Since you are just trying to clear the array, then $array = array() should be good enough.
I believe that array() explicitly defines it as an array. Your first statement $array = "" sets it to an empty string. Using unset will "reset" the variable, so it's neither a string or array until you assign a value to it, and $array = array() simply defines it as a new blank array.

Object pushed to array overites

I've got strange thing, it's probably simple but I can't find a solution for it. Here is part of the code:
$counter = 0;
$autoload_view_instace = new Logic_InvoiceCostData;
$sub_view_cost = array();
foreach($invoceCostData as $data)
{
$counter++;
$parm = $autoload_view_instace->edit_view_data($autoload_view, $data, $counter);
array_push( $sub_view_cost, $parm);
}
The loop calls the edit_view_data method which returns an object with some values. That object should be placed at the end of the array in each iteration without changing values of objects previously added. But after each iteration, all objects in the array have the same value as the newly added object.
apparently the array_push syntax is correct.. and it should work .. however you can do the same thing using.
$sub_view_cost[]=$parm ;
and make sure each time $parm get the correct value

PHP - How to modify deeply nested associative arrays?

I'm having troubles building a deeply nested associative array in PHP. From the questions/answers I've seen here and there, I gathered I should use references but I just can't figure out how to do so.
I am using PHP 5.3
I'm parsing a file that looks like JSON. It contains nested "sections" enclosed in curly braces and I want to build up a tree representation of the file using nested associative arrays.
I'm starting with a root section and a "current section" variables:
$rootSection = array();
$currentSection = $rootSection;
$sections = array();
When I enter a new section ('{'), this is what I do:
$currentSection[$newSectionName] = array();
array_push($sections, $currentSection);
$currentSection = $currentSection[$newSectionName];
I use the $sections variable to pop out of a section ('}') into its parent one:
$currentSection = array_pop($sections);
And finally, when I want to add a property to my section, I basically do:
$currentSection[$name] = $value;
I've removed all attempt to use references from the above code, as nothing has worked so far...
I might as well say that I am used to Javascript, where references are the default...
But it's apparently not the case with PHP?
I've dumped my variables in my parsing code and I could see that all properties were correctly added to the same array, but the rootSection array or the one pushed inside $sections would not be updated identically.
I've been looking for a way to do this for a few hours now and I really don't get it...
So please share any help/pointers you might have for me!
UPDATE: The solution
Thanks to chrislondon I tried using =& again, and managed to make it work.
Init code:
$rootSection = array();
$currentSection =& $rootSection;
$sections = array();
New section ('{'):
$currentSection[$newSectionName] = array();
$sections[] =& $currentSection;
$currentSection =& $currentSection[$newSectionName];
Exiting a section ('}'):
$currentSection =& $sections[count($sections) - 1];
array_pop($sections);
Note that starting around PHP 5.3, doing something like array_push($a, &$b); is deprecated and triggers a warning. $b =& array_pop($a) is also not allowed; that's why I'm using the []=/[] operators to push/"pop" in my $sections array.
What I initially had problems with was actually this push/pop to my sections stack, I couldn't maintain a reference to the array and was constantly getting a copy.
Thanks for your help :)
If you want to pass something by reference use =& like this:
$rootSection = array();
$currentSection =& $rootSection;
$currentSection['foo'] = 'bar';
print_r($rootSection);
// Outputs: Array ( [foo] => bar )
I've also seen the syntax like this $currentSection = &$rootSection; but they're functionally the same.

Creating an array of objects in PHP

I would like to know what is the right of creating objects arrays in php.
My goal here is to be able to get data like this:
$obj = new MyClass();
echo $obj[0]->parameter; //value1
echo $obj[1]->parameter; //value2
Thanks for your time.
EDIT:
And if I want to do it in class it should look like this?
class MyClass{
public $property;
public function __construct() {
$this->property[] = new ProjectsList();
}
}
Any of the following are valid:
$myArray = array();
$myArray[] = new Object();
$myArray[1] = new Object();
array_push($myArray, new Object);
Try this,
$obj = array(new stdClass(), new stdClass())
or
$obj = array()
$obj[] = new stdClass()
$obj[] = new stdClass()
EDIT:
Class to stdClass
An important hint ...
The memory allocation for PHP arrays is exponential. A simple example: All array entries fit into an array, that allocates 2MB of memory. If this memory is no longer sufficient for all array entries, the memory allocation is expanded exponentially until the PHP memory limit is reached. If the array needs more than the 2 MB in this example, it will expand to 4MB, 16MB, etc.
When dealing with objects, better use collections. PHP provides the SplObjectStorage. This kind of collection allocates the exactly needed memory for its contents. Iteration over this collections behaves like using a yield. Only the memory for the current object in iteration is allocated. Beside that the SplObjectStorage class takes objects only. Should work perfectly for your purposes.
<?php
declare('strict_types=1');
namespace Marcel;
$collection = new SplObjectStorage();
$object1 = new stdClass();
$object2 = new stdClass();
$collection->attach($object1);
$collection->attach($object2);
The above shown code allows some stricter searching.
$collection->contains($object1); // returns true
Detaching an item works like a charme.
$collection->detach($object1);
$collection->contains($object1); // returns false
Iterations works with a pretty fast foreach loop or a while loop.
$collection->rewind();
foreach ($collection as $object) {
// yielding works here
}
$collection->rewind();
while ($collection->valid()) {
// yielding also works here automatically
$collection->next();
}
hope this will be helpful
$newArray[] = (object) array();
Honestly, I think you are on the right path. from what it sounds like you are not just trying to add to arrays, but convert arrays to objects.
<?php
$obj = (object) 'ciao';
echo $obj->scalar; // outputs 'ciao'
?>
PHP Objects
EDIT: I don't think you could add an object like this:
$this->property[] = new ProjectsList();
the "new ProjectsList()" would be how you would create an object from a class. ProjectsList would need to be a class. it would look more like this:
$obj = new ProjectsList;
$this->property[] = $obj;
you would need to make sure the ProjectsList existed first though.

Categories