Sebuah platform booking berbasis WordPress yang saya bangun berjalan mulus selama berbulan-bulan. Lalu hosting menaikkan PHP dari 7.x ke 8, dan halaman detail listing langsung mati total — layar putih, fatal error. Pesannya jelas dan menusuk:
Fatal error: Uncaught Error: Attempt to read property "price" on arrayBaris pemicunya ada di template part yang menampilkan harga listing. Kira-kira begini bentuknya:
$listing = get_listing_data( $id );
echo '<span class="price">' . $listing->price . '</span>';
echo '<span class="cap">' . $listing->capacity . '</span>';Sekilas tidak ada yang aneh. $listing->price, $listing->capacity — terlihat seperti akses properti objek yang wajar. Tapi justru di situ masalahnya: $listing bukan objek.
Kenapa ini terjadi
Getter dari plugin, get_listing_data( $id ), mengembalikan array asosiatif, bukan objek. Isinya kira-kira:
$listing = array(
'price' => 250000,
'capacity' => 4,
'gallery' => array( /* ... */ ),
// dan seterusnya — semuanya field meta
);Jadi nilai harga sebenarnya ada di $listing['price'], bukan $listing->price.
Di PHP 7, mengakses properti objek pada sebuah array itu salah, tapi hanya melempar notice dan diam-diam menghasilkan null. Kode tetap jalan, halaman tetap tampil — cuma harganya kosong, dan kalau ada yang sempat memperhatikan, paling muncul peringatan di log. Bug-nya laten: sudah ada di sana sejak awal, tapi tidak pernah meledak.
PHP 8 mengubah aturannya. Akses properti pada array bukan lagi notice yang bisa diabaikan, melainkan Error yang fatal. Begitu interpreter ketemu $listing->price dan $listing ternyata array, eksekusi langsung berhenti. Tidak ada null diam-diam lagi. Bug yang sebelumnya tertidur tiba-tiba menjadi situs mati saat upgrade.
Yang bikin lebih runyam, ada masalah kedua yang ikut ketahuan begitu saya menggali. Di alur checkout ada guard untuk memvalidasi listing:
$listing = get_listing_data( $id );
if ( empty( $listing['id'] ) ) {
// listing dianggap tidak valid -> redirect keluar dari checkout
wp_safe_redirect( home_url() );
exit;
}Masalahnya: get_listing_data() hanya mengembalikan field meta. Tidak ada key 'id' di dalamnya sama sekali. Jadi empty( $listing['id'] ) selalu bernilai true untuk setiap listing — yang sah sekalipun. Guard ini menganggap semua listing tidak valid dan salah me-redirect orang keluar dari checkout. Ini bug logika yang berdiri sendiri, tidak ada hubungannya dengan versi PHP, tapi sumbernya sama persis: berasumsi soal bentuk data tanpa pernah benar-benar memeriksanya.
Perbaikannya
Perbaikan untuk fatal error-nya langsung: akses pakai sintaks array.
$listing = get_listing_data( $id );
echo '<span class="price">' . $listing['price'] . '</span>';
echo '<span class="cap">' . $listing['capacity'] . '</span>';Untuk guard checkout, saya berhenti mengandalkan key yang memang tidak pernah ada. Cara yang benar untuk memastikan sebuah listing itu eksis adalah bertanya ke WordPress langsung lewat get_post(), bukan menebak-nebak isi array meta:
$post = get_post( $id );
if ( ! $post || $post->post_type !== 'listing' ) {
wp_safe_redirect( home_url() );
exit;
}
$listing = get_listing_data( $id );get_post() mengembalikan objek post sungguhan (atau null kalau tidak ada), jadi pengecekannya akurat dan checkout berhenti menendang keluar listing yang sah.
Satu hal lagi: beberapa template part memang butuh id dan title dari listing-nya. Daripada memaksa mereka membaca $listing->id (yang tidak ada) atau memanggil ulang ke mana-mana, saya suntikkan field itu ke dalam array setelah getter dipanggil, supaya template punya semua yang dibutuhkan dalam satu bentuk yang konsisten:
$post = get_post( $id );
$listing = get_listing_data( $id );
$listing['id'] = $post->ID;
$listing['title'] = get_the_title( $post );Sekarang template part bisa membaca $listing['id'] dan $listing['title'] dengan aman, dan tidak ada lagi yang berasumsi getter-nya mengembalikan objek.
Pelajaran
Konfirmasi bentuk nilai balik sebuah getter — array atau objek — sebelum kamu mengaksesnya. Nama seperti get_listing_data() tidak memberitahumu apa pun soal ini; satu-satunya cara tahu adalah membaca implementasinya atau var_dump() hasilnya sekali. Saya menganggap getter itu mengembalikan objek hanya karena "rasanya" seperti objek, dan asumsi itu bertahan diam-diam selama berbulan-bulan.
PHP 8 mengubah sejumlah notice lama menjadi fatal error, dan ini salah satu yang paling sering menggigit di proyek lawas. Mengakses properti pada array dulu hanya bisikan di log; sekarang dia mematikan halaman. Artinya, upgrade PHP bukan sekadar urusan kompatibilitas sintaks — dia membongkar setiap asumsi malas yang selama ini ditutupi oleh perilaku permisif PHP 7. Bug yang laten bertahun-tahun bisa berubah jadi situs down hanya karena satu kenaikan versi minor di panel hosting.
Pelajaran praktisnya: sebelum migrasi PHP 8, telusuri kode untuk pola -> pada nilai yang sumbernya array, dan jangan pernah memvalidasi keberadaan sesuatu lewat key yang belum kamu pastikan ada. Kalau mau tahu sebuah post eksis, tanyakan ke get_post() — bukan ke array meta yang kebetulan kamu pegang.
