Анонимные функции и callable
Чему вы научитесь:
- хранить функцию в переменной и передавать её в другую функцию через параметр
callable; - захватывать внешние переменные через
useи отличать копию значения отuse (&$x); - писать короткие стрелочные функции
fnи делать замыкание из готовой функции записьюstrlen(...); - сортировать массив по своему правилу с помощью
usortи оператора<=>.

В первой теме функция была карточкой с рецептом: основной рецепт отсылал к карточке крема, повар готовил по ней и возвращался с результатом. Такая карточка лежит на своём месте, и её зовут по имени.
Теперь представьте, что карточку можно взять в руки и отдать другому человеку. Кондитер не знает заранее, какую начинку вы хотите. Вы протягиваете ему карточку с рецептом, и он готовит по ней.
С функцией в PHP можно обращаться так же: положить её в переменную, передать в другую функцию и вызвать там. Начнём с функции в переменной:
<?php
$double = function (int $n): int {
return $n * 2;
};
echo $double(4), "\n"; // 8
echo $double(10), "\n"; // 20
echo gettype($double), "\n"; // objectДавайте разберём программу по строкам:
function (int $n): int { … }— функция без имени. У неё есть параметр, тип результата и тело, как у обычной функции.$double = …;кладёт эту функцию в переменную$double. Точка с запятой после}закрывает присваивание.$double(4)вызывает функцию из переменной: круглые скобки с аргументом стоят сразу после имени переменной. Вызов возвращает8.echo gettype($double)печатаетobject: функция в переменной — значение особого вида, объект встроенного классаClosure. Классы и объекты разберём в теме 11, а пока достаточно знать, что такое значение можно вызвать.
Функция без имени
Анонимная функция — это функция без имени, которую записывают там, где нужно значение. Её можно присвоить переменной, передать аргументом или вернуть из другой функции через return.
От функции с именем она отличается записью:
- после слова
functionсразу идут круглые скобки с параметрами, а имени нет; - вся запись — выражение, поэтому в присваивании после
}нужна;.
Есть и разница во времени. Функцию с именем PHP запоминает до запуска, и вызов может стоять выше объявления. Анонимная функция появляется, только когда выполнится строка с присваиванием, поэтому вызов $double выше этой строки закончится ошибкой.
Здесь картинка с карточкой неточна. Чтобы отдать настоящую карточку двоим, её пришлось бы переписать. А после $g = $double копии не появляется: обе переменные держат один и тот же объект Closure.
Параметр callable
Теперь передадим функцию в другую функцию. Функция apply получает функцию $f и число $x и возвращает результат вызова $f($x):
function apply(callable $f, int $x): int
{
return $f($x);
}
echo apply($double, 7), "\n"; // 14
echo apply(function (int $n): int {
return $n + 1;
}, 7), "\n"; // 8Разберём по шагам:
callable $f— тип параметра: сюда можно передать только то, что можно вызвать.- Внутри
applyвызов$f($x)запускает переданную функцию с аргументом$x. apply($double, 7)передаёт функцию из переменной без скобок. Она не вызывается сразу, а уходит вapply, и та возвращает14.- Во втором вызове анонимная функция записана прямо в скобках вызова.
applyвызывает её с7и возвращает8.
Функция apply сама решает, когда вызвать $f. Так и кондитер сам решает, когда готовить по вашей карточке. Значение, которое можно вызвать круглыми скобками, называют вызываемым значением.
Тип callable пропускает в функцию только вызываемые значения. Если передать в apply число, PHP остановит программу с TypeError ещё до первой строки тела.
Имя функции в строке
Вызываемым значением бывает и строка с именем функции.
Возьмём встроенную функцию abs: она возвращает модуль числа — его расстояние от нуля. abs(-5) равно 5, и abs(5) тоже 5. Передадим её в apply по имени:
echo apply('abs', -5), "\n"; // 5
$name = 'strtoupper';
echo $name('tea'), "\n"; // TEA
var_dump(is_callable('abs')); // bool(true)
var_dump(is_callable('shout')); // bool(false)Что здесь происходит:
apply('abs', -5)передаёт строку'abs'. Типcallableеё пропускает, потому что это имя существующей функции, и вызов$f($x)внутриapplyпревращается вabs(-5).$name('tea')вызывает функцию, имя которой лежит в переменной, то естьstrtoupper('tea').is_callableпроверяет, можно ли вызвать значение, и возвращаетtrueилиfalse. Функцииshoutв программе нет, поэтому вторая проверка печатаетbool(false).
Строку с именем удобно передавать, когда нужная функция уже есть: не придётся писать анонимную, которая только вызывает её.
Частая ошибка
После } анонимной функции забыли точку с запятой:
<?php
$double = function (int $n): int {
return $n * 2;
} // ✗
echo $double(4), "\n";Parse error: syntax error, unexpected token "echo"PHP читает присваивание дальше и вместо ; встречает echo. Ошибка разбора не даёт запуститься всему файлу: не выполнится ни одна строка.
У функции с именем после } точку с запятой не ставят, поэтому здесь её часто забывают. Исправление — };, как в первом примере.
Главное
- Анонимная функция — функция без имени; её кладут в переменную и вызывают скобками:
$double(4). - В присваивании после
}анонимной функции нужна;. - Параметр
callableпринимает вызываемое значение, и функция запускает его как$f($x). - Строка с именем функции, например
'abs', тоже вызываемое значение: проверить его можно черезis_callable.