Semua fungsi yang telah kita tulis sejauh ini punya nama, dan nama itulah yang dipakai untuk memanggilnya. Namun sering kali logika hanya dibutuhkan sekali: kriteria pengurutan untuk satu daftar, transformasi untuk satu array, atau blok hitung-hitungan yang langsung dijalankan. Untuk kebutuhan semacam itu PHP memperlakukan fungsi sebagai nilai — function yang dibuat tanpa nama, disimpan ke dalam variabel, dikirim sebagai argument, lalu dipanggil kapan pun diperlukan. Pada Chapter ini kita pelajari bentuk dasarnya, fakta bahwa setiap anonymous function sebenarnya adalah object Closure, pola pemakaian sebagai callback, hingga batas scope yang membuka jalan menuju closure.

Function sebagai Nilai #

Membuat anonymous function mirip dengan mendefinisikan fungsi biasa, hanya tanpa nama dan diakhiri titik koma — karena konstruksi ini adalah penugasan nilai, sama seperti $harga = 50000;. Variabel yang menerima fungsi tersebut selanjutnya bisa dipanggil seperti fungsi bernama. Mari kita buktikan juga bahwa nilai yang disimpan bukan sekadar “resep”, melainkan object sungguhan:

php
<?php
$sapa = function (string $nama): string {
    return "Halo, $nama!";
};

echo $sapa("Budi") . "\n";
echo get_class($sapa) . "\n";
var_dump($sapa instanceof Closure);

Output yang Dihasilkan:

text
Halo, Budi!
Closure
bool(true)

Mari kita telaah baris-barisnya:

  1. function (string $nama): string { ... } menghasilkan nilai berupa fungsi; variabel $sapa kini menunjuk ke fungsi tersebut.
  2. Pemanggilan memakai bentuk $sapa("Budi") — tanda kurung setelah variabel berarti “jalankan fungsi yang tersimpan di dalam variabel ini”.
  3. get_class($sapa) mengembalikan Closure, dan pemeriksaan $sapa instanceof Closure menghasilkan bool(true).

Callback Pengurutan untuk usort #

Pemakaian paling khas anonymous function adalah callback — fungsi yang dikirim sebagai argument untuk fungsi lain. Contoh primernya usort(), yang bertugas mengurutkan array tetapi tidak mengetahui aturan urutannya; aturan itu kita pasok lewat callback dengan kontrak: kembalikan angka negatif bila elemen pertama harus berada di depan, positif bila harus di belakang, dan nol bila keduanya setara. Operator spaceship <=> mempermudah kontrak ini karena menghasilkan tepat -1, 0, atau 1:

php
<?php
$produk = [
    ["nama" => "Laptop", "harga" => 8500000],
    ["nama" => "Mouse", "harga" => 150000],
    ["nama" => "Monitor", "harga" => 2200000],
];

usort($produk, function (array $a, array $b): int {
    return $a["harga"] <=> $b["harga"];
});

foreach ($produk as $produkItem) {
    echo $produkItem["nama"] . ": Rp" . $produkItem["harga"] . "\n";
}

Output yang Dihasilkan:

text
Mouse: Rp150000
Monitor: Rp2200000
Laptop: Rp8500000

Mari kita bedah cara kerjanya:

  1. Callback menerima dua elemen $a dan $b — dua baris yang sedang dibandingkan oleh usort() — dengan return type int sesuai kontrak.
  2. $a["harga"] <=> $b["harga"] bernilai -1 bila harga $a lebih kecil, 1 bila lebih besar, dan 0 bila sama.
  3. usort() memanggil callback ini berulang kali hingga seluruh array terurut — hasilnya naik dari Mouse termurah sampai Laptop termahal.

Inilah alasan anonymous function lahir: aturan urut “harga termurah dulu” hanya relevan untuk array ini, jadi tidak ada gunanya memenuhi scope global dengan fungsi bernama bandingkanHarga() yang dipakai satu kali.

Pola IIFE #

Anonymous function juga bisa langsung dijalankan pada saat dibuat — pola yang dikenal sebagai IIFE (Immediately Invoked Function Expression). Caranya: bungkus definisi dengan tanda kurung agar menjadi ekspresi utuh, lalu tempelkan tanda kurung pemanggilan beserta argument di belakangnya. Pola ini berguna membungkus perhitungan sementara agar tidak menetap di scope global:

php
<?php
$totalBelanja = 250000;

$kembalian = (function (int $uangDibayar, int $total): int {
    return $uangDibayar - $total;
})(300000, $totalBelanja);

echo "Kembalian: Rp$kembalian\n";

Output yang Dihasilkan:

text
Kembalian: Rp50000

Mari kita bedah konstruksinya:

  1. (function (int $uangDibayar, int $total): int { ... }) — tanda kurung pembungkus mengubah definisi fungsi menjadi ekspresi yang bisa langsung dipanggil.
  2. (300000, $totalBelanja) pada ekor konstruksi memanggil fungsi tersebut; nilai kembaliannya disimpan ke $kembalian.
  3. Setelah baris ini selesai, fungsi beserta variabel lokalnya hilang — tidak ada sisa di scope global.

array_map dan array_filter dengan Anonymous Function #

Dua fungsi array paling sering berpasangan dengan anonymous function. array_map() menerapkan sebuah transformasi ke setiap elemen dan mengembalikan array hasil transformasi; array_filter() menyimpan hanya elemen yang membuat callback mengembalikan true. Keduanya menerima callback sebagai argument terakhir:

php
<?php
$hargaProduk = [50000, 125000, 80000, 300000];

$hargaDiskon = array_map(function (int $harga): int {
    return (int) round($harga * 0.9);
}, $hargaProduk);

$hargaPremium = array_filter($hargaProduk, function (int $harga): bool {
    return $harga >= 100000;
});

echo "Setelah diskon : " . implode(", ", $hargaDiskon) . "\n";
echo "Harga premium  : " . implode(", ", $hargaPremium) . "\n";

Output yang Dihasilkan:

text
Setelah diskon : 45000, 112500, 72000, 270000
Harga premium  : 125000, 300000

Callback pertama memotong setiap harga sebesar 10% lalu membulatkannya; array_map() menjaga urutan dan jumlah elemen tetap empat. Callback kedua menguji $harga >= 100000, dan hanya dua elemen yang lolos. Satu detail yang sering mengejutkan: array_filter() mempertahankan key asli — bila elemen di tengah tersaring, array hasil memiliki lompatan pada indeksnya dan tidak diindeks ulang. Bila Anda butuh indeks berurutan 0, 1, 2, …, bungkus hasilnya dengan array_values().

Dinding Scope Tanpa Auto-Capture #

Kita tutup dengan batasan yang menentukan arah Chapter berikutnya: tubuh anonymous function punya scope sendiri. Variabel di luar fungsi tidak otomatis terlihat di dalamnya — PHP sengaja menjaga dinding scope ini agar fungsi tidak diam-diam bergantung pada keadaan sekitarnya. Lihat apa yang terjadi bila kita lupa:

php
<?php
$persenDiskon = 0.1;

$hitungDiskon = function (float $harga): float {
    return $harga * $persenDiskon;
};

echo $hitungDiskon(200000) . "\n";

Saat dijalankan, PHP memunculkan Warning: Undefined variable $persenDiskon dari dalam fungsi, lalu baris terakhir mencetak 0 — $persenDiskon yang tak terlihat dianggap bernilai null, dan 200000 * null menghasilkan 0. Tidak ada error fatal; program terus berjalan dengan nilai yang salah, dan diam-diam seperti inilah bug scope biasanya masuk. Solusinya adalah mendaftarkan variabel luar yang dibutuhkan lewat klausa use():

php
<?php
$persenDiskon = 0.1;

$hitungDiskon = function (float $harga) use ($persenDiskon): float {
    return $harga * $persenDiskon;
};

echo $hitungDiskon(200000) . "\n";

Output yang Dihasilkan:

text
20000

Klausa use ($persenDiskon) menyalin nilai variabel luar ke dalam fungsi pada saat fungsi dibuat, sehingga callback kini menghasilkan 20000. Perhatikan bahwa use() tetap tidak membuka seluruh scope global — hanya variabel yang disebut secara eksplisit yang ikut masuk. Mekanisme penangkapan ini adalah jantung dari closure, dan kita bedah tuntas aturan mainnya — by value, by reference, kapan salinan diambil — pada Chapter Closures .

Ringkasan #

Anonymous function menjadikan fungsi sebagai nilai: disimpan dalam variabel, dikirim sebagai callback untuk usort(), array_map(), dan array_filter(), dijalankan lewat pola IIFE, dan selalu terwujud sebagai object Closure. Batasannya pun jelas — tubuhnya memiliki scope sendiri dan tidak melihat variabel luar tanpa klausa use(). Jembatan itulah yang mengangkat anonymous function menjadi closure utuh, topik yang kita bedah pada Chapter berikutnya.

Referensi #