D
P
0

WordPress & PHP

Class 'WP_List_Table' not found: Satu File yang Selalu Dimuat Bikin Seluruh Frontend Error 500

21 Juni 2026·3 menit baca
Class 'WP_List_Table' not found: Satu File yang Selalu Dimuat Bikin Seluruh Frontend Error 500

Tepat setelah saya meng-upload sebuah plugin WordPress yang saya bangun, setiap request frontend balik dengan HTTP 500. Bukan satu halaman — semuanya. Homepage, post tunggal, arsip, semua tumbang. Log PHP cuma menampilkan satu baris:

PHP Fatal error: Uncaught Error: Class "WP_List_Table" not found

Yang bikin pusing: sebelum deploy saya sudah jalankan php -l di semua file dan lolos bersih. Tidak ada syntax error. Tapi begitu live, frontend langsung mati total.

Kenapa ini terjadi

Plugin itu punya layar admin yang menampilkan daftar percakapan dalam tabel, jadi saya bikin class yang meng-extend WP_List_Table:

class My_Conversations_List_Table extends WP_List_Table {
    // kolom, baris, pagination, dll.
}

Masalahnya bukan di class-nya — tapi di file mana class itu tinggal dan kapan file itu dimuat. Class ini ada di file yang di-require_once tanpa syarat di hook plugins_loaded. Saya sengaja muat di plugins_loaded karena hook lain di file itu harus ikut nyala di request REST juga, bukan cuma di admin.

Di sinilah jebakannya: WP_List_Table tidak termasuk class yang selalu tersedia. Ia tinggal di:

wp-admin/includes/class-wp-list-table.php

File itu hanya dimuat saat konteks wp-admin — bukan di frontend, bukan di REST. Jadi pada request frontend apa pun, PHP mem-parse deklarasi:

class My_Conversations_List_Table extends WP_List_Table { ... }

…lalu mencoba me-resolve parent class WP_List_Table, tidak menemukannya, dan langsung fatal. Karena file ini di-require di plugins_loaded yang jalan di setiap request, fatal ini terjadi di setiap halaman frontend.

Dan inilah kenapa php -l tidak pernah menangkapnya: lint hanya memeriksa sintaks, bukan resolusi parent class. class A extends B {} adalah sintaks yang sah meski B tidak pernah ada. Resolusi parent baru terjadi di runtime, saat PHP benar-benar memuat class itu — dan di situlah ia meledak.

Perbaikannya: pisahkan filenya, lazy-load di konteks admin

Solusinya: pindahkan class yang meng-extend wp-admin ke filenya sendiri, dan jangan pernah require file itu tanpa syarat. Require hanya dari dalam callback yang dijalankan di konteks admin, setelah menarik parent-nya lebih dulu:

// Dipanggil hanya saat membangun layar admin (mis. dari 'admin_menu')
function my_render_conversations_screen() {
    if ( ! class_exists( 'WP_List_Table' ) ) {
        require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
    }
    if ( ! class_exists( 'My_Conversations_List_Table' ) ) {
        require_once __DIR__ . '/class-my-conversations-list-table.php';
    }
 
    $table = new My_Conversations_List_Table();
    $table->prepare_items();
    $table->display();
}

Karena require untuk file class itu sekarang ada di dalam callback admin, PHP tidak pernah mem-parse deklarasi extends WP_List_Table di request frontend. Parent-nya dipastikan ada lebih dulu lewat class_exists() + require_once, baru class anak dimuat. Frontend kembali hidup, layar admin tetap jalan.

Mana parent yang aman, mana yang berbahaya

Tidak semua extends itu jebakan. Yang penting: apakah parent-nya dimuat saat bootstrap, atau hanya di wp-admin?

  • WP_REST_Controller aman di-extend saat parse time — core memuatnya saat bootstrap, termasuk di request REST dan frontend.
  • WP_Widget juga aman — dimuat awal oleh core.
  • Yang berbahaya adalah parent yang khusus wp-admin: WP_List_Table, dan class layar admin lain seperti WP_Customize_Control / WP_Customize_Manager. Itu hanya ada saat konteks admin.

Aturannya: kalau parent-nya tinggal di wp-admin/includes/*, jangan deklarasikan anaknya di file yang dimuat di setiap request.

Smoke test yang mengalahkan php -l

Karena php -l buta terhadap resolusi parent, saya sekarang pakai tes parse-time yang benar-benar memuat file, mensimulasikan konteks frontend (di mana WP_List_Table tidak ada):

php -r 'define("ABSPATH","/tmp/"); require "class-my-conversations-list-table.php";'

Kalau file itu mencoba meng-extend parent yang tidak ada, perintah ini langsung fatal di terminal — sebelum sampai produksi. Inilah cara cepat memastikan tidak ada file "selalu dimuat" yang diam-diam bergantung pada class wp-admin.

Pelajaran

php -l hijau bukan jaminan file aman dimuat. Jangan pernah mendeklarasikan class yang meng-extend parent khusus wp-admin (WP_List_Table dan kawan-kawan) di file yang dimuat di setiap request. Pisahkan ke filenya sendiri, lalu lazy-load dari callback konteks admin setelah require_once parent-nya. Dan ganti lint dengan smoke test yang benar-benar memuat file — itu yang menangkap fatal resolusi parent sebelum frontend Anda ikut tumbang.