'PHP object loaded as reference in foreach loop

I am looping through an object and i try to duplicate one of the items while changing one of it's variables.

But when i copy the original and then change the title in the new one, the old one changes along with it. Which it shouldn't, since i did not initialised it as a reference.

$calendar = array(
    (object)[
        'id' => 1,
        'title' => 'original 1',
    ],
    (object)[
        'id' => 2,
        'title' => 'original 2',
    ],
    (object)[
        'id' => 3,
        'title' => 'original 3',
    ],
);

foreach ($calendar AS $key => $item){
    if($item->id == 2){
        $item->title = 'new 2';
        array_splice($calendar, $key, 0, [1]);
        $calendar[$key] = $item;
    }
}

echo "<pre>";
print_r($calendar);
die();

I would expect the output of this to keep original 2 intact. But it changes it along with it.

(
    [0] => stdClass Object
        (
            [id] => 1
            [title] => original 1
        )

    [1] => stdClass Object
        (
            [id] => 2
            [title] => new 2
        )

    [2] => stdClass Object
        (
            [id] => 2
            [title] => new 2
        )

    [3] => stdClass Object
        (
            [id] => 3
            [title] => original 3
        )

)

Even if i make a new object and use that one to make the changes, it still changes the orignial.

foreach ($calendar AS $key => $item){
    if($item->id == 2){
        $new_item = $item;
        $new_item->title = 'new 2';
        array_splice($calendar, $key, 0, [1]);
        $calendar[$key] = $new_item;
    }
}

Now i could probably fix this by just making a new object from scratch and copy the values one by one in it. But where's the fun in that?

So my question is...Why does this happen? Even though i didn't cast $item as &$item



Solution 1:[1]

Assignment or clone ?

Because, you're using objects, the problem is that $new_item = $item; doesn't create a new object, it creates a new reference of $item, named $new_item.

In the following example, $a and $b are the same object:

$a = new stdclass;
$b = $a;
var_dump($a, $b);

Output is:

object(stdClass)#1 (0) {...} // same object #1
object(stdClass)#1 (0) {...} // same object #1

Clone

You could use the keyword clone to create a new instance:

$a = new stdclass;
$b = clone $a; // Clone the object
var_dump($a, $b);

Output:

object(stdClass)#1 (0) {...} // object #1
object(stdClass)#2 (0) {...} // new object #2

So, in your case, you could use:

if ($item->id == 2) {
    $clone          = clone $item; // << Create a COPY of $item
    $clone->title   = 'new 2'; // Update the copy, not the reference
    $calendar[$key] = $clone; // Add this copy to final array
    // ...
}

A note about parameters

When you're using objects as parameters for some functions, objects are reference, so, the given object can be updated in that function. Here is a simple example (demo):

function updateObject(object $object): void {
    $object->newProperty = true;
}

$obj = new stdClass;
var_dump($obj);
updateObject($obj);
var_dump($obj);

The code above gives the following (condensed) output:

object(stdClass)#1 (0) { }
object(stdClass)#1 (1) { ["newProperty"]=> bool(true) }

Further reading : Objects and references

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1