Passing objects by reference and by value

The other day it happened to me again. I was creating a wrapper function that was calling some functions. The output of one function would be used as input for the next one. Basically I tried to simplify the code and to make sure the functions were doing just a single thing.

I called the wrapper function and printed the intermediate outputs from the inner functions to the screen. Unfortunately, the results were not what I expected. It took a while to understand what was going on here. Then I thought back to the thing I once read about passing arguments to functions by reference versus passing them by value.

Case study

Look at the following piece of code:

$now = new DateTime('now');
echo 'now: ' . $now->format('Y-m-d');
$tommorow = addDay($now);
echo '<br>now: ' . $now->format('Y-m-d');
echo '<br>tommorow: ' . $tommorow->format('Y-m-d');

function addDay($date) {
   $tommorow = $date;
   $tommorow->add(new DateInterval('P1D'));
   return $tommorow;
}

So we start with today and put this in a variable $now. Than we create a variable $tommorow and set that to one day after today. However, the output of this code snippet yields

now: 2015-08-05
now: 2015-08-06
tommorow: 2015-08-06

Passing argument by value

When you call a function and you pass an argument by value, it is the value that you can use. Setting some variable inside the function to the argument passed and the changing the variable does not change the argument passed.

Passing argument by reference

This is quite the opposite when you pass a value by reference. You basically do not pass the value, but rather a pointer to the place where the value is saved. If you copy the argument to a new variable inside the function and then change the variable, the original argument is also changed because the two variables point to the same thing.

Simple variables versus objects

There is one more note to this story though. You see, simple variables as strings, integers and floats are by default passed by value. But this is different for objects. If you pass an object to a function it is implicitly passed by reference. And this is why my code behaved the way it did. I was passing around DateTime objects and hence, these objects are passed by reference.

And the solution?

A solution is to clone the object when assigning it to another variable. The code below does work as expected:

$now = new DateTime('now');
echo 'now: ' . $now->format('Y-m-d');
$tommorow = addDay($now);
echo 'now: ' . $now->format('Y-m-d');
echo 'tommorow: ' . $tommorow->format('Y-m-d');

function addDay($date) {
    $tommorow = clone $date;
    $tommorow->add(new DateInterval('P1D'));
    return $tommorow;
}

I hope this little explanation will help others save some time. It is at least a good reminder for myself, because now I wrote it down I’ll probably won’t forget about this anymore!


Mijn Twitter profiel Mijn Facebook profiel
Pim Hooghiemstra Webdeveloper and founder of PLint-sites. Loves to build complex webapplications using Vue and Laravel! All posts
View all posts by Pim Hooghiemstra

Leave a Reply

Your email address will not be published. Required fields are marked *