Seluruh materi section ini bermuara pada satu pertanyaan praktis: apa yang terjadi ketika aplikasi gagal di produksi, di depan pengunjung nyata? Kegagalan tidak bisa dihapus — koneksi database terputus, berkas rusak, layanan pihak ketiga lambat — tetapi reaksi aplikasi terhadap kegagalan bisa kita rancang. Tujuannya ganda: pengunjung tetap nyaman dengan pesan yang jelas tanpa istilah teknis, sementara developer tetap menerima bukti lengkap untuk memperbaiki akar masalah. Chapter penutup ini merangkai semua alat yang telah kita pelajari — konfigurasi php.ini, error handler, exception handler, dan logging — menjadi satu strategi produksi yang utuh.

Prinsip: Pisahkan Pengalaman Pengunjung dari Kebutuhan Developer #

Perilaku bawaan PHP menampilkan pesan error mentah langsung ke output, dan hal itu sangat membantu saat pengembangan. Di produksi, kebiasaan yang sama justru berbahaya karena pesan mentah membocorkan hal-hal yang membantu penyerang memahami aplikasi Anda:

  • Path absolut di server, misalnya /var/www/aplikasi/src/pesanan.php, membeberkan struktur direktori.
  • Stack trace menyingkap nama function, class, dan alur internal kode.
  • Pesan kegagalan database bisa memuat potongan query beserta nama tabel.
  • Nomor versi PHP dan library yang terpasang mempermudah pencarian celah keamanan yang diketahui.

Karena itu prinsip dasarnya sederhana: pesan detail mengalir ke log untuk developer, sementara layar pengunjung hanya menerima pesan umum yang menenangkan. Kedua arus ini tidak boleh tertukar — menampilkan detail kepada pengunjung sama berbahayanya dengan mencatat detail yang salah untuk developer.

Konfigurasi Dasar Produksi #

Titik awal strategi adalah konfigurasi yang telah kita bahas pada chapter error-reporting , kali ini dalam bentuk finalnya untuk php.ini produksi:

ini
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/aplikasi/php-error.log

Empat baris ini memindahkan seluruh laporan PHP dari layar ke berkas log: pesan error, warning, sampai kegagalan saat PHP dinyalakan semuanya tercatat, tidak ada yang tampil ke pengunjung. Pastikan direktori /var/log/aplikasi/ dapat ditulis oleh pengguna yang menjalankan PHP dan tidak dapat dibaca dari web. Dengan konfigurasi ini, error_reporting boleh tetap E_ALL — semakin lengkap catatan untuk developer, semakin cepat akar masalah ditemukan.

Mendeteksi Lingkungan Runtime #

Aplikasi yang sama biasanya berjalan di dua dunia: melalui web server untuk melayani pengunjung, dan melalui CLI untuk skrip cron atau perintah administratif. Kebijakan error yang tepat untuk keduanya berbeda — di CLI, pesan yang tampil di terminal justru membantu. Konstanta PHP_SAPI dan function php_sapi_name() memberi tahu kita sedang berjalan di mode mana:

php
<?php
echo "SAPI saat ini: " . php_sapi_name() . "\n";

$appEnv = getenv("APP_ENV") ?: "production";
echo "Lingkungan aplikasi: $appEnv\n";

if (PHP_SAPI === "cli") {
    echo "Mode CLI: pesan error boleh tampil langsung di terminal\n";
} else {
    echo "Mode web: pesan error harus disembunyikan dari pengunjung\n";
}

Output yang Dihasilkan:

text
SAPI saat ini: cli
Lingkungan aplikasi: production
Mode CLI: pesan error boleh tampil langsung di terminal

Pada lingkungan latihan kita, php_sapi_name() mengembalikan cli karena kode dijalankan dari terminal. Aplikasi web dengan PHP-FPM umumnya mengembalikan fpm-fcgi, sedangkan web server bawaan php -S mengembalikan cli-server. Baris getenv("APP_ENV") membaca variabel lingkungan yang lazim dipakai untuk menandai tahap aplikasi — nilainya diambil dari production ketika variabel tersebut tidak diatur, jadi kebijakan aman berlaku secara bawaan. Menjalankan skrip yang sama dengan APP_ENV=development akan membalik kebijakannya tanpa mengubah satu baris kode pun.

Halaman Error yang Ramah Pengunjung #

Setelah pesan mentah disembunyikan, pengunjung yang menabrak kegagalan tetap perlu jawaban. Halaman error yang baik memuat tiga unsur: permintaan maaf yang jujur, penjelasan tanpa istilah teknis, dan kode referensi yang menghubungkan laporan pengunjung dengan baris log:

php
<?php
function buatHalamanError(int $kodeHttp, string $pesanRamah, string $kodeReferensi): string
{
    http_response_code($kodeHttp);

    $html = "<!DOCTYPE html>\n"
        . "<html lang=\"id\">\n"
        . "<head><meta charset=\"utf-8\"><title>Terjadi Kendala</title></head>\n"
        . "<body>\n"
        . "  <h1>Maaf, terjadi kendala</h1>\n"
        . "  <p>$pesanRamah</p>\n"
        . "  <p>Kode referensi: $kodeReferensi</p>\n"
        . "</body>\n"
        . "</html>\n";

    return $html;
}

echo buatHalamanError(
    503,
    "Sistem kami sedang dalam pemeliharaan. Silakan coba lagi dalam 5 menit.",
    "ERR-2026-0115"
);

Output yang Dihasilkan:

text
<!DOCTYPE html>
<html lang="id">
<head><meta charset="utf-8"><title>Terjadi Kendala</title></head>
<body>
  <h1>Maaf, terjadi kendala</h1>
  <p>Sistem kami sedang dalam pemeliharaan. Silakan coba lagi dalam 5 menit.</p>
  <p>Kode referensi: ERR-2026-0115</p>
</body>
</html>

Mari kita telaah bagian-bagian utamanya:

  1. Baris 4: http_response_code($kodeHttp) mengirim status HTTP yang benar — 503 memberi tahu browser dan mesin pencari bahwa kendala ada di pihak server, bukan pada permintaan pengunjung.
  2. Baris 6-14: halaman dirakit sebagai string HTML sederhana dengan pesan yang manusiawi dan tanpa satu pun detail teknis.
  3. Baris 19-22: pemanggil cukup memilih kode status, pesan, dan kode referensi — function ini bisa dipakai ulang untuk 500 (kegagalan umum) maupun 503 (pemeliharaan).

Kode status HTTP sendiri mengikuti kesepakatan yang baku: awalan 5xx berarti server sedang bermasalah, sedangkan 4xx berarti permintaan pengunjung yang salah — halaman 404 untuk URL yang tidak ditemukan adalah contoh 4xx yang paling akrab. Pada framework modern, halaman semacam ini biasanya sudah tersedia sebagai berkas template terpisah yang dapat dikustomisasi, tetapi prinsip isinya tetap sama.

Jaring Pengaman Terakhir #

Sekarang kita rangkai semuanya: exception handler dari chapter exception-handlers sebagai penangkap terakhir, error_log() dari chapter logging-errors sebagai pencatat bukti, dan pesan ramah sebagai penutup untuk pengunjung:

php
<?php
$berkasLog = sys_get_temp_dir() . "/produksi-kasir.log";
@unlink($berkasLog);

set_exception_handler(function (Throwable $e) use ($berkasLog): void {
    // aplikasi nyata: $kodeReferensi = strtoupper(bin2hex(random_bytes(4)));
    $kodeReferensi = "ERR-2026-0115-8F3A";

    error_log(
        sprintf(
            "[%s] %s: %s di %s baris %d\n",
            $kodeReferensi,
            get_class($e),
            $e->getMessage(),
            $e->getFile(),
            $e->getLine()
        ),
        3,
        $berkasLog
    );

    echo "Maaf, terjadi kendali. Kode referensi: $kodeReferensi\n";
});

throw new RuntimeException("Koneksi database timeout setelah 5 detik");

Output yang Dihasilkan:

text
Maaf, terjadi kendali. Kode referensi: ERR-2026-0115-8F3A

Alur kerjanya berjalan seperti ini: exception RuntimeException terlepas tanpa catch, sehingga handler terakhir mengambil kendali. Handler menulis satu baris log berisi kode referensi, kelas exception, pesan, berkas, dan baris kejadian — bentuknya persis seperti [ERR-2026-0115-8F3A] RuntimeException: Koneksi database timeout ... di ... — lalu menampilkan pesan ramah kepada pengunjung. Ketika seorang pengunjung menelepon dukungan dan menyebut kode ERR-2026-0115-8F3A, tim cukup mencari kode itu di log untuk menemukan kejadian persisnya.

Pada aplikasi web sesungguhnya, dua penyesuaian diperlukan. Pertama, kode referensi dibuat acak untuk setiap kejadian — misalnya strtoupper(bin2hex(random_bytes(4))) — seperti tercantum pada komentar; contoh di atas memakai nilai tetap agar hasil latihannya konsisten. Kedua, echo diganti dengan http_response_code(500) diikuti pembacaan template halaman error yang kita rancang di atas. Pemasangannya juga dipindah ke berkas masuk aplikasi (front controller) agar meliputi seluruh request sejak awal.

Monitoring dan Peringatan Dini #

Log yang lengkap tetap sia-sia bila tidak ada yang membacanya sampai pengunjung komplain. Aplikasi produksi yang matang menambahkan lapisan monitoring yang membaca log dan memberi sinyal secara otomatis:

Sinyal yang dipantauContoh ambangTindakan otomatis
Lonjakan baris ERROR per menitdari puluhan menjadi ratusanKirim peringatan ke kanal tim (chat, pager)
Endpoint kesehatan gagal/health bukan lagi 200 OKBangunkan petugas jaga
Ukuran berkas logmelebihi 80% kuota penyimpananJalankan rotasi log
Kode referensi berulangpola sama muncul berulangBuat tiket dengan prioritas tinggi

Endpoint kesehatan (health endpoint) layak dijelaskan sebentar karena paling mudah dibangun: satu URL khusus, misalnya /health, yang tugasnya memeriksa dependensi terpenting — koneksi database, penyimpanan berkas — lalu mengembalikan 200 OK bila semuanya sehat dan 503 bila ada yang bermasalah. Alat pemantauan eksternal seperti layanan uptime check memanggil URL ini setiap beberapa detik dari luar server, sehingga gangguan terdeteksi bahkan sebelum pengunjung sempat mengeluh. Peringatan dini dari lonjakan error bekerja serupa: alat pembaca log menghitung baris per satuan waktu dan mengirim notifikasi ketika hitungan menembus ambang yang tidak wajar.

Checklist Siap Produksi #

Rangkuman seluruh strategi ini cukup muat dalam satu tabel periksa. Jadikan tabel berikut sebagai pemeriksaan terakhir setiap kali aplikasi hendak dirilis:

ItemPengaturan yang benar
display_errorsOff — tidak ada pesan mentah ke pengunjung
log_errorsOn dengan error_log menunjuk direktori khusus di luar folder publik
Level pelaporanTetap E_ALL — catatan untuk developer selengkap mungkin
Halaman error ramahTersedia untuk 500 dan 503, tanpa detail teknis
Kode referensiTerbit di halaman pengunjung dan di baris log yang sama
Exception handler terakhirTerpasang di front controller, mencatat lalu menampilkan pesan ramah
MonitoringEndpoint kesehatan + peringatan lonjakan error
Rotasi logTerjadwal agar berkas tidak membengkak tanpa batas
Uji kegagalan di stagingLempar exception secara sengaja dan pastikan seluruh alur bekerja

Baris terakhir pada tabel sering terlupakan padahal paling menentukan: strategi error handling adalah kode yang jarang berjalan, dan kode yang jarang berjalan pantas diuji secara sengaja. Di lingkungan staging, lemparkan exception tiruan dari beberapa titik aplikasi, lalu pastikan log terisi, kode referensi muncul, halaman ramah tampil, dan peringatan monitoring berbunyi.

Ringkasan #

Strategi error handling produksi berdiri di atas pemisahan dua arus: detail lengkap mengalir ke log untuk developer, pesan umum dan kode referensi mengalir ke layar untuk pengunjung. Konfigurasi php.ini memindahkan laporan PHP ke log, deteksi lingkungan menjaga CLI tetap nyaman dikerjakan, halaman ramah menjaga pengalaman pengunjung, exception handler terakhir merangkai semuanya, dan monitoring memastikan kegagalan terdengar sebelum menjadi keluhan. Dengan chapter ini, perjalanan kita di Section Error Handling genap — dari mengenali jenis error, menangkap exception, sampai merancang reaksi aplikasi yang matang di lapangan. Keterampilan ini akan terus terpakai di setiap aplikasi nyata yang Anda bangun selanjutnya.

Referensi #