Перейти к содержимому
Теория · Функции как значения · урок 5 из 8

Стрелочные функции и strlen(...)

До сих пор замыкание было карточкой рецепта, на которую цены записывают через use. Но бывают рецепты в одну строку, например «умножить на курс». Для них не заводят целую карточку — хватает стикера.

Стикер маленький: на нём помещается одна строка. Зато цены, которые в этой строке упомянуты, стикер переписывает сам, и перечислять их отдельно не нужно. Такой стикер в PHP — стрелочная функция fn.

Сравним анонимную и стрелочную функции, которые делают одно и то же:

PHP
<?php

$rate = 2;
$long = function ($n) use ($rate) {
    return $n * $rate;
};
$short = fn($n) => $n * $rate;
echo $long(3), ' ', $short(3), "\n"; // 6 6

Давайте разберём по шагам:

  1. $long — анонимная функция со списком use, фигурными скобками и return.
  2. $short делает то же одной строкой. Списка use в ней нет, а $rate она всё равно видит.
  3. Оба вызова возвращают 6, и echo печатает 6 6.

Запись fn

Стрелочная функция — короткая анонимная функция, тело которой состоит из одного выражения. Разберём запись fn($n) => $n * $rate по частям:

  • fn — ключевое слово; в стиле PER Coding Style скобки с параметрами ставят сразу после него, без пробела;
  • ($n) — параметры, как у любой функции; типы тоже можно указать: fn(int $n): int => $n * 2;
  • => отделяет параметры от тела;
  • $n * $rate — тело, одно выражение; его значение функция возвращает сама, поэтому return не пишут.

Стрелочная функция — тоже объект Closure. Её кладут в переменную, передают в параметр callable и вызывают скобками.

Захват без use

Стрелочная функция сама захватывает внешние переменные, которые упоминает её выражение. Захват идёт по значению и в момент создания, как у use ($rate):

PHP
$rate = 10;
echo $short(3), "\n"; // 6
$count = 0;
$add = fn() => ++$count;
echo $add(), ' ', $add(), ' ', $count, "\n"; // 1 1 0

Давайте посмотрим, что здесь происходит:

  1. $rate = 10; меняет внешнюю переменную, но $short захватил копию 2 и по-прежнему возвращает 6.
  2. $add увеличивает $count, но только свою копию. Каждый вызов начинает с 0 и возвращает 1.
  3. Внешняя $count так и остаётся 0.

Здесь картинка со стикером неточна. Цены стикер переписывает сам, но надпись «смотрите на доске» на нём не помещается: захватить переменную по ссылке fn не может. Для счётчика нужна анонимная функция с use (&$count).

Чем стрелочная функция отличается от анонимной — частый вопрос на собеседовании. Ответ:

  • fn захватывает внешние переменные сама, а function видит только перечисленные в use;
  • fn захватывает только по значению, а function умеет и use (&$x);
  • тело fn — одно выражение без return, а в function можно написать сколько угодно инструкций.

Замыкание из готовой функции

Иногда нужная функция уже есть, например встроенная strlen. С PHP 8.1 из неё можно сделать замыкание записью strlen(...). Три точки в скобках стоят на месте аргументов и означают «не вызывать, а сделать замыкание».

PHP
$len = strlen(...);
$up = strtoupper(...);
echo $len('tea'), ' ', $up('tea'), "\n"; // 3 TEA
echo gettype($len), "\n"; // object

Разберём по шагам:

  1. strlen(...) не вызывает strlen, а возвращает объект Closure, и он ложится в $len.
  2. $len('tea') вызывает strlen('tea') и возвращает 3, а $up('tea') возвращает TEA.
  3. echo gettype($len) печатает object, как и для любой анонимной функции.

Строка 'strlen' тоже вызываемое значение, но у записи strlen(...) есть преимущество. Если ошибиться в имени, PHP остановит программу сразу, на строке с (...). Строку с опечаткой PHP проверит, только когда её вызовут или передадут в параметр callable.

Частая ошибка

В стрелочную функцию пытаются записать тело в фигурных скобках:

PHP
<?php

$double = fn($n) => { // ✗
    return $n * 2;
};
echo $double(7), "\n";
TEXT
Parse error: syntax error, unexpected token "{"

После => PHP ждёт одно выражение, а инструкции в фигурных скобках выражением не бывают. Из-за ошибки разбора файл не запускается совсем. Исправление — fn($n) => $n * 2, а для тела из нескольких инструкций — анонимная функция с function.

Главное

  • fn($n) => выражение — стрелочная функция: одно выражение, значение которого она возвращает без return.
  • fn сама захватывает внешние переменные из выражения — по значению, в момент создания.
  • Захватить переменную по ссылке fn не может: для этого нужна function с use (&$x).
  • strlen(...) (PHP 8.1) делает замыкание из готовой функции, не вызывая её.