Closure, Callable & Generator
Fungsi anonim yang menangkap variabel (use), Closure::bind, callable dan first-class callable, serta generator (yield) untuk memproses data besar hemat memori.
Fungsi sebagai nilai
Di PHP fungsi bisa disimpan dalam variabel, dikirim sebagai argumen, dan dikembalikan dari fungsi lain. Fungsi tanpa nama ini disebut closure (objek dari class Closure).
$sapa = function (string $nama): string {
return "Halo, $nama!";
};
echo $sapa('Budi'); // Halo, Budi!
Menangkap variabel luar dengan use
Closure biasa tidak bisa melihat variabel di luarnya. Gunakan use untuk membawanya masuk:
$pajak = 0.11;
$hitung = function (int $harga) use ($pajak): float {
return $harga * (1 + $pajak);
};
echo $hitung(100000); // 111000
use ($x)menyalin nilai saat closure dibuat (perubahan di luar tidak ikut).use (&$x)menangkap referensi sehingga perubahan terlihat dua arah.
Arrow function: versi ringkas
$hitung = fn (int $harga): float => $harga * (1 + $pajak); // otomatis menangkap $pajak
Arrow function (fn) hanya berisi satu ekspresi dan menangkap variabel luar secara otomatis (nilainya).
Callable
Tipe callable menerima apa pun yang bisa dipanggil:
| Bentuk | Contoh |
|---|---|
| Closure / arrow function | fn ($x) => $x * 2 |
| Nama fungsi (string) | 'strtoupper' |
| Method objek | [$objek, 'metode'] |
| Method static | ['NamaClass', 'metode'] atau 'NamaClass::metode' |
Objek dengan __invoke | new Pengali(3) |
| First-class callable (PHP 8.1) | strtoupper(...), $obj->metode(...) |
function terapkan(array $data, callable $fn): array
{
$hasil = [];
foreach ($data as $item) {
$hasil[] = $fn($item);
}
return $hasil;
}
print_r(terapkan([1, 2, 3], fn ($x) => $x ** 2)); // [1, 4, 9]
print_r(terapkan(['a', 'b'], strtoupper(...))); // ['A', 'B']
Fungsi yang mengembalikan fungsi
function pengali(int $faktor): Closure
{
return fn (int $x): int => $x * $faktor;
}
$kali5 = pengali(5);
echo $kali5(4); // 20
Teknik ini bisa dipakai untuk membuat filter, validator, atau middleware yang dapat dikonfigurasi.
Masalah: data terlalu besar
Bayangkan memproses berkas 5 juta baris. Jika semuanya dimuat ke array, memori habis. Generator menyelesaikannya: fungsi yang menghasilkan nilai satu per satu dan berhenti sementara di antara nilai.
yield
function angkaGenap(int $maks): Generator
{
for ($i = 0; $i <= $maks; $i += 2) {
yield $i; // serahkan nilai, lalu "tidur" sampai diminta lagi
}
}
foreach (angkaGenap(10) as $n) {
echo $n . ' '; // 0 2 4 6 8 10
}
Fungsi yang berisi yield otomatis mengembalikan objek Generator; kodenya baru berjalan saat di-foreach. Hanya satu nilai ada di memori pada satu waktu, jadi 1 juta atau 1 miliar angka pun tetap hemat.
Contoh nyata: membaca berkas besar
function bacaBaris(string $path): Generator
{
$f = fopen($path, 'r');
try {
while (($baris = fgets($f)) !== false) {
yield rtrim($baris, "\n");
}
} finally {
fclose($f);
}
}
foreach (bacaBaris('log.txt') as $no => $baris) { ... }
Key, yield from, dan send
function pasangan(): Generator
{
yield 'a' => 1; // yield kunci => nilai
yield 'b' => 2;
}
function gabung(): Generator
{
yield 0;
yield from [1, 2, 3]; // delegasikan ke array/generator lain
yield 4;
}
iterator_to_array(gabung(), false); // [0, 1, 2, 3, 4]
Generator juga bisa diurutkan lewat pipeline: satu generator memfilter hasil generator lain, tanpa pernah membuat array penuh.
function ambilGenap(iterable $sumber): Generator
{
foreach ($sumber as $n) {
if ($n % 2 === 0) {
yield $n;
}
}
}
Kapan memakai apa?
| Kebutuhan | Pilih |
|---|---|
| Callback pendek satu ekspresi | Arrow function fn |
| Callback butuh beberapa baris | Closure dengan use |
| Meneruskan fungsi yang sudah ada | First-class callable nama(...) |
| Data besar / tak terbatas | Generator |
Error umum
Undefined variable $xdi dalam closure → lupause ($x)(kecuali arrow function).- Generator hanya bisa dilewati sekali. Meng-
foreach-nya lagi menghasilkanException: Cannot traverse an already closed generator. - Perubahan variabel setelah closure dibuat tidak terlihat →
usemenyalin nilai; pakai&bila perlu referensi.