In PHP, what does a by-reference parameter such as &$param do, and what happens if you pass it a literal or an expression?
answer
- & in the declaration, never at the call
- binds to the caller's variable
- literal: Error, could not be passed by reference
- call result: Notice, Only variables should be passed
- copy-on-write makes & useless for speed
basics
~20 sA parameter declared &$param is bound to the caller's variable, so assignments inside the function change it. Only variables and returned references qualify: a literal throws Error, and a plain function result draws a notice with the change lost.
solid answer
~50 sPHP passes arguments **by value** by default. Declaring `&$param` makes the parameter a **reference to the caller's variable**: `sort($rows)` and `preg_match($re, $s, $matches)` write into your variables this way. The `&` goes only in the declaration; the call looks normal. The argument must be something that can be referenced: a variable, an array element or property (created if missing), or a reference returned by a `function &f()`. A literal or assignment expression, like `bump(5)`, throws `Error: bump(): Argument #1 ($n) could not be passed by reference`. The result of an ordinary function call, like `end(explode(',', $s))`, raises `Notice: Only variables should be passed by reference` and the function works on a temporary, so its writes are lost. Using `&` to save copying is a myth: arrays are copy-on-write, so passing by value is cheap until someone writes.
code
php · 22 lines<?php
declare(strict_types=1);
function addTax(float &$amount, float $rate = 0.2): void
{
$amount *= 1 + $rate;
}
$total = 100.0;
addTax($total);
echo $total, "\n"; // 120
try {
addTax(50.0);
} catch (Error $e) {
// addTax(): Argument #1 ($amount) could not be passed by reference
echo $e->getMessage(), "\n";
}
$csv = 'a,b,c';
$parts = explode(',', $csv);
echo end($parts), "\n"; // c, no notice: end() gets a variablego deeper
Recall that & goes in the function declaration, that it lets the function change the caller's variable, and that only variables can be passed.
Explain the difference between the Error for literals and the Notice for call results, and why copy-on-write makes & pointless for speed.
Show how you would refactor reference-heavy legacy functions into return values without breaking callers, and spot notices from end(explode()) style calls in logs.
Set a team convention on output parameters and in-place mutation, weighing PHP's own by-reference APIs against readability and static analysis.
## By value is the default When PHP calls a function, each parameter normally receives a **copy of the argument's value**. Changing the parameter inside the function leaves the caller's variable untouched. For arrays and strings the copy is lazy (**copy-on-write**): caller and callee share one value until one of them modifies it, and only then is it duplicated. Passing a large array by value therefore costs nothing extra as long as the function only reads it. ## What `&` on a parameter changes Writing `&` before a parameter name, `function addTax(float &$amount)`, makes that parameter a **reference**: another name for the caller's variable. Every assignment to `$amount` inside the function is an assignment to the caller's `$total`. Key facts: - the `&` appears **only in the declaration**; the call is written normally, `addTax($total)`; - a by-reference parameter **may have a default**, which makes an optional output parameter possible, as in `preg_match(string $pattern, string $subject, &$matches = null, ...)`; - it works with **named arguments** too: `parse($input, errors: $errs)` binds `$errs`; - a **by-reference variadic**, `int &...$counters`, binds each passed variable; - functions can also **return** a reference, `function &find()`, which the caller must take with `=&`. That is rarely needed in modern code. Built-ins that rely on it include `sort()` and the other sorting functions (they sort the array you pass and return `true`), `array_push()`, `array_pop()`, `end()`, and `preg_match()` with its `$matches`. ## What may be passed | Argument passed to `function bump(&$n) { $n++; }` | Result | |---|---| | a variable: `bump($count)` | `$count` is incremented | | an undefined variable or array element: `bump($hits['home'])` | created as `null` without a warning, then incremented to `1` | | a literal: `bump(5)` | `Error: bump(): Argument #1 ($n) could not be passed by reference` | | an assignment expression: `bump($x = 5)` | same `Error` | | a user function's return value: `bump(nextId())` | `Notice: Only variables should be passed by reference`; runs on a temporary, change lost | | a reference returned by `function &counter()` | the returned reference is bound | The `Error` is thrown at runtime, so it can be caught, but it signals a bug. The notice case is the classic `end(explode(',', $csv))`: it prints the last element, but the notice shows up in logs, and the fix is to assign `explode()`'s result to a variable first. ## Objects are a different case An object variable holds a **handle** to the object, not the object itself. A by-value parameter receives a copy of that handle, so calling methods or setting properties on it affects the same object the caller sees, with no `&` involved. `&` on an object parameter is needed only if the function must make the **caller's variable** point to a different object, which is almost never good design. ## When to use it, and when not 1. **Use it** for output parameters that PHP's own API is built around (`preg_match()`'s `$matches`), or for in-place algorithms on a caller's array that must not return a copy. 2. **Avoid it for speed.** Copy-on-write already makes by-value passing cheap, and a reference makes the data flow harder to follow for readers and static analysers. 3. **Prefer returning values.** Return the new array, or a small value object, rather than mutating arguments; callers can see what changed from the assignment. 4. **Never write `&` at the call site.** `f(&$x)` is not valid syntax in current PHP; call-time pass-by-reference was removed long ago. ## Spotting reference bugs in review References fail quietly, so a few patterns are worth looking for: - a function that both **returns** a value and **modifies** a by-reference argument, where callers use only one of the two and miss the other effect; - an undefined variable passed to a by-reference parameter, which silently comes into existence as `null` instead of raising the usual undefined-variable warning; - a chain like `array_pop(explode(...))` or `end(array_keys(...))`, which triggers the notice and should assign the intermediate result to a variable first; - a by-reference parameter added "for performance" on a function that never writes to it, which can be turned back into a plain by-value parameter.
- In PHP, why is declaring a large array parameter as &$rows usually not a performance win?Arrays are copy-on-write: a by-value parameter shares the caller's array until one side modifies it, so a function that only reads `$rows` never copies it. `&` saves nothing there, and it lets the function change the caller's data, which readers and static analysers then have to account for. It pays off only for deliberate in-place modification.
- Can a PHP by-reference parameter be optional, and can it be passed by name?Yes to both. `function parse(string $s, ?array &$errors = null)` lets callers omit the output parameter, as `preg_match()` does with `&$matches = null`. With a named argument, `parse($input, errors: $errs)` binds `$errs`, while `parse($input, errors: [])` throws the same could-not-be-passed-by-reference Error as a positional literal.
By value hands the function a photocopy of your page; by reference hands over your notebook itself, so whatever it writes stays in your notebook. You cannot hand over a notebook for a number shouted across the room, which is why a literal cannot be passed by reference.
saying these in an interview costs you the question
- Pass-by-reference is marked at the call site, as in foo(&$x).
- Passing a literal to a by-reference parameter silently passes a copy.
- end(explode(',', $s)) throws an Error because explode() returns a value.
- Array parameters should be by reference to avoid copying on every call.
- Objects need & for a function to change their properties.