Ada bug yang bikin kamu curiga sama logika sendiri sampai berjam-jam, padahal logikanya benar dari awal. Yang salah cuma satu: nama parameter yang saya pilih ternyata sudah punya pemilik.
Ini kejadian di sebuah situs membership yang saya kerjakan. Alur autentikasi dan pengajuan profilnya pakai pola paling standar di dunia PHP: proses form, redirect balik ke halaman asal, sisipkan ?error=sesuatu di URL, lalu tampilkan notice sesuai isi parameternya. Pola yang sudah saya pakai ratusan kali.
Kali ini notice-nya tidak pernah muncul. Sekali pun.
Gejalanya: URL-nya benar, isinya tidak
Yang bikin bingung, di address bar semuanya kelihatan sempurna. Setelah submit gagal, browser mendarat di URL seperti ini:
/request-profile/?error=exists&match=x&type=companyParameternya ada, ejaannya benar, redirect-nya jalan. Tapi halamannya polos. Tidak ada notice, tidak ada pesan merah, tidak ada apa pun yang memberi tahu user kenapa pengajuannya ditolak.
Reaksi pertama saya jelas salah alamat: saya mulai mencurigai template, kondisi if, urutan hook, sampai caching. Saya sempat menyalahkan full page cache karena dari luar memang mirip gejalanya.
Halaman login lebih jahat lagi. Di sana gejalanya terbaca sebagai "refresh blank", dan yang paling menyesatkan: kadang jalan. /login/?error=captcha sempat menampilkan notice dengan benar waktu saya tes pertama kali. Beberapa jam kemudian, di halaman yang sama, giliran ?error=1 yang datang tanpa parameternya dan notice-nya hilang lagi. Nondeterministik seperti itu adalah cara tercepat untuk membuat orang berhenti percaya sama hasil tesnya sendiri.
Diagnosa dua menit yang harusnya saya lakukan duluan
Setelah cukup lama menebak, saya berhenti menebak. Alih-alih menerka apa yang sampai ke template, saya cetak saja isinya. Satu baris, ditaruh di template yang bersangkutan:
<!-- DBG get=<?php echo esc_html( wp_json_encode( $_GET ) ); ?> -->Saya taruh di dalam HTML comment supaya tidak merusak tampilan, lalu buka URL yang tadi dan lihat view-source. Hasilnya menutup seluruh perdebatan dalam sekali lihat:
<!-- DBG get={"match":"x","type":"company"} -->Key match ada. Key type ada. Key error tidak ada.
Jadi masalahnya bukan di kondisi notice, bukan di template, bukan di cache. Parameternya memang tidak pernah sampai ke kode saya. Semua jam sebelumnya saya habiskan untuk men-debug cabang if yang datanya memang sudah kosong sejak awal.
Akar masalahnya: error bukan milik saya
error adalah salah satu nama query var publik yang sudah dipesan WordPress. Dia bukan nama bebas yang boleh kamu pakai untuk keperluan sendiri, dia bagian dari daftar var yang ikut diproses saat request di-parse.
Di stack situs ini, efeknya keras: parameter itu dibuang secara global sebelum template saya sempat melihatnya. Yang bikin diagnosisnya makin sulit, pembuangan itu tidak konsisten antar path, dan itulah kenapa halaman login sempat terlihat berfungsi lalu berhenti berfungsi tanpa ada yang saya ubah.
Saya sempat tergoda menelusuri siapa persis di rantai eksekusi yang menghapusnya, tapi berhenti karena tidak ada gunanya. Kalaupun ketemu, kesimpulannya tetap sama: selama saya memakai nama yang sudah dipesan, saya sedang berebut dengan sistem yang selalu menang.
Bukti pembandingnya rapi. Di alur yang sama, parameter status yang saya bikin sendiri tidak pernah sekali pun hilang. Bukan karena dia lebih beruntung, tapi karena tidak ada yang mengklaim nama itu.
Perbaikannya: kasih prefix punya sendiri
Solusinya tidak elegan, tidak pintar, dan selesai dalam sepuluh menit. Saya kasih prefix ke semua parameter buatan sendiri supaya tidak mungkin bentrok dengan nama bawaan:
// Sebelumnya, bentrok dengan query var bawaan
wp_safe_redirect( add_query_arg( 'error', 'exists', $return_url ) );
// Sesudahnya, nama milik sendiri
wp_safe_redirect( add_query_arg( 'req_error', 'exists', $return_url ) );Di sisi template, tinggal ganti nama key yang dibaca:
$req_error = isset( $_GET['req_error'] ) ? sanitize_key( wp_unslash( $_GET['req_error'] ) ) : '';
if ( 'exists' === $req_error ) {
echo '<p class="notice notice-error">Profil dengan data ini sudah terdaftar.</p>';
}Saya pakai pola yang sama di seluruh situs: auth_error untuk alur login, req_error untuk alur pengajuan, dan prefix pendek yang seragam untuk parameter status lainnya. Begitu deploy, semua notice yang selama ini bisu langsung muncul, termasuk yang di halaman login. Tidak ada satu pun logika notice yang perlu saya ubah, karena logikanya memang sudah benar sejak awal.
Daftar nama yang jangan pernah kamu pakai
Ini bagian yang saya tempel di catatan supaya tidak kejeblos dua kali. Untuk parameter GET buatan sendiri di WordPress, hindari nama-nama ini:
errorppagenamespost_typem
Semuanya punya arti khusus buat WordPress. Sebagian menyebabkan parameter kamu dibuang seperti kasus di atas, sebagian lagi lebih parah karena justru mengubah query utama atau melempar kamu ke halaman lain. Aturan yang saya pakai sekarang sederhana: kalau parameter itu buatan saya, namanya harus punya prefix saya.
Yang saya bawa pulang
- Kalau kondisi di template tidak pernah kena, jangan langsung mengaudit kondisinya. Buktikan dulu bahwa datanya benar-benar sampai.
var_dumpversi paling hemat waktu di WordPress adalah satu HTML comment berisiwp_json_encode( $_GET ). Dua menit, tidak merusak layout, dan menjawab pertanyaan paling mendasarnya.- Gejala yang nondeterministik bukan berarti hantu. Sering kali artinya perilaku itu bergantung pada path atau konteks request, bukan pada kode yang barusan kamu ubah.
- Nama parameter itu ruang bersama. WordPress sudah mengklaim sebagiannya lebih dulu, jadi kasih prefix pada punyamu dan masalah ini tidak akan pernah datang lagi.