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:

DirectiveDevelopmentProductionAlasan
display_errorsOnOffpesan error membocorkan path, kredensial, dan struktur kode
display_startup_errorsOnOffsama, untuk kegagalan fase startup
log_errorsOnOndi produksi error wajib tercatat, bukan tampil
error_reportingE_ALLE_ALL & ~E_DEPRECATED & ~E_STRICTkeduanya pelihara jangkauan; yang beda hanyalah ke mana error pergi
memory_limitlonggar / -1128M–256Mmembatasi dampak kebocoran memori
expose_phpOnOffmenyembunyikan versi PHP dari header X-Powered-By
opcache.enable11hasil kompilasi bytecode dipakai ulang antar request
opcache.validate_timestamps10berkas produksi tidak berubah antar deploy; restart saat deploy
date.timezonebebaseksplisit, mis. Asia/Jakartamenghindari 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
<?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:

text
display_errors           = 1
display_startup_errors   = 1
log_errors               = 0
memory_limit             = 128M
date.timezone            = UTC
SAPI            = cli
php.ini termuat = false
OPcache termuat = false

Mari 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
<?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:

text
sebelum: display_errors=1
sesudah: display_errors=0

Nilai 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:

ini
; 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 = 0

File 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
<?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:

text
development     -> debug=true, cache=false
staging         -> debug=true, cache=true
production      -> debug=false, cache=true
tidak-dikenal   -> debug=false, cache=true

Tiga 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
<?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:

text
production
staging

Fungsi 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_errors dan display_startup_errors sudah Off.
  • log_errors sudah On dengan error_log mengarah ke berkas atau layanan yang benar-benar dipantau tim.
  • error_reporting diset eksplisit — E_ALL atau preset production — bukan dibiarkan mengikuti default.
  • memory_limit eksplisit dan sesuai profil aplikasi, bukan -1.
  • date.timezone ditetapkan eksplisit, misalnya Asia/Jakarta.
  • expose_php sudah Off agar 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.

Referensi #