<? phpukraine СПІВБЕСІДИ
Пошук по платформі
CORE PHP · JUNIOR ЧАСТО ПИТАЮТЬ

Як працюють замикання, use і стрілочні функції?

Замикання — це обʼєкт класу Closure; `function` захоплює зовнішні змінні лише явно через `use` (за значенням або за посиланням через `&`), а стрілочна функція `fn` (PHP 7.4+) захоплює їх автоматично й завжди за значенням.

Чому змінна, яку я змінив після оголошення closure, усередині нього стара?
Чим `fn` відрізняється від `function` — це просто коротший запис?
Що таке `use (&$total)` і навіщо там амперсанд?
Чому в замиканні всередині класу працює `$this`, хоча ви його нікуди не передавали?
closure use arrow function PHP 7.4 callable $this

Замикання в PHP — це не окрема мовна конструкція «функція всередині функції», а звичайний обʼєкт класу Closure. Тому його можна покласти в змінну, передати аргументом, повернути з методу, зберегти у властивості й викликати як $fn(...). Ключова властивість — воно памʼятає частину контексту, у якому було оголошене. Але «памʼятає» не автоматично: анонімна функція, оголошена через function, бачить лише свої параметри, глобальні суперглобали й те, що ви явно перелічили в use. Це принципова відмінність від JavaScript, де замикання затягує всю зовнішню область видимості; у PHP список захоплення пишеться руками.

Найважливіший момент, на якому валяться на співбесідах: use ($x) спрацьовує в момент оголошення замикання, а не в момент виклику. PHP копіює поточне значення $x у внутрішнє сховище обʼєкта Closure, і подальші зміни зовнішньої змінної на нього не впливають — саме це показує перший блок коду. Якщо потрібен звʼязок в обидва боки, пишуть use (&$x): тоді замикання і зовнішній код працюють з однією змінною, і присвоєння всередині видно назовні. Окремо варто розрізняти семантику типів: копіювання змінної, що тримає обʼєкт, копіює лише ідентифікатор, тому зміни властивостей обʼєкта видно й без &; масив же копіюється по-справжньому, і зовнішнє додавання елемента в замикання не потрапить.

Стрілочні функції зʼявилися в PHP 7.4 і закривають найчастіший випадок — коротке перетворення в array_map, usort, колекціях Laravel. Синтаксис fn (аргументи) => вираження має три жорсткі властивості. По-перше, захоплення неявне: усі зовнішні змінні, згадані в тілі, захоплюються автоматично, use для fn не існує і є синтаксичною помилкою. По-друге, захоплення завжди за значенням — накопичувач на fn не побудувати, для цього лишається function () use (&$total). По-третє, тіло — рівно одне вираження, яке автоматично і повертається: ні {}, ні return, ні кількох інструкцій там бути не може. Типи параметрів і повернення при цьому дозволені (fn (int $n): int => ...), і зловживати їх пропуском не варто.

Окрема тема — $this. Замикання, оголошене в методі, автоматично привʼязує поточний обʼєкт і область видимості класу, тому всередині доступні навіть private-властивості — це видно на $withTitle у прикладі. Слово static перед function або fn цю привʼязку знімає: $this стає недоступним, а спроба привʼязати обʼєкт через bindTo() дасть попередження і null. Практичний мотив писати static — час життя: замикання зі звʼязаним $this утримує обʼєкт у памʼяті, і для довгоживучих callback-ів (реєстрація в контейнері, слухачі подій, кеш замикань) це зайве утримання. У зворотний бік працюють Closure::bind() / bindTo() / Closure::call(), які підставляють інший $this і scope; на цьому побудовані макроси Laravel.

Межі варто назвати чесно. Замикання не бачить змінних зовнішньої функції без use, і global тут не заміна — це інший механізм із іншими проблемами. fn не є «кращим function»: щойно потрібні дві інструкції, try/catch або посилання, повертаються до повної форми. І в обох випадках залишається питання читабельності списку захоплення: якщо в use більше двох-трьох змінних, це майже завжди сигнал, що замість замикання просився окремий метод або невеликий клас із __invoke().

$multiplier = 3;

// use копіює ЗНАЧЕННЯ в момент оголошення замикання
$byValue = function (int $n) use ($multiplier): int {
    return $n * $multiplier;
};

// fn (PHP 7.4+) захоплює автоматично і теж лише за значенням
$arrow = fn (int $n): int => $n * $multiplier;

$multiplier = 10;                 // змінюємо ПІСЛЯ оголошення
echo $byValue(2);                 // 6  — захоплено ще трійку
echo $arrow(2);                   // 6  — так само

// use (&$x): замикання і зовнішній код ділять одну змінну
$total = 0;
$accumulate = function (int $price) use (&$total): void {
    $total += $price;             // пишемо назовні
};
$accumulate(100);
$accumulate(50);
echo $total;                      // 150

final class Report
{
    private string $title = 'Звіт';

    /** @return array<int, string> */
    public function render(array $rows): array
    {
        // звичайне замикання само привʼязує $this — приватне поле доступне
        $withTitle = fn (string $row): string => "{$this->title}: {$row}";

        // static прибирає привʼязку: $this всередині недоступний
        $trim = static fn (string $row): string => trim($row);

        return array_map($withTitle, array_map($trim, $rows));
    }
}

$counter = Closure::bind(
    function (): string { return $this->title; },  // $this підставимо ззовні
    new Report(),
    Report::class                 // scope дає доступ до private
);
echo $counter();                  // 'Звіт'
Що замикання — це не «просто функція», а обʼєкт класу `Closure`: його можна покласти в змінну, передати аргументом, повернути з методу і викликати через `$fn()` або `$fn->call()`.
Що `use ($x)` копіює значення в момент оголошення замикання, а не в момент виклику — тому подальші зміни `$x` зовні на замикання не впливають.
Що `use (&$x)` захоплює за посиланням: замикання і зовнішній код працюють з однією змінною, і саме так накопичують суму або збирають результат.
Що обʼєкт, захоплений за значенням, усе одно лишається тим самим обʼєктом — копіюється змінна, а не те, на що вона вказує.
Що `fn` (PHP 7.4+) — це одне-єдине вираження, без `{}`, без `return`, з автоматичним захопленням по значенню, і посилання в ньому неможливе взагалі.
Що звичайне замикання, оголошене в методі, автоматично привʼязує `$this`, а `static function () {}` / `static fn () =>` цю привʼязку прибирають.
Казати, що `use` «передає змінну в функцію»: параметри передають при виклику, `use` захоплює при оголошенні — між цими моментами може пройти будь-який час.
Очікувати, що `$sum` зміниться після `$fn()`, коли написано `use ($sum)` без `&` — зовнішня змінна лишиться незмінною.
Ставити `&` «про всяк випадок» у циклі: усі замикання почнуть ділити одну змінну й після циклу віддаватимуть її останнє значення.
Думати, що `fn` захоплює за посиланням або що в нього можна дописати `use` — синтаксис `fn ($x) use ($y) => ...` є помилкою парсингу.
Писати в `fn` кілька інструкцій через `;` — стрілочна функція має рівно одне вираження, яке і є результатом.
Дивуватись «Using $this when not in object context» у замиканні, яке оголосили через `static function`, або тому, що `$this` витік у замикання, збережене в контейнері й тримає обʼєкт від збирання.
ПОРАДА

Одна фраза, яка закриває питання: «`function () use ($x)` — явне захоплення, за значенням, або за посиланням через `&`; `fn () => $x` — неявне захоплення, тільки за значенням, тільки одне вираження». Далі додайте, що `$this` захоплюється в обох, і зняти його можна словом `static`.

оновлено 3 вересня 2026 · ліцензія CC-BY-SA-4.0 Знайшли неточність? Напишіть →
ПЕРЕВІРТЕ СЕБЕ

Стрілочна функція захоплює використані змінні неявно, у момент оголошення й лише за значенням; синтаксис `use` до неї не застосовується взагалі — `fn () use ($x) => ...` не парситься.