1

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

2 Answers 2

4

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

Sign up to request clarification or add additional context in comments.

Comments

2

As simple PHP Object assignment does not create a new object. It simply creates a new pointer to the same object.

I think you want to use the clone keyword:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.