Are PHP objects passed by reference ?
Tags: php, oop, beginners
Published: 2022-10-08 07:58:56

TL;DR : No.

For the longest time I've considered that Objects were "passed by reference" in PHP, and while it may not be technically true, it's a useful simplification that does the job in most cases. So if you read the title and thought "Yes they are" there's no need to panic, everything is probably fine, it doesn't mean that your apps will suddenly blow in your face. On the other hand if you thought "what on earth is a reference ?", you _miiight_ have a few surprises lurking in your codebase, and you're definitely in the right place !

So let's dig in with a refresher on references, then we'll see how it looks like objects *are* passed by reference, and then why it's actually a bit more complicated than that.

---

### References in PHP

The [PHP doc about references](https://www.php.net/manual/en/language.oop5.references.php) tells us that references are "aliases", which means that we can tell PHP we want two variables to point to the same data (the docs also tells us that despite appearances they are not pointers, which is not particularly helpful unless you're already proficient in C). The symbol to create a reference is `&`.

So we can do :
```php
$a = 'foo'; // $a contains 'foo'
$b = &$a;   // $b is now a reference, or an alias of $a

$b = 'bar' // We're actually changing the content of $a
echo $a    // displays 'bar' (because we just changed the value)
echo $b    // displays 'bar' (because it's still a reference to $a)
```

This is a nice little oddity, but really not that useful in your everyday code. Now what's a lot more useful is that you can __pass variables by reference__ to a function, so that the function can actually modify the content of the variable outside of its own scope.

This is done by appending the `&` in front of the parameter in the function declaration.

```php
/**
 * Decrements a number if it's > 0.
 * Returns true if it was decremented, false if it was already 0 or negative;
 */
function decrement(int &$a) {
    if ($a > 0) {
        $a = $a - 1;
        return true;
    }
    return false;
}

$timeLeft = 10;
$wasModified = decrement($timeLeft);

echo $timeLeft; // will output 9
```

As you can see, this allows us to return something, and modify what was passed to the function directly. This is notably used in the [preg_match()](https://www.php.net/manual/fr/function.preg-match.php) function that will return true if something matched, and accept a parameter `$m` that will be modified to contain what was matched. This is a useful feature, but it could also be the cause of hard to track bugs, so you should probably use this with caution.

Here's a nice animation from [this post](https://blog.penjee.com/passing-by-value-vs-by-reference-java-graphical/) that illustrates it nicely :

![Animation of a coffee cup that gets duplicated then filled. When passed by reference the original cup gets filled to](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j7zlwv9l4aosfm5smpv6.gif)


---

### Why objects look like they're passed by reference

It's likely that you've already read somewhere that objects are always passed by reference in PHP. And it sure looks like it.

Take this simple example :

```php
function doStuff($object) {
    $object->data = 'newValue';
}

$object = (object)['data' => 'value'];
doStuff($object);

echo $object->data; // displays 'newValue'
```


As you can see, there's no `&` in our function declaration, and yet `$object->data` now contains 'newValue' instead of 'value'. So the function did modify the object, which indeed looks a whole lot like it was passed by reference.


#### The issue with modifying objects

Here's a more concrete example that is the source of bugs in actual production codebases. Let's say we want to send our users an email with a receipt for the coming month, and we want the title to display the beginning of the period and the end of the period in a user friendly manner like "_From Thursday July 1 to Saturday July 31_", and the current date in the footer of the email. We'll create some functions to get the start and end of the month corresponding to the current date, and format them, then use the result in a string (or more realistically a template). It would look like this : 

```php
function getFirstDay($date) {
    return $date->modify('first day of this month')->format('l F j');
}

function getLastDay($date) {
    return $date->modify('last day of this month')->format('l F j');
}

$today = new DateTime('2022-07-15');
$firstday = getFirstDay($today);
$lastDay = getLastDay($today);

$emailTitle = "Your receipt for the period from $firstday to $lastDay";
$emailFooter = "Sent on " .  $today->format("l F j");
```

The title will say
"_Your receipt for the period from Thursday July 1 to Saturday July 31_", which is what we want, but the footer says "_Sent on Saturday July 31_", which is definitely not what we want. The issue is that $today was actually modified by the functions when we called ->modify() on the date, so as soon as we use this on a date we lose the original value of the date forever...

There are two ways around this :

1- clone the object before working on it :
```php
function getFirstDay($today) {
    $date = clone $today;
    return $date->modify('first day of this month')
        ->format('l F j');
}
```
2- Use a library like Carbon 2 that provides __Immutable Objects__, which are essentially objects where every method always returns a clone instead of modifying the current object.

---

### Why objects aren't really passed by reference

So we've seen that it looks an awful lot like objects are passed by reference and how to avoid potential issues, but the whole point of this post was to show that they're not, so here are a couple examples that make it obvious.

#### Example 1 : objects in an array

Let's assume a User class that takes the name of the user in the constructor and assigns it to a name property. Now let's see what happens if we put several of those users in an array and pass it to a function that makes the name uppercase for each element  :

```php
Class User
{
    public function __construct(public string $name){}
}

function capitalizeNames(array $users) {
    foreach ($users as $user) {
        $user->name = strtoupper($user->name);
    }
}

$users = [
    new User('John'),
    new User('Jack'),
];

capitalizeNames($users);

echo json_encode($users); // [{"name":"JOHN"},{"name":"JACK"}]
```

As you can see, each user in the array was modified by the capitalizeNames() function.

We didn't use a reference, we never passed an object to the function since we passed an array (by value) so we might think we're safe. And yet every object inside of the array was modified.


#### Example 2 : Assigning an object to another a variable

Now let's look at what happens if we assign an instance of an object to a new variable :

```php

$user1 = new User('name');
$user2 = $user1;

$user1->name = 'Jack';

echo $user1->name; // Jack
echo $user2->name; // Jack
```

So basically as soon as we do `$user2 = $user1;`, we can use both interchangeably and changing one will change the other.

### So what happened ?

The thing is that a variable never "contains" an object. Whenever we call `new`, PHP will return a _handle_, which is a ~~reference~~... a ~~pointer~~... yeah let's stick with handle. That handle is basically a number that identifies the object.
You can actually see this object identifier when using `var_dump()`, it's the number with a # in front when dumping an object, here with `var_dump($user1, $user2)` from our previous example, we can see that they're both the same object since they have the same handle : 

![Screenshot of a var_dump that shows the object handle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o51c2pfwle6ewjmjumx.png)

You could also use [`spl_object_id()`](https://www.php.net/manual/en/function.spl-object-id.php) which returns this object identifier.

Whenever we copy an object variable, pass it to a function or put it in an array, we're not passing or copying the instance of the object, only its handle. So **an object will only ever exist once** unless we use `clone`. Kind of like an object in real life.

---

### Conclusion

While it might be useful to think that objects are passed by reference, I think it's better to keep in mind that when you create an object there's only one instance that exists, and what you're using is not the object but simply a handle.

I hope this can be useful to someone. Also I don't have any background in C and only a vague understanding of how this is actually implemented in the PHP core. So if there's something blatantly wrong above please let me know in the comments.