Data dalam program sering kali “mungkin ada, mungkin tidak”: parameter URL yang tidak selalu dikirim pengguna, kunci konfigurasi yang belum diisi, atau kolom database yang bernilai kosong. Membaca data seperti itu secara langsung memicu peringatan Warning: Undefined variable atau Warning: Undefined array key pada PHP 8. Null coalescing operator (??) menyelesaikan masalah tersebut dalam satu ekspresi ringkas: gunakan nilai yang ada, atau jatuhkan ke nilai cadangan. Pada Chapter ini kita akan mempelajari cara kerja ??, bentuk assignment-nya ??=, serta pola penggunaan yang umum di aplikasi nyata.
Masalah Nilai yang Mungkin Tidak Ada #
Sebelum ada ??, cara baku menangani nilai yang mungkin tidak ada adalah menggabungkan fungsi isset() dengan operator ternary (kondisi ? nilaiJikaBenar : nilaiJikaSalah — operator ini akan kita bedah tuntas pada Chapter Ternary Operator
). Pola tersebut berfungsi, tetapi membuat kita menulis nama variabel atau kunci array dua kali:
<?php
$setelan = ["tema" => "gelap"];
$bahasa = isset($setelan["bahasa"]) ? $setelan["bahasa"] : "id";
echo "Bahasa: $bahasa\n";Output yang Dihasilkan:
Bahasa: idFungsi isset($setelan["bahasa"]) memeriksa apakah kunci "bahasa" ada dan nilainya bukan null. Karena kunci tersebut tidak ada, ekspresi ternary menghasilkan nilai cadangan "id". Pola ini benar, tetapi boros — kita menyebut $setelan["bahasa"] dua kali dan pembaca harus memindai seluruh baris untuk menemukan nilai cadangannya.
Operator ?? untuk Nilai Cadangan #
Operator ?? (dibaca: “null coalescing”) memadatkan pola di atas menjadi satu ekspresi. Operand kiri adalah nilai utama; operand kanan adalah nilai cadangan yang dipakai hanya jika operand kiri tidak ada atau bernilai null:
<?php
$setelan = ["tema" => "gelap"];
$tema = $setelan["tema"] ?? "terang";
$bahasa = $setelan["bahasa"] ?? "id";
echo "Tema: $tema\n";
echo "Bahasa: $bahasa\n";Output yang Dihasilkan:
Tema: gelap
Bahasa: idMari kita telaah baris-baris pentingnya:
- Baris 4 (
$tema = ...): kunci"tema"ada dan nilainya"gelap"(bukannull), sehingga$temadiisi"gelap"; nilai cadangan"terang"diabaikan. - Baris 5 (
$bahasa = ...): kunci"bahasa"tidak ada di array$setelan, sehingga$bahasadiisi nilai cadangan"id"— tanpa peringatan apa pun.
Perilaku “tanpa peringatan” ini setara dengan isset(): operator ?? memeriksa keberadaan terlebih dahulu sebelum membaca nilai, sehingga variabel yang belum pernah didefinisikan pun aman dipakai:
<?php
$namaPengguna = $tidakPernahDidefinisikan ?? "Tamu";
echo "Halo, $namaPengguna\n";Output yang Dihasilkan:
Halo, TamuPerlu digarisbawahi: variabel $tidakPernahDidefinisikan memang tidak ada, namun baris tersebut tidak mencetak Warning: Undefined variable. Inilah alasan utama ?? menjadi cara idiomatik PHP modern untuk menangani data opsional.
Hanya null yang Digantikan #
Operator ?? mengganti nilai hanya dalam dua keadaan: nilai tidak ada, atau nilainya null. Nilai lain — sekalipun dianggap false oleh PHP — tetap dipakai apa adanya. Perhatikan contoh berikut:
<?php
$catatan = null;
$stok = 0;
$kodeKupon = "0";
echo ($catatan ?? "belum ada catatan") . "\n";
echo ($stok ?? 100) . "\n";
echo ($kodeKupon ?? "tanpa kupon") . "\n";Output yang Dihasilkan:
belum ada catatan
0
0Nilai null pada $catatan digantikan nilai cadangan. Sebaliknya, $stok bernilai 0 dan $kodeKupon bernilai "0" — keduanya adalah nilai falsy (akan dibahas tuntas pada Chapter Truthiness dan Short-Circuit
) — namun keduanya bukan null, sehingga keduanya lolos tanpa diganti. Perilaku ini disengaja: angka nol dan string "0" adalah data valid yang tidak boleh hilang diam-diam.
Merangkai Beberapa Nilai Cadangan #
Operand ?? boleh dirangkai menjadi rantai: PHP mengevaluasi dari kiri ke kanan dan mengambil nilai pertama yang ada dan bukan null. Nilai di sebelah kanannya tidak dievaluasi sama sekali:
<?php
$variabelLingkungan = null;
$berkasSetelan = null;
$sumberData = $berkasSetelan ?? $variabelLingkungan ?? "nilai bawaan";
echo "Sumber data: $sumberData\n";Output yang Dihasilkan:
Sumber data: nilai bawaanSifat malas (lazy) rantai ?? bisa dibuktikan dengan sebuah fungsi. Contoh berikut mensimulasikan pembacaan data mahal: selama cache berisi data, fungsi pembaca tidak pernah dipanggil:
<?php
function bacaDariServer(): ?string {
echo "(menghubungi server...)\n";
return "data dari server";
}
$cache = "data dari cache";
$data = $cache ?? bacaDariServer();
echo $data . "\n";
$cache = null;
$data = $cache ?? bacaDariServer();
echo $data . "\n";Output yang Dihasilkan:
data dari cache
(menghubungi server...)
data dari serverPada penugasan pertama, $cache berisi string sehingga bacaDariServer() tidak pernah dieksekusi — tidak ada baris “(menghubungi server…)” sebelum “data dari cache”. Setelah $cache diubah menjadi null, giliran fungsi tersebut dijalankan. (Fungsi akan dibahas tuntas pada Section Fungsi; untuk sekarang cukup pahami bahwa bacaDariServer() adalah kode yang baru dieksekusi saat dipanggil.)
Operator Assignment ??= #
Sering kali kita ingin aturannya terbalik: variabel tetap mempertahankan nilai lamanya, dan nilai bawaan hanya diisi bila variabel tersebut kosong. PHP 7.4 menyediakan ??= untuk itu:
<?php
$setelan = ["timeout" => 30];
$setelan["timeout"] ??= 60;
$setelan["retries"] ??= 3;
print_r($setelan);
$catatan = null;
$catatan ??= "Belum ada catatan";
echo $catatan . "\n";Output yang Dihasilkan:
Array
(
[timeout] => 30
[retries] => 3
)
Belum ada catatanKunci "timeout" sudah bernilai 30, sehingga ??= 60 tidak mengubahnya. Kunci "retries" belum ada, sehingga dibuat dan diisi 3 — ??= pada elemen array yang belum ada tidak memicu peringatan, sama seperti ??. Variabel $catatan yang semula null kemudian terisi string cadangan. Ekspresi $x ??= $y juga mengembalikan nilai akhirnya, sehingga bisa langsung dipakai dalam ekspresi lain.
Pola Umum dalam Praktik #
Penggunaan paling sering dari ?? adalah membaca input dari superglobal seperti $_GET tanpa harus memeriksa keberadaannya. Contoh berikut berjalan di CLI, tempat $_GET berupa array kosong, sehingga kedua parameter jatuh ke nilai bawaan:
<?php
$halaman = (int) ($_GET["halaman"] ?? 1);
$aksi = $_GET["aksi"] ?? "daftar";
echo "Aksi: $aksi, halaman: $halaman\n";Output yang Dihasilkan:
Aksi: daftar, halaman: 1Pola yang sama berlaku untuk konfigurasi aplikasi: mulai dari nilai bawaan, lalu biarkan berkas setelan atau variabel lingkungan menimpa sebagian saja. Berkas contoh.php pada bagian bawah halaman merangkum pola-pola tersebut dalam satu program utuh yang bisa Anda jalankan langsung.
Ringkasan #
Operator ?? mengambil nilai pertama yang ada dan bukan null dari rangkaian operand, tanpa pernah memicu peringatan untuk variabel atau kunci array yang tidak ada. Bentuk ??= mengisi nilai bawaan hanya bila target kosong. Berbeda dengan ?:, operator ini tidak menyentuh nilai falsy seperti 0 atau "" — ia hanya peduli null. Selanjutnya kita akan bertemu sepupu dekatnya: nullsafe operator ?-> yang menerapkan ide serupa pada akses method dan properti objek.
Referensi #
- Dokumentasi operator perbandingan (termasuk ??) — php.net
- Fungsi isset() — php.net
- Fitur baru PHP 7.0 (null coalescing operator) — php.net
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh.php
Unduh berkas<?php
// contoh.php — rangkuman Null Coalescing Operator
// Jalankan dengan: php contoh.php
// 1. Nilai cadangan untuk kunci array yang mungkin tidak ada
$setelanPengguna = [
"tema" => "gelap",
"notifikasi" => null,
];
$tema = $setelanPengguna["tema"] ?? "terang";
$notifikasi = $setelanPengguna["notifikasi"] ?? "aktif";
$bahasa = $setelanPengguna["bahasa"] ?? "id";
echo "Tema : $tema\n";
echo "Notifikasi : $notifikasi\n";
echo "Bahasa : $bahasa\n";
// 2. Hanya null yang diganti; 0 dan "0" tetap dipakai
$stok = 0;
$kodeKupon = "0";
echo "Stok : " . ($stok ?? 100) . "\n";
echo "Kode kupon : " . ($kodeKupon ?? "tanpa kupon") . "\n";
// 3. Rantai nilai cadangan: ambil yang pertama tersedia
$variabelLingkungan = null;
$berkasSetelan = null;
$sumber = $berkasSetelan ?? $variabelLingkungan ?? "nilai bawaan";
echo "Sumber data : $sumber\n";
// 4. ??= mengisi hanya bila target kosong
$setelanPengguna["timeout"] ??= 60;
$setelanPengguna["retries"] ??= 3;
echo "Timeout : {$setelanPengguna['timeout']} detik\n";
echo "Retries : {$setelanPengguna['retries']}\n";
// 5. Input request dengan nilai bawaan
$halaman = (int) ($_GET["halaman"] ?? 1);
echo "Halaman : $halaman\n";