1

Hi a good day to everyone. I am new to PHP and here I am trying to push multiple objects ($viewership_prj) to a single array ($viewership_prj_arr).

The first time I wrote the following. The resulting array was not correct.

Basically whenever a new object is pushed into the array, all existing objects in that array are overwritten with the properties of that newly pushed object.

                         $viewership_prj_arr = array();
                         $viewership_prj = (object) array();
                         foreach($projects as $prj)
                         {   
                            /*...*/
                                            
                            $viewership_prj->title = $prj['project_title'];
                            $viewership_prj_arr[] = $viewership_prj;

                            // $viewership_prj_arr[] is [title1] after first loop
                            // [title2, title2] after second loop
                            // [title3, title3, title3] after third loop
                            // ...
                         }

Then I changed to this and it worked. I declared a new object inside each loop.

                         $viewership_prj_arr = array();
                         foreach($projects as $prj)
                         {   
                            /*...*/

                            $viewership_prj = (object) array();
                                            
                            $viewership_prj->title = $prj['project_title'];
                            $viewership_prj_arr[] = $viewership_prj;

                            // $viewership_prj_arr[] is [title1] after first loop
                            // [title1, title2] after second loop
                            // [title1, title2, title3] after third loop
                            // ...
                         }

I was very confused.

The only reason I could think of is that when the object is pushed into the array, it was passed by reference and not by value. I tried looking up the manual https://www.php.net/manual/en/language.oop5.references.php#:~:text=Objects%20and%20references%20%C2%B6,passed%20by%20references%20by%20default%22.&text=A%20PHP%20reference%20is%20an,the%20object%20itself%20as%20value. but it is not making much sense to me.

Could someone help me clarify this? Much thanks in advance.

姚一帆
  • 15
  • 5
  • You are correct. `$viewership_prj` is passed by reference as it's declared outside of the loop. Which means you are overriding the previously set title in the second iteration of the loop. Your adjustment is the way to go here. – PtrTon Mar 01 '21 at 10:45
  • Ooh wow thank you @PtrTon! So by default PHP objects are passed by reference? While reading the manual I thought it was only by reference when you added a '&' sign, e.g.`$viewership_prj_arr[] = &$viewership_prj;`. I would also like to clarify whether it is possible and/or recommended to pass an object by value? Thanks! – 姚一帆 Mar 01 '21 at 10:51

1 Answers1

0

Oversimplified, PHP stores an object as

"identifier to object" => actual object

Creating a new object $object_1 = (object)[] stores

"identifier to object 1" => actual object 1

$object_1 = "identifier to object 1"

The value of $object_1 is a reference to object 1 and NOT the actual object itself. They are two seperate items, linked by the identifier.

So, when you do

$object_1->title = "a";

$array[1] = $object_1;
$array[2] = $object_1;
$array[3] = $object_1;

Under the hood, it looks like:

"identifier to object 1" => actual object 1->title = "a"

$array[
  1 => "identifier to object 1",   // (actual object 1->title = "a")
  2 => "identifier to object 1",   // (actual object 1->title = "a")
  3 => "identifier to object 1",   // (actual object 1->title = "a")
  ];

Changing $object_1->title = "b" sets

  "identifier to object 1" => actual object 1->title = "b"

but doesn't change the identifiers in $array. They remain "identifier to object 1", so all the elements of the array are now:

$array[
  [1] => "identifier to object 1",   // (actual object 1->title = "b")
  [2] => "identifier to object 1",   // (actual object 1->title = "b")
  [3] => "identifier to object 1",   // (actual object 1->title = "b")
  ];

Also $object_2 = $object_1 will only copy the "identifier to object 1". Both variables point to the same object:

$object_1 = "identifier to object 1"
$object_2 = "identifier to object 1"

Any variable containing a reference to the actual object 1 can change the actual object 1.

$object_1->title = "c";
 or
$object_2->title = "c";
 or
$array[3]->title = "c";

do all exactly the same, as they all contain a reference to the same actual object 1. So changing one means changing all.

If you want all array elements to have their own object, you either have to create a new object for each and every one of them or - if you want to use the values of the previous object - use clone.

$array = [];
$names = [ "John", "Jack", "Jill" ];
$object = (object)[];
$object->name = "";
$object->coffee = "black with sugar";

foreach( $names as $name ){
  $object->name = $val;      //change the name of the actual object
  $array[] = clone $object;  //create a new object with the current values of $object
  }

Will output

$array[
 stdClass Object ( name=> John, coffee=> black with sugar ), 
 stdClass Object ( name=> Jack, coffee=> black with sugar ), 
 stdClass Object ( name=> Jill, coffee=> black with sugar ) 
]
//$object only contains the last change!
$object = stdClass Object ( name=> Jill, coffee=> black with sugar ) 
Michel
  • 4,076
  • 4
  • 34
  • 52