D
P
0
← Semua artikel Read in English

WordPress & PHP di Produksi

`Cannot redeclare theme_geo_label()`: php -l Bilang Bersih, Upload Folder `inc/` Menjatuhkan Seluruh Situs

· · 4 menit baca
`Cannot redeclare theme_geo_label()`: php -l Bilang Bersih, Upload Folder `inc/` Menjatuhkan Seluruh Situs

Saya punya kebiasaan yang selama bertahun-tahun terasa cukup: sebelum mengirim file PHP ke server, lewatkan dulu ke php -l. Kalau outputnya bersih, berarti aman. Kebiasaan itu bertahan sampai satu sore ketika semua file yang saya kirim lolos lint, dan situsnya tetap tumbang total begitu upload selesai.

Gejalanya: satu upload, seluruh situs hilang

Saya sedang mengerjakan tema kustom untuk sebuah situs WordPress berisi direktori listing. Perubahan hari itu menyentuh beberapa file di folder inc/, jadi saya unggah folder itu sekaligus. Sebelum kirim, tiap file saya cek:

php -l inc/directory-ui.php
# No syntax errors detected in inc/directory-ui.php

Semua bersih. Upload jalan. Lalu situsnya lenyap.

Bukan cuma satu halaman yang aneh, bukan cuma satu template yang rusak. Halaman publik dan /wp-admin sama-sama menampilkan layar putih dengan "There has been a critical error on this website". Tidak ada pintu belakang lewat dashboard, karena dashboard-nya ikut mati.

Di log, errornya sudah menunggu, kira-kira begini bentuknya:

PHP Fatal error:  Cannot redeclare theme_geo_label() (previously declared in
/wp-content/themes/custom-theme/inc/template-functions.php) in
/wp-content/themes/custom-theme/inc/directory-ui.php

Saya sempat kaget beberapa detik, karena pesan itu langsung menyebut dua file yang keduanya baru saja saya nyatakan "clean".

Kenapa php -l mustahil menangkap ini

Ini bagian yang sebenarnya sudah saya tahu secara teori tapi tidak pernah saya rasakan konsekuensinya sampai hari itu.

php -l melakukan satu hal: mem-parse satu file, lalu bilang apakah isinya kalimat PHP yang sah. Dia tidak menjalankan kode, tidak mengikuti require, dan sama sekali tidak tahu ada file lain di tema yang sama.

Dan di sini letak jebakannya: kedua file saya benar secara sintaks. inc/template-functions.php sah. inc/directory-ui.php juga sah. Tidak ada kurung yang lupa ditutup, tidak ada titik koma hilang. Deklarasi fungsi ganda bukan kesalahan sintaks, dia kesalahan runtime yang baru lahir pada detik kedua definisi itu masuk ke proses PHP yang sama.

Cara paling jujur menyebutnya: lint per-file menjawab pertanyaan "apakah file ini valid?", bukan "apakah file ini aman digabung dengan puluhan file lain di tema ini?". Saya selama ini memperlakukan jawaban pertanyaan pertama seolah-olah itu jawaban pertanyaan kedua.

Akar masalahnya: dua fungsi kembar yang selalu dimuat bareng

Saya menambahkan helper baru bernama theme_geo_label() di inc/directory-ui.php. Nama yang persis sama ternyata sudah ada sejak lama di inc/template-functions.php, ditulis untuk keperluan lain di bagian tema yang berbeda.

Bentuk tabrakannya sesederhana ini:

// inc/template-functions.php  (sudah ada sejak lama)
function theme_geo_label( $post_id ) {
    // ...
}
 
// inc/directory-ui.php  (helper baru saya)
function theme_geo_label( $post_id ) {
    // ...
}

Yang membuatnya fatal total, bukan sekadar fatal di satu halaman, adalah cara keduanya dimuat. Dua file itu di-require dari functions.php tanpa syarat apa pun. Artinya keduanya ikut terbaca di setiap request, frontend maupun admin. Tidak ada satu pun jalur request yang lolos, jadi tidak ada satu pun halaman yang selamat. Itu penjelasan kenapa /wp-admin ikut mati dan kenapa situasinya terasa jauh lebih genting daripada bug biasa.

Satu catatan yang saya tahan diri untuk tidak lakukan waktu itu: membungkus deklarasi baru dengan function_exists() supaya errornya diam. Itu memang menghilangkan fatal, tapi hasilnya lebih buruk. Yang dipakai jadi definisi mana pun yang kebetulan dimuat duluan, dan bug-nya berubah dari fatal yang berisik jadi perilaku salah yang senyap. Bug berisik jauh lebih murah.

Perbaikannya: rename satu fungsi, upload satu file

Recovery-nya ternyata jauh lebih kecil daripada kepanikannya. Saya ganti nama helper baru jadi sesuatu yang lebih spesifik dan tidak mungkin bertabrakan, lalu unggah ulang satu file itu saja:

// inc/directory-ui.php
function theme_post_geo_label( $post_id ) {
    // ...
}

Satu file naik, situs hidup lagi dalam hitungan detik. Total downtime-nya pendek, tapi selama beberapa menit itu situsnya benar-benar nol, termasuk pintu masuk admin.

Gate yang sekarang selalu saya jalankan sebelum kirim

Karena php -l secara desain tidak bisa menolong di sini, saya tambahkan satu langkah manual sebelum tiap pengiriman: untuk setiap fungsi baru, grep seluruh tema dan pastikan namanya hanya muncul di satu file.

grep -rl "function theme_geo_label(" --include='*.php' .
# harus mengembalikan tepat 1 file

Kalau outputnya dua baris, berhenti di situ. Jangan upload dulu.

Untuk file yang menambahkan banyak fungsi sekaligus, saya jalankan versi loop-nya biar tidak ada yang kelewat:

grep -oE "^function [a-zA-Z0-9_]+" inc/directory-ui.php | awk '{print $2}' | while read fn; do
  n=$(grep -rl "function $fn(" --include='*.php' . | wc -l | tr -d ' ')
  echo "$n  $fn"
done

Semua baris harus dimulai dengan 1. Angka 2 atau lebih berarti ada tabrakan yang sedang menunggu meledak di server, bukan di laptop.

Yang saya bawa pulang