0

I'm seeing some confusing behavior related to reference/assignment in PHP...

private function doSomething($things)
{
    foreach ($things as $thing) {
        echo $thing->property; // 'foobar'
        $copyThing = $thing;
        unset($copyThing->property);
        echo $thing->property; // undefined

I expect this behavior when passing variables by reference (&$thing) but I'm not trying to do that here and it seems to be happening anyway. What am I missing?

8
  • 1
    objects in foreach loops are always passed by reference Commented Aug 3, 2016 at 22:01
  • 1
    $thing is already a passed reference of $things as your in a foreach loop Commented Aug 3, 2016 at 22:02
  • @M.M Whoa! Did that default change in any recent versions of PHP? I swear I remember having to use &$var to overtly pass by reference. Commented Aug 3, 2016 at 22:03
  • 2
    If it's an object, then no.... because a variable holding an object is always a pointer Commented Aug 3, 2016 at 22:04
  • 1
    You would need to clone your object, for instance: foreach ($things as $thing) { $copyThing = clone $thing; unset($copyThing->property); } Commented Aug 3, 2016 at 22:09

1 Answer 1

3

Just explaining my comment:

objects in foreach loops are always passed by reference

When you use a foreach loop for an array of objects the variable that you are using inside the loop is a pointer to that object so it works as a reference, any change on the object inside the loop is a change on the object outside. This is because:

objects are always passed by reference (@user3137702 quote)

Detailed and official explanation here.


When you copy and unset your variable:

$copyThing = $thing;
unset($copyThing->property);

you are creating another pointer and unseting it, so the original value is a gone. As a matter of fact, since the foreach loop also uses a pointer the $things array is also affected.

check this ideone (notice the vardump [where the 'a' property is gone], as the output is the same as you got)


I do not know in which version it changed, if ever, as it seems like default object/pointer behavior


As a workaround (some ideas):

  1. Copy your initial array
  2. Use clone: $x = clone($obj); (As long as the default copy constructor works for your objects)
Sign up to request clarification or add additional context in comments.

5 Comments

I would amend this to simply say "objects are always passed by reference" , as this behavior doesn't really have anything to do with a foreach loop.
Aha! I get it now. I initially misunderstood it to be about all foreach loops... but now I understand that it's unique to objects. Is it correct that if I contstructed the exact same example but instead of unseting object properties I unset keys in an array it would not be by reference?
Yes, correct. you would be unsetting the copy, just because: ideone
@M.M Is there a way to prevent this default behavior? In other words, is there a way I can just copy the object without effecting the original?
@emersonthis as I said in the workaround part of the answer you could use clone to copy your object to another variable (another memory location so there would be a new pointer)

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.