Aplikasi yang ramah di laptop pengembang bisa berubah menjadi kebocoran data di server produksi. Alasannya bukan kodenya, melainkan konfigurasinya: pesan error yang membantu Anda men-debug justru membocorkan path berkas dan struktur internal kepada pengunjung, memori tanpa batas yang memanjani skrip rusak jadi bom waktu, dan opcache yang mati membuat setiap request mengompilasi ulang seluruh berkas. Chapter pembuka section ini membahas jarak antara konfigurasi development dan production: cara membaca directive dengan ini_get(), mengubahnya saat runtime, memakai preset php.ini-production, dan memuat konfigurasi berbeda per environment. Seluruh contoh dijalankan pada CLI PHP 8.4 yang sama dengan buku ini, sehingga Anda bisa membandingkan langsung.
Perbedaan Utama Development dan Produksi #
Matriks berikut merangkum directive yang paling sering berbeda antara dua lingkungan tersebut:
| Directive | Development | Production | Alasan |
|---|---|---|---|
display_errors | On | Off | pesan error membocorkan path, kredensial, dan struktur kode |
display_startup_errors | On | Off | sama, untuk kegagalan fase startup |
log_errors | On | On | di produksi error wajib tercatat, bukan tampil |
error_reporting | E_ALL | E_ALL & ~E_DEPRECATED & ~E_STRICT | keduanya pelihara jangkauan; yang beda hanyalah ke mana error pergi |
memory_limit | longgar / -1 | 128M–256M | membatasi dampak kebocoran memori |
expose_php | On | Off | menyembunyikan versi PHP dari header X-Powered-By |
opcache.enable | 1 | 1 | hasil kompilasi bytecode dipakai ulang antar request |
opcache.validate_timestamps | 1 | 0 | berkas produksi tidak berubah antar deploy; restart saat deploy |
date.timezone | bebas | eksplisit, mis. Asia/Jakarta | menghindari perilaku yang bergantung default server |
Prinsip yang menyatukan tabel ini: development memaksimalkan keterlihatan, produksi memaksimalkan keselamatan dan kecepatan. Error tetap dilaporkan selebar mungkin di kedua lingkungan — yang berubah hanyalah tujuannya, dari layar menjadi berkas log. Nilai error_reporting pada preset produksi memang menyaring E_DEPRECATED; banyak tim modern membiarkan E_ALL penuh juga di produksi dan menyaring di lapisan logging — keputusan tim. Yang tidak bisa ditawar: tampilkan di development, catat di produksi.
Membaca Directive dengan ini_get #
Fungsi ini_get() mengembalikan nilai directive saat ini sebagai string, dan ini adalah cara paling jujur mengaudit apa yang sedang berjalan di server mana pun:
<?php
$daftarDirective = [
"display_errors",
"display_startup_errors",
"log_errors",
"memory_limit",
"date.timezone",
];
foreach ($daftarDirective as $nama) {
$nilai = ini_get($nama);
$tampil = ($nilai === false || $nilai === "") ? "(kosong)" : $nilai;
printf("%-24s = %s\n", $nama, $tampil);
}
echo "SAPI = ", PHP_SAPI, "\n";
echo "php.ini termuat = ", var_export(php_ini_loaded_file(), true), "\n";
echo "OPcache termuat = ", var_export(extension_loaded("Zend OPcache"), true), "\n";Output yang Dihasilkan:
display_errors = 1
display_startup_errors = 1
log_errors = 0
memory_limit = 128M
date.timezone = UTC
SAPI = cli
php.ini termuat = false
OPcache termuat = falseMari kita baca hasilnya seperti seorang insinyur deployment. Konstanta PHP_SAPI bernilai cli — contoh ini berjalan di antarmuka baris perintah, yang memang menampilkan error langsung ke terminal karena display_errors bernilai 1; nyaman untuk belajar, berbahaya di web. Nilai log_errors masih 0, artinya tidak ada yang tercatat ke mana pun — di produksi ini berarti error hilang begitu terjadi. Baris php.ini termuat = false adalah temuan terpenting: lingkungan buku ini berjalan tanpa file konfigurasi sama sekali, sehingga seluruh nilai di atas adalah default bawaan PHP. Terakhir, OPcache termuat = false menegaskan bahwa ekstensi opcache tidak dikompilasi ke dalam build ini; server produksi yang layak harus memuatnya.
Mengubah Directive saat Runtime #
Sebagian directive bisa diubah dari dalam skrip dengan ini_set(), dan perubahan itu berlaku hanya untuk request yang sedang berjalan:
<?php
echo "sebelum: display_errors=", ini_get("display_errors"), "\n";
ini_set("display_errors", "0");
echo "sesudah: display_errors=", ini_get("display_errors"), "\n";Output yang Dihasilkan:
sebelum: display_errors=1
sesudah: display_errors=0Nilai berubah dari 1 menjadi 0 — dan hanya itu yang terjadi. Tidak ada file yang ditulis, tidak ada request lain yang terpengaruh, dan setelah skrip selesai nilainya kembali seperti semula. ini_set() cocok untuk penyetelan terarah di satu titik masuk aplikasi, bukan sebagai tempat menyimpan seluruh konfigurasi produksi. Perlu dicatat juga bahwa tidak semua directive bisa diubah saat runtime: directive bertipe PHP_INI_SYSTEM — termasuk hampir seluruh opcache.* — hanya bisa diset lewat file konfigurasi atau pool PHP-FPM sebelum aplikasi berjalan.
Ada pelajaran penting yang tampak justru dari kegagalan. Saat menguji contoh-contoh chapter ini, kesalahan pada skrip setelah display_errors dimatikan tidak lagi mencetak apa pun — layar benar-benar kosong, tanpa pesan, tanpa jejak. Inilah sisi gelap produksi yang tidak menampilkan error: kegagalan menjadi senyap. Satu-satunya penyelamat adalah log_errors yang menyala dan berkas log yang benar-benar dipantau; tanpa keduanya, Anda baru tahu aplikasi bermasalah ketika pengguna mengeluh.
Preset php.ini-production #
Distribusi sumber PHP menyertakan dua file konfigurasi siap pakai: php.ini-development untuk pengembangan dan php.ini-production untuk produksi. Keduanya berisi directive yang sama persis dengan nilai yang berbeda, dan preset produksinya kurang lebih berbunyi:
; Cuplikan php.ini-production (diringkas)
display_errors = Off
display_startup_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
expose_php = Off
memory_limit = 128M
opcache.enable = 1
opcache.validate_timestamps = 0File ini adalah titik awal yang baik, bukan titik akhir: sesuaikan memory_limit dengan profil aplikasi Anda, arahkan error_log ke lokasi yang benar-benar dipantau tim, dan tetapkan date.timezone eksplisit. Pasangan opcache.validate_timestamps = 0 perlu penjelasan khusus: PHP berhenti memeriksa waktu modifikasi berkas, sehingga performa maksimal tercapai — tetapi setiap deploy harus disertai restart PHP-FPM agar kode baru termuat. Strategi deploy yang menyertai pengaturan ini dibahas pada chapter-chapter selanjutnya di section ini.
Konfigurasi per Environment #
Antarmuka php.ini menyetel satu lingkungan; aplikasi nyata punya beberapa — development, staging, production — yang berbagi kode namun berbeda perilaku. Saklar yang paling lazim adalah variabel environment APP_ENV, dan konfigurasi per environment bisa dimuat berdasarkan nilainya:
<?php
$konfigurasi = [
"development" => ["debug" => true, "cache" => false],
"staging" => ["debug" => true, "cache" => true],
"production" => ["debug" => false, "cache" => true],
];
function muatKonfigurasi(array $konfigurasi, string $appEnv): array
{
return $konfigurasi[$appEnv] ?? $konfigurasi["production"];
}
foreach (["development", "staging", "production", "tidak-dikenal"] as $appEnv) {
$config = muatKonfigurasi($konfigurasi, $appEnv);
printf(
"%-15s -> debug=%s, cache=%s\n",
$appEnv,
var_export($config["debug"], true),
var_export($config["cache"], true)
);
}Output yang Dihasilkan:
development -> debug=true, cache=false
staging -> debug=true, cache=true
production -> debug=false, cache=true
tidak-dikenal -> debug=false, cache=trueTiga environment pertama memuat setnya masing-masing; environment keempat yang tidak dikenal jatuh ke set production lewat operator ??. Arah fallback ini disengaja: saat nilai environment salah ketik atau hilang, aplikasi memilih mode paling aman — debug mati dan cache menyala — alih-alih membocorkan informasi karena kebetulan default ke development. Pada aplikasi nyata, set konfigurasi ini hidup di file terpisah per environment dan dimuat dari satu titik masuk.
Deteksi nilainya sendiri adalah pola getenv() dengan default:
<?php
putenv("APP_ENV"); // simulasi: variabel tidak disetel server
function deteksiAppEnv(): string
{
$nilai = getenv("APP_ENV");
return ($nilai === false || $nilai === "") ? "production" : $nilai;
}
echo deteksiAppEnv(), "\n";
putenv("APP_ENV=staging"); // simulasi: server menyuntikkan variabel
echo deteksiAppEnv(), "\n";Output yang Dihasilkan:
production
stagingFungsi getenv() mengembalikan false ketika variabel tidak ada, sehingga pemeriksaan eksplisit terhadap false dan string kosong menjadi gerbang default production. Pemanggilan putenv() pada contoh di atas hanya mensimulasikan server yang menyuntikkan variabel — pada deployment nyata, variabel seperti APP_ENV datang dari konfigurasi PHP-FPM, container, atau file .env yang tidak masuk repositori. Penanganan variabel environment dan rahasia secara utuh adalah topik Chapter Environment Variables
berikutnya.
Checklist Konfigurasi Produksi #
Sebelum aplikasi dinyatakan siap produksi, audit konfigurasi dengan daftar berikut:
display_errorsdandisplay_startup_errorssudahOff.log_errorssudahOndenganerror_logmengarah ke berkas atau layanan yang benar-benar dipantau tim.error_reportingdiset eksplisit —E_ALLatau preset production — bukan dibiarkan mengikuti default.memory_limiteksplisit dan sesuai profil aplikasi, bukan-1.date.timezoneditetapkan eksplisit, misalnyaAsia/Jakarta.expose_phpsudahOffagar versi PHP tidak bocor lewat header respons.- OPcache aktif dengan
opcache.validate_timestamps = 0, dan prosedur deploy menyertakan restart PHP-FPM. - Tidak ada kredensial di file konfigurasi yang masuk repositori — rahasia hidup di variabel environment.
Ringkasan #
Konfigurasi produksi adalah seni memindahkan keterlihatan dari layar ke log: display_errors dimatikan, log_errors dinyalakan, error_reporting tetap selebar mungkin, memori dibatasi, dan OPcache dinyalakan dengan penyetelan khusus produksi. Preset php.ini-production memberi titik awal, ini_get() memberi kebenaran tentang apa yang sedang berjalan, dan variabel APP_ENV menjadi saklar antar environment dengan fallback yang gagal ke sisi aman. Satu janji dari checklist di atas belum kita penuhi: kredensial yang tidak boleh masuk repositori. Cara mengelolanya lewat variabel environment adalah chapter berikutnya.