Sepanjang section ini kita beberapa kali menulis 'strtoupper' sebagai string untuk diteruskan ke array_map(). Bentuk itu bekerja, tetapi string bukanlah fungsi: salah tik satu huruf dan PHP baru menjerit saat runtime, tepat di depan pengguna. PHP 8.1 memperkenalkan first-class callable syntax — tulis nama fungsi diikuti tanda kurung bertitik tiga seperti strlen(...) — dan hasilnya adalah objek Closure siap pakai yang mewakili fungsi tersebut. Mekanik lengkap berbagai bentuk callable beserta sejarahnya sudah dibahas pada Chapter First-Class Callables
di Section Fungsi; chapter ini memotret dari sudut paradigma: fungsi bernama sebagai nilai yang bisa dioper, disimpan, dan disusun.
Sintaks Titik Tiga #
Bentuk paling dasarnya mengubah fungsi bernama apa pun menjadi nilai:
<?php
$hitungPanjang = strlen(...); // first-class callable syntax
echo $hitungPanjang('Belajar PHP') . "\n";
echo strlen('Belajar PHP') . "\n";
var_dump($hitungPanjang instanceof Closure);Output yang Dihasilkan:
11
11
bool(true)Mari kita telaah tiga baris hasilnya:
strlen(...)membentukClosureyang merujuk fungsistrlen; tanda(...)di sini bukan spread operator, melainkan penanda “buatkan callable untuk fungsi ini”.$hitungPanjang('Belajar PHP')menghasilkan11, persis seperti memanggilstrlen()langsung — tidak ada perilaku baru yang ditambahkan maupun dikurangi.instanceof Closurebernilaitruemenegaskan hasilnya adalah objekClosuresungguhan, sejajar dengan anonymous function dan arrow function yang sudah kita kenal.
Bedanya dengan bentuk string terletak di alat di sekitar kode: IDE mengenali strlen(...) sebagai rujukan fungsi sehingga rename otomatis dan “go to definition” bekerja, dan static analysis seperti PHPStan dapat memverifikasi keberadaan fungsinya tanpa menjalankan program.
Fungsi Bawaan sebagai Argumen #
Kekuatan sesungguhnya muncul saat first-class callable bertemu higher-order function. Kita bisa mengoper fungsi bawaan PHP ke array_map() tanpa membungkusnya dalam closure terlebih dahulu:
<?php
$namaPengguna = ['budi santoso', 'siti rahayu'];
// Bentuk lama: nama fungsi sebagai string.
$versiString = array_map('strtoupper', $namaPengguna);
// Bentuk modern: fungsi sebagai ekspresi yang dikenali alat analisis.
$versiCallable = array_map(strtoupper(...), $namaPengguna);
echo implode('; ', $versiString) . "\n";
echo implode('; ', $versiCallable) . "\n";Output yang Dihasilkan:
BUDI SANTOSO; SITI RAHAYU
BUDI SANTOSO; SITI RAHAYUKedua baris menghasilkan keluaran yang identik — bentuk string tetap valid dan tidak akan dihapus. Yang berubah adalah jaminan: pada strtoupper(...), kesalahan ketik langsung terdeteksi sebagai error yang jelas saat baris itu dievaluasi, bukan Warning: array_map(): Expected parameter 1 to be a valid callback yang samar. Pola ini juga berlaku untuk fungsi kita sendiri, misalnya array_map(hitungDiskon(...), $daftarHarga) ketika tanda tangannya cocok.
Method sebagai Callable #
Sintaks ini tidak berhenti di fungsi lepas — method static, method instance, bahkan method pada $this semuanya bisa diangkat menjadi callable:
<?php
final class PengurutProduk
{
public static function bandingkanHarga(array $a, array $b): int
{
return $a['harga'] <=> $b['harga'];
}
}
$produk = [
['nama' => 'Kopi Gayo', 'harga' => 85000],
['nama' => 'Gula Aren', 'harga' => 25000],
['nama' => 'Teh Melati', 'harga' => 45000],
];
usort($produk, PengurutProduk::bandingkanHarga(...));
foreach ($produk as $p) {
echo "{$p['nama']}: Rp{$p['harga']}\n";
}Output yang Dihasilkan:
Gula Aren: Rp25000
Teh Melati: Rp45000
Kopi Gayo: Rp85000Ekspresi PengurutProduk::bandingkanHarga(...) menghasilkan closure untuk method static tersebut, lalu usort() memakainya sebagai aturan perbandingan — hasilnya produk terurut dari harga termurah. Aturan pembandingnya kini punya rumah yang jelas: sebuah class yang bisa disebut namanya, diuji terpisah, dan dipakai ulang. Bentuk instance bekerja serupa:
<?php
final class PembersihEmail
{
public function bersihkan(string $email): string
{
return strtolower(trim($email));
}
}
$pembersih = new PembersihEmail();
$bersihkan = $pembersih->bersihkan(...); // method instance sebagai callable
$daftarEmail = [' [email protected] ', '[email protected] '];
$emailBersih = array_map($bersihkan, $daftarEmail);
// Gabungkan dengan closure untuk tahap berikutnya: ambil domainnya.
$domain = array_map(
fn (string $email): string => substr(strstr($email, '@'), 1),
$emailBersih
);
echo implode('; ', $emailBersih) . "\n";
echo implode('; ', $domain) . "\n";Output yang Dihasilkan:
[email protected]; [email protected]
example.com; example.comBaris $pembersih->bersihkan(...) mengikat method ke objeknya — closure hasilnya “ingat” $pembersih sehingga $bersihkan(' [email protected] ') menjalankan method pada instance yang benar. Dua tahap berikutnya memperlihatkan komposisi dalam praktik: hasil $emailBersih menjadi masukan array_map() kedua yang mengupas domainnya dengan closure biasa. First-class callable, closure anonim, dan higher-order function bercampur bebas dalam satu pipeline — dan tepat di titik itulah kita siap membahas cara merangkainya secara sistematis.
Ringkasan #
Sintaks strlen(...) mengangkat fungsi bernama — fungsi global, method static, method instance — menjadi objek Closure yang bisa dioper seperti data biasa. Dibanding bentuk string, first-class callable lebih awal tertangkap saat salah tik, lebih ramah IDE, dan dikenali static analysis. Keterbatasannya: argumen tidak bisa dibekukan lewat sintaks ini; untuk itu partial application dengan closure tetap jawabannya. Dengan fungsi murni, closure, dan callable bernama di tangan, bahan komposisi sudah lengkap — Chapter Function Composition
menyusunnya menjadi pipeline utuh.