Funkcje anonimowe i arrow functions

🎯 Po tej lekcji napiszesz funkcję anonimową z use i arrow function oraz użyjesz ich razem z array_map.

Do tej pory tworzyłeś funkcje ze stałą nazwą. PHP pozwala też tworzyć funkcje bez nazwy — przypisywać je do zmiennych i przekazywać jako argumenty. To otwiera zupełnie nowy sposób myślenia o kodzie.

Zasięg zmiennych — ważna różnica

Zanim przejdziemy do funkcji anonimowych, poznaj kluczową zasadę PHP: funkcje mają własny zasięg zmiennych. Zmienna zdefiniowana poza funkcją nie jest widoczna w jej środku:

<?php
declare(strict_types=1);

$wiadomosc = 'Cześć!';

function wyswietl(): void
{
    echo $wiadomosc; // Uwaga: Undefined variable $wiadomosc
}

wyswietl();

To celowa cecha PHP — chroni przed nieoczekiwanymi powiązaniami między zmiennymi. Jeśli chcesz skorzystać ze zmiennej zewnętrznej, musisz to zrobić jawnie.

Funkcje anonimowe (closures)

Funkcja anonimowa to funkcja bez nazwy — przechowujesz ją w zmiennej:

<?php
declare(strict_types=1);

$powitaj = function(string $imie): string {
    return 'Cześć, ' . $imie . '!';
};

echo $powitaj('Ola');  // wynik: Cześć, Ola!
echo $powitaj('Kuba'); // wynik: Cześć, Kuba!

Funkcja anonimowa działa jak zmienna — możesz ją przekazać do innej funkcji jako argument.

Słowo kluczowe use — importowanie zmiennych

Żeby funkcja anonimowa mogła korzystać ze zmiennej z zewnętrznego zakresu, musisz ją zaimportować przez use:

<?php
declare(strict_types=1);

$prefix = 'Zamówienie nr ';

$formatuj = function(string $numer) use ($prefix): string {
    return $prefix . $numer;
};

echo $formatuj('#001'); // wynik: Zamówienie nr #001
echo $formatuj('#002'); // wynik: Zamówienie nr #002

use ($prefix) kopiuje wartość zmiennej $prefix w momencie definicji funkcji. Późniejsza zmiana $prefix nie wpłynie na closure — chyba że przekażesz referencję przez use (&$prefix).

Arrow functions — fn

PHP 7.4 wprowadził krótszą składnię: arrow function zapis fn:

<?php
declare(strict_types=1);

$pozdrow = fn(string $imie): string => 'Hej, ' . $imie . '!';

echo $pozdrow('Anna'); // wynik: Hej, Anna!

Arrow function różni się od closure dwoma rzeczami:

  1. Krótszy zapis — zamiast function(...) { return ...; } piszesz fn(...) => ...
  2. Automatyczne przechwytywanie zakresu — nie potrzebuje use, widzi zmienne z zewnątrz automatycznie
<?php
declare(strict_types=1);

$prefix = 'Produkt: ';

// Closure wymaga use:
$f1 = function(string $n) use ($prefix): string { return $prefix . $n; };

// Arrow function przechwytuje automatycznie:
$f2 = fn(string $n): string => $prefix . $n;

echo $f1('Jabłka'); // wynik: Produkt: Jabłka
echo $f2('Mleko');  // wynik: Produkt: Mleko
💡 Kiedy co: Arrow function jest idealna dla jednolinijkowych przekształceń, np. w array_map czy array_filter. Closure z use sprawdza się, gdy potrzebujesz wielu linii kodu lub chcesz modyfikować zmienną zewnętrzną przez referencję.

Ciało arrow function to zawsze jedno wyrażenie — nie napiszesz w nim wielu instrukcji ani nie użyjesz klamer {}. Gdy potrzebujesz więcej, użyj zwykłej funkcji anonimowej z use.

Zastosowanie z array_map

Najczęstsze zastosowanie funkcji anonimowych i arrow functions to przekazywanie ich do funkcji wyższego rzędu, takich jak array_map:

<?php
declare(strict_types=1);

$imiona = ['ola', 'kuba', 'anna'];

// Closure:
$wielkie = array_map(function(string $i): string {
    return strtoupper($i);
}, $imiona);

// Arrow function — krócej:
$wielkie = array_map(fn(string $i): string => strtoupper($i), $imiona);

print_r($wielkie);
// wynik:
// Array
// (
//     [0] => OLA
//     [1] => KUBA
//     [2] => ANNA
// )

Przykład z przechwytywaniem zmiennej z zakresu zewnętrznego:

<?php
declare(strict_types=1);

$stawkaVAT = 0.23;
$cenyNetto  = [100, 200, 350];

$cenyBrutto = array_map(fn($c) => round($c * (1 + $stawkaVAT), 2), $cenyNetto);

print_r($cenyBrutto);
// wynik:
// Array
// (
//     [0] => 123
//     [1] => 246
//     [2] => 430.5
// )

Arrow function automatycznie widzi $stawkaVAT bez use — dlatego jest tu idealnym wyborem.

⚠️ Uwaga na modyfikację przez use: use ($x) kopiuje wartość. Żeby zmiana wewnątrz closure wpłynęła na oryginał, użyj use (&$x). Arrow functions zawsze przechwytują przez wartość — nie można przez referencję.

Funkcje w tej lekcji

Sprawdź się

Jak funkcja anonimowa (closure) uzyskuje dostęp do zmiennej z otaczającego zakresu?

Czym arrow function (fn) różni się od funkcji anonimowej pod względem zasięgu?

Co zwraca array_map(fn($x) => $x * 2, [1, 2, 3])?

Ćwiczenie

Masz tablicę imion: ['ola', 'kuba', 'anna']. Użyj array_map z arrow function, żeby zamienić każde imię na wersję z wielką pierwszą literą (użyj ucfirst). Wyświetl wynik przez print_r.

Pokaż rozwiązanie
<?php
declare(strict_types=1);

$imiona = ['ola', 'kuba', 'anna'];
$sformatowane = array_map(fn($i) => ucfirst($i), $imiona);
print_r($sformatowane);
// wynik:
// Array
// (
//     [0] => Ola
//     [1] => Kuba
//     [2] => Anna
// )