Лаконичные замыкания в коде PHP

Раздел: PHP -> Анонимные и стрелочные функции

Стрелочные функции в PHP: обзор и применение

Стрелочные функции (arrow functions) появились в PHP 7.4 как лаконичная альтернатива анонимным функциям. Их синтаксис fn(аргументы) => выражение позволяет сократить код, автоматически захватывая переменные из внешней области видимости по значению. Они подходят для простых колбэков, когда требуется одно выражение.

Основное решение для создания быстрого колбэка в одну строку:

$multiply = fn($a, $b) => $a * $b;
echo $multiply(3, 4); // 12

анонимная функция php (анонимные функции в php)

Такая функция автоматически захватывает все переменные, которые упоминаются внутри неё, без явного use. Это уменьшает объём кода по сравнению с классической анонимной функцией.

Альтернативный вариант с анонимной функцией и ключевым словом use:

$factor = 2;
$double = function($x) use ($factor) {
    return $x * $factor;
};
echo $double(5); // 10

стрелочные функции php (стрелочные функции в php)

Этот вариант требует больше кода, но допускает захват по ссылке и множественные выражения.

Как создать простую стрелочную функцию?

Стрелочная функция объявляется с ключевым словом fn, после которого в круглых скобках перечисляются параметры, затем символ => и единственное выражение. Выражение автоматически возвращается.

$greet = fn($name) => 'Привет, ' . $name;
echo $greet('Мир'); // Привет, Мир

Проблема: стрелочная функция не может содержать несколько инструкций. Если нужно выполнить более одного действия (например, запись в лог и возврат значения), следует использовать анонимную функцию с фигурными скобками.

Ошибка: попытка написать fn($x) => { $y = $x * 2; return $y; } вызовет синтаксическую ошибку. Единственное выражение не может быть блоком.

Как захватить внешние переменные без use?

Стрелочные функции автоматически захватывают переменные, которые используются внутри них. Достаточно просто обратиться к переменной по имени.

$discount = 0.9;
$applyDiscount = fn($price) => $price * $discount;
echo $applyDiscount(100); // 90

Проблема: если переменная не определена в момент вызова функции, возникнет ошибка Undefined variable. Захват происходит по значению, т.е. изменения переменной после создания функции не влияют на результат.

Ограничение: захват по ссылке невозможен. Для передачи по ссылке используйте анонимную функцию с use (&$var).

Как использовать стрелочные функции с array_map?

Классический сценарий: преобразование всех элементов массива. Стрелочная функция делает запись очень компактной.

$numbers = [1, 2, 3, 4];
$squared = array_map(fn($n) => $n * $n, $numbers);
print_r($squared); // [1, 4, 9, 16]

Аналог с анонимной функцией:

$squared = array_map(function($n) { return $n * $n; }, $numbers);

Проблема: если требуется выполнить дополнительные действия (например, логирование), придётся перейти на анонимную функцию.

Как фильтровать массивы с помощью стрелочных функций?

Функция array_filter принимает колбэк, который возвращает true для элементов, которые нужно оставить. Стрелочная функция идеально подходит для простых условий.

$nums = [5, 12, 8, 20];
$greaterThanTen = array_filter($nums, fn($v) => $v > 10);
print_r($greaterThanTen); // [12, 20]

Ошибка: если в стрелочной функции используется сложное логическое выражение с побочными эффектами, код становится трудночитаемым. В таких случаях лучше использовать анонимную функцию с явным return.

Как передать стрелочную функцию в качестве callback для сортировки?

Для пользовательской сортировки (usort, uasort) часто требуется краткое сравнение.

$fruits = ['apple', 'Banana', 'cherry'];
usort($fruits, fn($a, $b) => strcmp(strtolower($a), strtolower($b)));
print_r($fruits); // case-insensitive сортировка

Проблема: внутри стрелочной функции нельзя использовать ссылки на внешние объекты (например, $this), если функция определена в глобальной области. Однако если функция определена внутри метода класса, $this доступен автоматически.

Как использовать типизацию параметров и возвращаемого значения?

Начиная с PHP 8.0 стрелочные функции поддерживают объявление типов параметров и возвращаемого типа, что повышает надёжность кода.

$toUpper = fn(string $text): string => strtoupper($text);
echo $toUpper('hello'); // HELLO

Ошибка: передача значения неверного типа вызовет исключение TypeError. Строгая типизация может быть включена директивой declare(strict_types=1);.

Как получить доступ к внешним переменным по ссылке?

Стрелочные функции не позволяют захватывать переменные по ссылке. Если нужно модифицировать переменную из внешнего контекста, используйте анонимную функцию с use (&$var).

$counter = 0;
$increment = function() use (&$counter) {
    $counter++;
};
$increment();
echo $counter; // 1

Ошибка: попытка использовать fn() => $counter++ не изменит внешнюю переменную, так как захват происходит по значению. Результат будет равен старому значению $counter, а сама переменная не изменится.

Какие ещё ограничения существуют?

  • Нельзя использовать ключевое слово yield для генерации значений (генераторы).
  • Нельзя объявить рекурсивную стрелочную функцию, так как у неё нет имени.
  • Не поддерживается передача аргументов по ссылке в параметрах (параметры всегда передаются по значению).
  • Внутри стрелочной функции нельзя использовать parent:: или self:: (если она определена не в контексте класса).

Расширенные примеры использования стрелочных функций

Пример 1. Комбинация array_reduce для вычисления суммы элементов:

Пример
$items = [10, 20, 30];
$sum = array_reduce($items, fn($carry, $item) => $carry + $item, 0);
echo $sum; // 60

Пример 2. Возврат стрелочной функции из другой функции (замыкание):

Пример
function createMultiplier($factor) {
    return fn($x) => $x * $factor;
}
$double = createMultiplier(2);
echo $double(7); // 14

Пример 3. Использование стрелочной функции внутри метода класса с доступом к $this:

Пример
class ProductFilter {
    private array $items;

    public function __construct(array $items) {
        $this->items = $items;
    }

    public function getValidItems(): array {
        return array_filter($this->items, fn($item) => $item->isValid());
    }
}

Пример 4. Применение match внутри стрелочной функции:

Пример
$describe = fn($status) => match($status) {
    'active' => 'Включён',
    'inactive' => 'Отключён',
    default => 'Неизвестно'
};
echo $describe('active'); // Включён

Пример 5. Передача стрелочной функции в качестве callback для выборочного преобразования:

Пример
$numbers = [1, -2, 3, -4];
$squaredPositive = array_map(
    fn($v) => $v * $v,
    array_filter($numbers, fn($v) => $v > 0)
);
print_r($squaredPositive); // [1, 9]

Пример 6. Использование статической стрелочной функции (статическое замыкание не захватывает $this):

Пример
$staticArrow = static fn($x) => $x + 1;
echo $staticArrow(10); // 11

Пример 7. Стрелочная функция с variadic-параметром (PHP 8.1+):

Пример
$sumAll = fn(...$numbers) => array_sum($numbers);
echo $sumAll(1, 2, 3, 4); // 10

Пример 8. Использование Closure::call для привязки контекста:

Пример
class Box {
    public int $value = 100;
}
$box = new Box();
$closure = fn($x) => $this->value + $x;
$result = $closure->call($box, 50);
echo $result; // 150

Пример 9. Сравнение производительности стрелочных и анонимных функций (стрелочные функции обычно быстрее из-за отсутствия дополнительной области видимости):

Пример
$arr = range(1, 100000);
$start = microtime(true);
$result = array_map(fn($v) => $v * 2, $arr);
echo microtime(true) - $start; // меньше времени

Пример 10. Ошибка при попытке использовать yield:

Пример
$gen = fn() => yield 1; // Parse error: syntax error
Parse error: syntax error, unexpected 'yield' (T_YIELD) ...

Стрелочные функции в PHP - comments

En
стрелочные функции php (php)