Лаконичные замыкания в коде 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) ...