skip to content

In PHP, are objects passed by reference, and what really happens when you assign an object or pass it to a function?

level: juniorimportance: must knowfreq 72%

answer

  1. what the variable actually stores
  2. assignment copies an identifier
  3. method calls reach the shared instance
  4. rebinding a parameter stays local
  5. & aliases the variable itself

basics

~20 s

No. A PHP object variable holds an object handle, and assignment, argument passing and return copy that handle, so both sides reach one instance. Rebinding a parameter never affects the caller; only & creates a true alias.

solid answer

~40 s

Strictly, no. A variable that "holds" an object in PHP holds an **object handle** (an identifier the engine uses to find the object), not the object and not a PHP reference. `$b = $a`, passing `$a` to a function and returning it all copy the handle, so property writes and method calls through either variable hit the **same instance** — which is why it *looks* like pass-by-reference. The difference shows when you **rebind**: `$param = new Quote()` inside a function only changes the local copy of the handle, and the caller still sees its original object. A real PHP reference (`&$param`, `$d = &$c`) makes two names for one variable slot, so rebinding one rebinds the other. When you need a second, independent instance you use `clone`.

code

php · 29 lines
php
<?php
declare(strict_types=1);

final class Quote
{
    public array $lines = [];
}

function addLine(Quote $q): void
{
    $q->lines[] = 'desk';     // mutates the shared instance
}

function replace(Quote $q): void
{
    $q = new Quote();         // rebinds the local handle only
}

function replaceByRef(Quote &$q): void
{
    $q = new Quote();         // rebinds the caller's variable
}

$draft = new Quote();
addLine($draft);
replace($draft);
echo count($draft->lines), PHP_EOL;  // 1
replaceByRef($draft);
echo count($draft->lines), PHP_EOL;  // 0

go deeper

for a junior

Recall that an object variable holds a handle: assignment and passing share one instance, and clone is how you get a second one.

for a middle

Explain the difference between a copied handle and a PHP reference by showing what happens when a parameter is rebound versus mutated.

for a senior

Show where shared handles cause bugs in real code, such as getters leaking internal objects or services mutating a caller's draft, and how clone or immutability contains it.

for a principal

Discuss when a codebase should rely on shared mutable objects versus immutable value objects, and how that choice shapes API contracts between teams.

## What an object variable actually holds PHP has two kinds of "sharing" that are easy to confuse: - An **object handle** (the manual calls it an *object identifier*) is a small value that points at an object living in the engine's object store. `new Quote()` creates the object and gives you a handle to it. - A **PHP reference** (created with `&`) is an alias: two variable names bound to the *same variable slot*, whatever that slot contains. An object variable holds a handle. The object itself is never stored "in" the variable, and it is never copied implicitly. `var_dump()` shows the handle's number as `#3` in `object(Quote)#3`, and `spl_object_id()` returns the same integer. ## Assignment, arguments and return values Every ordinary transfer copies the **handle**, never the object: 1. `$b = $a;` — both variables now hold copies of one handle. 2. `save($a)` — the parameter receives a copy of the handle. 3. `return $this->quote;` — the caller receives a copy of the handle. Because every copy points at the same object, any **mutation through a handle** — `$b->total = 10`, `$param->addLine(...)` — is visible through all the others. That is the behaviour people describe as "objects are passed by reference", and it is why the phrase survives. It is still not a reference, and the difference is observable. ## Where a handle and a reference differ | Operation | Handle copy (`$b = $a`, plain parameter) | Reference (`$b = &$a`, `&$param`) | |---|---|---| | `$b->x = 1` | changes the shared object | changes the shared object | | `$b = new Quote()` | only `$b` now points elsewhere | `$a` points to the new object too | | `$b = null` | `$a` still holds the object | `$a` becomes `null` as well | | `unset($b)` | removes `$b` only | removes the name `$b`; `$a` keeps its value | The second and third rows are the interview test. A function that does `$quote = new Quote();` on a plain parameter creates a new object that disappears when the function returns; the caller never sees it. Declaring the parameter as `Quote &$quote` makes the rebinding reach the caller — which is rarely what you want and is usually better expressed by returning the new object. ## Consequences in real code - **Collaborators can mutate what you hand them.** Passing a draft quote to a pricing service lets that service change the draft. If that is not allowed, pass a copy (`clone`) or design the class as immutable. - **Getters leak internals.** Returning an object-valued property returns a handle to the internal object, so callers can mutate your state from outside. Return a copy or an immutable type. - **An object lives while any handle to it exists.** Unsetting one variable does not destroy an object that another variable, property or array element still points to. - **Arrays are different.** An array is a value with copy-on-write semantics: assigning it copies it logically. But an array *of objects* copies the handles, so the elements still point to the same instances. - **`&` on an object parameter is almost never needed** to modify the object's properties. It is only needed to rebind the caller's variable. ## Getting a second instance To obtain an independent object you must ask for one explicitly: `clone $quote` creates a new instance with copies of the property values (a shallow copy — nested objects are shared again by handle), or you construct a new object. Nothing in PHP copies an object behind your back, which is exactly why aliasing bugs happen when two parts of a program assume they each own "their" object. ## Reading the evidence while debugging Two tools make the handle model visible: - `var_dump($a, $b)` prints the same `#N` for both variables after `$b = $a`, and a different number after `$b = clone $a`. - `spl_object_id($a) === spl_object_id($b)` is true exactly when `$a === $b` for two live objects. Ids are recycled once an object is destroyed, so a stored id can later belong to a different object; compare live objects with `===` instead of keeping ids around. When a bug report says a quote "changed by itself", the first step is to find which other variable, property or array element still holds a handle with the same `#N`. ## Summary - Object variables hold handles; handles are copied on assignment, passing and return. - Mutations are shared because the object is shared. - Rebinding is local unless you used `&`. - `clone` is the only built-in way to get a second instance of the same state.

  • If objects are shared through handles, when would you still declare a parameter as `&$obj`?
    Only when the function must make the caller's variable point at a different object or at `null`, for example a helper that lazily creates an instance into a variable it was given. Modifying properties never needs `&`. In modern code the same effect is usually clearer as a return value: `$quote = $factory->ensure($quote);`.
  • How do you check whether two variables point at the same instance?
    Use `===`: for objects it is true only when both operands are the same instance. `spl_object_id()` returns the integer id that `var_dump()` shows as `#N`, which helps when debugging, but ids are reused after an object is destroyed, so they are not safe as long-lived keys.

An object handle is like a cloakroom ticket: photocopy the ticket and two people can reach the same coat, and either can sew a button on it. If one of them swaps their ticket for another coat's ticket, the other still holds a ticket for the original coat. A PHP reference is instead two names for one ticket holder, so a swap by either name is a swap for both.

saying these in an interview costs you the question

  • Objects are passed by reference exactly like &$var parameters.
  • Assigning $b = $a copies the object's properties into a new object.
  • Reassigning a parameter inside a function replaces the caller's object.
  • A function needs &$obj to be able to change the object's properties.
  • Objects are copied on write, just like arrays.