В чем разница короткого синтаксиса функций fn и замыканий в PHP
Что проверяют
Знаете ли вы, чем стрелочная функция отличается от обычной анонимной по поведению, а не только по синтаксису. Главное здесь захват переменных. Следом обычно просят показать, что будет, если внутри fn изменить внешнюю переменную.
Как отвечать
Начните с того, что обе конструкции создают объект класса Closure: fn это не отдельный тип, а сокращённая запись замыкания, появившаяся в PHP 7.4. Дальше различия:
- захват:
fnсама захватывает все внешние переменные, упомянутые в выражении, и только по значению. Обычному замыканию их нужно перечислить вuse, зато там можно взять ссылку:use (&$total); - момент захвата: и в
fn, и вuseпо значению переменная фиксируется при создании функции, а не при вызове; - тело: у
fnровно одно выражение, его результат возвращается автоматически. Несколько операторов, циклы иreturnвнутри не пишутся. С PHP 8.0throwстал выражением, поэтомуfn() => throw new LogicException()допустимо, как иmatch; $this: внутри класса обе формы привязывают$thisавтоматически. Если он не нужен, пишутstatic fnилиstatic function, чтобы замыкание не держало ссылку на объект;- вложенность: вложенная
fnвидит переменные внешней без дополнительных объявлений.
Практическое правило: fn для коротких колбэков в array_map, array_filter, usort и в цепочках коллекций Laravel, обычное замыкание для логики в несколько строк или когда нужно менять внешнее состояние.
Где спотыкаются
Пишут fn() => $count++ и ждут, что снаружи $count вырастет. Этого не будет: меняется копия внутри функции, внешняя переменная остаётся прежней.
Говорят, что замыкание «умеет захватывать переменные, а fn нет». На деле fn захватывает автоматически, а явное объявление нужно как раз обычному замыканию.
Путают захват по значению с копированием объекта. Если захвачен объект, копируется его идентификатор, и методы, вызванные внутри fn, меняют тот же самый объект.