By value and by reference
Imagine the machine does not take your coins but makes exact copies of them and works with the copies. Whatever it does inside, the coins in your wallet stay the same.
A function in PHP works the same way: it gets a copy of every argument. Let's check this on an array, a shopping cart:
<?php
function addItem(array $list, string $item): array
{
$list[] = $item;
return $list;
}
$cart = ['tea'];
$bigger = addItem($cart, 'milk');
echo count($cart), ' ', count($bigger), "\n"; // 1 2Let's go step by step:
addItem($cart, 'milk')passes a copy of the array$cartinto the function.$list[] = $item;adds'milk'to the copy, and$listnow has two elements.return $list;returns the changed copy, and it goes into$bigger.echoprints1 2:$cartstill has one element.
A parameter gets a copy
This way is called passing by value: the function gets a copy of the argument. As you already know, $b = $a copies a whole array. A parameter gets its argument the same way a variable does in an assignment, so the array inside the function is a separate array. Numbers and strings work the same way.
For a change to reach the calling code, the function returns the new value, and the calling code keeps it: $cart = addItem($cart, 'milk');.
In real life you cannot copy a coin, while PHP does it unnoticed and cheaply. A real copy of an array appears only when the function writes to it, so passing a large array to a function is cheap.
Passing by reference: &
Sometimes a function must change the calling code's variable itself. For that, you put & before the parameter's name. This is passing by reference: the parameter becomes a second name for the variable passed in the call. In the machine picture, & means handing the machine your wallet itself.
function addBonus(int &$score): void
{
$score = $score + 10;
}
$points = 50;
addBonus($points);
addBonus($points);
echo $points, "\n"; // 70Let's take it apart:
int &$scoreis a parameter by reference:$scoreinside the function and$pointsoutside are two names for one variable.- Each call adds
10straight to$points. - After two calls,
echoprints70.
The & sign is written only in the function declaration, while the call has a plain variable name: addBonus($points). Passing by reference is rare: a function that returns a new value is easier to read.
What to answer in an interview
Interviewers often ask this: "Does PHP pass arguments by value or by reference?" The answer:
- by value by default: the function gets a copy, arrays included;
- by reference only when the parameter has
&; - objects (see theme 11) are passed by value too: the copied handle is the number PHP finds the object by, so the function works with the same object.
The & sign in foreach
The & sign also appears in the foreach loop. Writing foreach ($prices as &$price) hands the element itself to the loop instead of a copy, and an assignment changes the array:
$prices = [10, 20, 30];
foreach ($prices as &$price) {
$price = $price * 2;
}
unset($price);
echo implode(' ', $prices), "\n"; // 20 40 60After the loop, $price is still tied to the array's last element. If you later write something into $price, for example in the next foreach with the same name, the last element gets spoiled. That is why such a loop is followed right away by unset($price): it breaks the tie and leaves the array alone.
Common mistake
A plain number was passed to a parameter by reference instead of a variable:
addBonus(50); // ✗Fatal error: Uncaught Error: addBonus(): Argument #1 ($score) could not be passed by referenceA parameter by reference needs a variable the function can change, while 50 is a plain value with nowhere to store a change. The program stops at this line. The fix is to pass a variable: $points = 50;, then addBonus($points);.
Key points
- By default a function gets a copy of the argument, arrays included: changes inside are not seen outside.
- The parameter
&$scoreis a second name for the variable from the call, and writing to it changes that variable. - A parameter by reference takes a variable, not a ready value.
- After a
foreachwith the&sign, the loop variable is removed withunset.