По значению и по ссылке
Представьте, что автомат не забирает ваши монеты, а снимает с них точные копии и работает с копиями. Что бы он ни делал внутри, монеты в вашем кошельке остаются прежними.
Функция в PHP устроена так же: она получает копию каждого аргумента. Давайте проверим это на массиве — корзине покупок:
<?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 2Разберём по шагам:
addItem($cart, 'milk')передаёт в функцию копию массива$cart.$list[] = $item;добавляет'milk'в копию, и в$listтеперь два элемента.return $list;возвращает изменённую копию, и она попадает в$bigger.echoпечатает1 2: в$cartпо-прежнему один элемент.
Параметр получает копию
Такой способ называют передачей по значению: функция получает копию аргумента. Как вы уже знаете, $b = $a копирует массив целиком. Параметр получает аргумент так же, как переменная при присваивании, поэтому массив внутри функции — отдельный массив. С числами и строками то же самое.
Чтобы изменение дошло до вызывающего кода, функция возвращает новое значение, а вызывающий код сохраняет его: $cart = addItem($cart, 'milk');.
В жизни снять копию с монеты нельзя, а PHP делает это незаметно и экономно. Настоящая копия массива появляется, только когда функция в него пишет, поэтому передать большой массив в функцию дёшево.
Передача по ссылке: &
Иногда функция должна изменить саму переменную вызывающего кода. Для этого перед именем параметра ставят &. Это передача по ссылке: параметр становится вторым именем той переменной, которую передали в вызов. В картинке с автоматом & — это отдать автомату сам кошелёк.
function addBonus(int &$score): void
{
$score = $score + 10;
}
$points = 50;
addBonus($points);
addBonus($points);
echo $points, "\n"; // 70Давайте разберём:
int &$score— параметр по ссылке:$scoreвнутри функции и$pointsснаружи — два имени одной переменной.- Каждый вызов прибавляет
10прямо к$points. - После двух вызовов
echoпечатает70.
Знак & пишут только в объявлении функции, а в вызове стоит обычное имя переменной: addBonus($points). Передачу по ссылке используют редко: функцию, которая возвращает новое значение, проще читать.
Что ответить на собеседовании
Этот вопрос часто задают на собеседовании: «PHP передаёт аргументы по значению или по ссылке?» Ответ:
- по умолчанию по значению: функция получает копию, и массивы тоже;
- по ссылке — только если у параметра стоит
&; - объекты (о них — тема 11) тоже передаются по значению: копируется идентификатор — номер, по которому PHP находит объект, — поэтому функция работает с тем же объектом.
Знак & в foreach
Знак & встречается и в цикле foreach. Запись foreach ($prices as &$price) отдаёт в цикл сам элемент, а не копию, и присваивание меняет массив:
$prices = [10, 20, 30];
foreach ($prices as &$price) {
$price = $price * 2;
}
unset($price);
echo implode(' ', $prices), "\n"; // 20 40 60После цикла $price всё ещё связана с последним элементом массива. Если позже записать что-то в $price, например в следующем foreach с тем же именем, испортится последний элемент. Поэтому сразу после такого цикла пишут unset($price): он разрывает связь и не трогает массив.
Частая ошибка
В параметр по ссылке передали готовое число, а не переменную:
addBonus(50); // ✗Fatal error: Uncaught Error: addBonus(): Argument #1 ($score) could not be passed by referenceПараметру по ссылке нужна переменная, которую функция сможет изменить, а 50 — готовое значение, и изменение негде сохранить. Программа останавливается на этой строке. Исправление — передать переменную: $points = 50;, затем addBonus($points);.
Главное
- По умолчанию функция получает копию аргумента, и массивы тоже: изменения внутри не видны снаружи.
- Параметр
&$score— второе имя переменной из вызова, и запись в него меняет эту переменную. - В параметр по ссылке передают переменную, а не готовое значение.
- После
foreachсо знаком&переменную цикла убирают черезunset.