Aplikasi PHP yang sama hampir selalu berjalan di lebih dari satu tempat: di laptop Anda saat development, di server staging untuk pengujian, dan di server produksi untuk pengguna sungguhan. Meskipun kodenya identik, ketiga lingkungan tersebut membutuhkan konfigurasi yang berbeda — alamat database, kredensial, hingga mode tampilan error. Menulis konfigurasi semacam itu langsung di dalam kode membuat setiap perpindahan lingkungan menuntut perubahan kode dan membuka peluang kebocoran data produksi. Environment variables menyelesaikan masalah ini dengan menyimpan konfigurasi di lingkungan tempat proses berjalan, bukan di dalam berkas kode.
Mengapa Konfigurasi Dipisah dari Kode #
Prinsip twelve-factor app menyebutnya sebagai pemisahan config dari code: kode yang sama dirilis ke semua lingkungan, sedangkan hal yang berbeda antar lingkungan disuntikkan lewat environment. Dengan pola ini, memindahkan aplikasi dari staging ke produksi cukup mengganti nilai environment — tanpa menyentuh satu baris kode pun, dan tanpa membangun ulang paket aplikasi. Konfigurasi menjadi sesuatu yang dimiliki operator server, bukan sesuatu yang ditanam permanen di repositori.
Berikut contoh nilai yang lazim berbeda antar lingkungan:
| Nama variable | Nilai development | Nilai production |
|---|---|---|
APP_ENV | development | production |
APP_DEBUG | true | false |
DB_HOST | 127.0.0.1 | 10.0.1.20 |
DB_NAME | toko_dev | toko_prod |
MAIL_MAILER | log | smtp |
Sebagai patokan, semua nilai yang berpotensi berbeda antar lingkungan — alamat layanan, kredensial, flag debug — adalah kandidat kuat environment variable. Sebaliknya, konstanta bisnis murni seperti tarif pajak atau batas maksimal keranjang belanja lebih tepat hidup di dalam kode karena nilainya sama di lingkungan mana pun.
Membaca Nilai dengan getenv() dan putenv() #
PHP menyediakan function getenv() untuk membaca environment variable dan putenv() untuk menulisnya pada proses yang sedang berjalan. Function getenv() mengembalikan nilai bertipe string, atau false ketika variable yang diminta tidak ada. Mari kita uji keduanya secara langsung:
<?php
putenv('KURSUS_APP_ENV=development');
var_dump(getenv('KURSUS_APP_ENV'));
var_dump(getenv('KURSUS_TIDAK_ADA'));Output yang Dihasilkan:
string(11) "development"
bool(false)Mari kita telaah kode di atas:
- Baris 2 (
putenv('KURSUS_APP_ENV=development');): menetapkan variableKURSUS_APP_ENVbernilai"development"pada environment proses PHP saat ini. Nama variable sengaja diberi prefixKURSUS_agar mudah dibedakan dari environment sistem. - Baris 4 (
var_dump(getenv('KURSUS_APP_ENV'));): membaca kembali nilai yang baru ditetapkan dan mencetak tipenya —string(11)berarti string sepanjang 11 karakter. - Baris 5 (
var_dump(getenv('KURSUS_TIDAK_ADA'));): variable yang tidak pernah ditetapkan menghasilkanbool(false), bukannulldan bukan string kosong.
Perlu dicatat bahwa putenv() hanya berlaku untuk proses PHP saat ini beserta proses anak yang dibangkitkannya — perintah ini tidak mengubah environment shell tempat Anda mengetik. Di server sungguhan, nilai environment biasanya sudah diset oleh systemd, container runtime, atau supervisor sebelum PHP berjalan; putenv() paling sering kita pakai justru saat menguji kode konfigurasi seperti pada chapter ini.
variables_order dan Superglobal $_ENV #
Selain getenv(), PHP juga menyediakan superglobal $_ENV berupa array yang berisi snapshot environment saat proses PHP dimulai. Apakah array tersebut terisi ditentukan oleh direktif variables_order pada php.ini: nilainya adalah gabungan huruf E (Environment), G (Get), P (Post), C (Cookie), dan S (Server). Build PHP yang dipakai course ini berjalan tanpa berkas php.ini, sehingga memakai nilai bawaan EGPCS — huruf E ikut serta. Berbeda dengan itu, php.ini-development dan php.ini-production resmi dari PHP memilih GPCS, yang membuat $_ENV dibiarkan kosong.
<?php
$urutan = ini_get('variables_order');
echo "variables_order: $urutan\n";
echo "Huruf E ada: " . (str_contains($urutan, 'E') ? 'ya' : 'tidak') . "\n";
putenv('KURSUS_DB_HOST=127.0.0.1');
echo 'getenv: ' . getenv('KURSUS_DB_HOST') . "\n";
var_dump(array_key_exists('KURSUS_DB_HOST', $_ENV));Output yang Dihasilkan:
variables_order: EGPCS
Huruf E ada: ya
getenv: 127.0.0.1
bool(false)Tiga hal penting dapat kita amati dari kode di atas:
- Baris 2-4:
ini_get()membaca nilai direktifvariables_orderdari konfigurasi build ini, yaituEGPCS, sehingga hurufEterdeteksi. - Baris 6-7:
getenv()berhasil membaca nilai yang baru saja ditulisputenv()—getenv()selalu membaca environment asli proses tanpa pedulivariables_order. - Baris 8: meskipun
$_ENVaktif karenaEada padavariables_order,array_key_exists()mengembalikanfalse—$_ENVadalah snapshot awal, sehingga penulisan lewatputenv()setelah proses berjalan tidak terpantau di dalamnya.
Semua Nilai Berbentuk String #
Environment variable tidak mengenal tipe data: angka, boolean, apa pun isinya dikirim sebagai teks. PHP tidak melakukan konversi otomatis saat Anda membacanya, sehingga getenv('KURSUS_MAX_RETRIES') yang berisi 3 tetap berupa string "3", bukan integer. Konsekuensinya, kita perlu melakukan casting secara eksplisit:
<?php
putenv('KURSUS_MAX_RETRIES=3');
$maxRetries = getenv('KURSUS_MAX_RETRIES');
var_dump($maxRetries);
$sebagaiInt = (int) $maxRetries;
var_dump($sebagaiInt + 1);
putenv('KURSUS_FEATURE_FLAG=true');
$fiturAktif = filter_var(getenv('KURSUS_FEATURE_FLAG'), FILTER_VALIDATE_BOOL);
var_dump($fiturAktif);Output yang Dihasilkan:
string(1) "3"
int(4)
bool(true)Mari kita bedah baris per baris:
- Baris 4-5:
var_dump()menunjukkanstring(1) "3"— nilai dari environment selalu string meskipun terlihat seperti angka. - Baris 7-8: cast
(int)mengubah string"3"menjadi integer3, sehingga operasi aritmetika$sebagaiInt + 1menghasilkanint(4). - Baris 10-12: untuk boolean,
filter_var()denganFILTER_VALIDATE_BOOLmengenali string"1","true","on", dan"yes"sebagaitrue, serta nilai lainnya sebagaifalse— jauh lebih aman daripada casting(bool)yang menganggap semua string non-kosong sebagaitrue.
Pola Nilai Default yang Aman #
Variable opsional biasanya perlu nilai cadangan ketika tidak diset di environment. Karena getenv() mengembalikan false untuk variable yang tidak ada, pemeriksaan yang tepat adalah membandingkan hasilnya dengan false secara eksplisit:
<?php
function ambilEnv(string $kunci, ?string $default = null): ?string
{
$nilai = getenv($kunci);
return $nilai === false ? $default : $nilai;
}
putenv('KURSUS_APP_NAME=Toko Budi');
putenv('KURSUS_TIMEOUT=0');
echo ambilEnv('KURSUS_APP_NAME', 'Aplikasi Default') . "\n";
echo ambilEnv('KURSUS_TIDAK_ADA', 'Aplikasi Default') . "\n";
echo 'Dengan ?:: ' . (getenv('KURSUS_TIMEOUT') ?: '30') . "\n";
echo 'Dengan fungsi: ' . ambilEnv('KURSUS_TIMEOUT', '30') . "\n";Output yang Dihasilkan:
Toko Budi
Aplikasi Default
Dengan ?:: 30
Dengan fungsi: 0Mari kita amati hasilnya:
- Baris 2-6 (function
ambilEnv): membaca variable; jikagetenv()mengembalikanfalse, nilai$defaultyang dikembalikan — perbandingan memakai=== falseagar hanya variable yang benar-benar tidak ada yang digantikan default. - Baris 11-12: variable
KURSUS_APP_NAMEada sehingga nilainya tercetak, sedangkanKURSUS_TIDAK_ADAdigantikan"Aplikasi Default". - Baris 14-15: inilah jebakannya —
KURSUS_TIMEOUTberisi string"0"yang sah, tetapi operator?:menganggapnya falsy sehingga mencetak30(salah), sementaraambilEnv()dengan benar mencetak0.
Berkas .env untuk Development #
Mengetik export KURSUS_DB_HOST=... setiap kali membuka terminal tentu melelahkan. Praktik umum di development adalah menyimpan daftar KEY=VALUE pada berkas .env di root proyek, lalu memuatnya ke environment saat aplikasi start. Pustaka seperti vlucas/phpdotenv dan Symfony Dotenv (dipakai Laravel dan Symfony) melakukannya dengan matang — menangani tanda kutip, export, dan variable bersarang. Berikut versi sederhananya agar kita paham mekanismenya:
<?php
function muatDotEnv(string $isiBerkas): void
{
foreach (explode("\n", $isiBerkas) as $baris) {
$baris = trim($baris);
if ($baris === '' || str_starts_with($baris, '#') || !str_contains($baris, '=')) {
continue;
}
[$kunci, $nilai] = explode('=', $baris, 2);
putenv(trim($kunci) . '=' . trim($nilai));
}
}
$isiEnv = <<<'ENV'
#-- Konfigurasi lingkungan development
KURSUS_APP_NAME=Toko Budi
KURSUS_DB_HOST=127.0.0.1
KURSUS_DB_NAME=toko_dev
ENV;
muatDotEnv($isiEnv);
echo getenv('KURSUS_APP_NAME') . "\n";
echo getenv('KURSUS_DB_HOST') . "\n";
echo getenv('KURSUS_DB_NAME') . "\n";Output yang Dihasilkan:
Toko Budi
127.0.0.1
toko_devFunction muatDotEnv() memecah isi berkas per baris, melewati baris kosong dan baris komentar berawalan #, lalu memisahkan kunci dan nilai pada tanda = pertama (batas 2 membuat nilai yang mengandung = tetap utuh). Setiap pasangan lalu didaftarkan lewat putenv(), dan hasilnya dapat dibaca getenv() seperti biasa. Format berkas konfigurasi seperti ini dibahas lebih mendalam pada chapter Configuration Files
.
Mencantumkan Environment dengan Aman #
Saat men-debug konfigurasi, kita kadang ingin melihat variable mana saja milik aplikasi yang sudah termuat. Memanggil getenv() tanpa argumen mengembalikan seluruh environment sebagai array — mencetak semuanya adalah keputusan buruk karena environment juga memuat PATH, token CI, dan berbagai rahasia sistem. Cara yang aman adalah memfilter hanya prefix milik aplikasi kita sendiri:
<?php
putenv('KURSUS_DEMO_APP_NAME=Toko Budi');
putenv('KURSUS_DEMO_DB_HOST=127.0.0.1');
putenv('KURSUS_DEMO_DB_USER=budi');
foreach (getenv() as $kunci => $nilai) {
if (str_starts_with($kunci, 'KURSUS_DEMO_')) {
printf("%s=%s\n", $kunci, $nilai);
}
}Output yang Dihasilkan:
KURSUS_DEMO_APP_NAME=Toko Budi
KURSUS_DEMO_DB_HOST=127.0.0.1
KURSUS_DEMO_DB_USER=budiLoop di atas hanya mencetak tiga variable yang diawali KURSUS_DEMO_ — sisanya diabaikan meskipun ada ratusan variable sistem di environment. Pola ini aman dipakai di halaman diagnostics internal karena output-nya terprediksi dan tidak membocorkan nilai di luar prefix aplikasi.
Ringkasan #
Environment variables memisahkan konfigurasi dari kode: nilai dibaca lewat getenv() yang selalu andal, semua nilai berbentuk string sehingga perlu cast eksplisit, nilai default sebaiknya diberikan lewat perbandingan === false, dan berkas .env mempermudah development dengan tetap tidak boleh ter-commit. Dengan pola ini, satu paket kode dapat berpindah lingkungan hanya dengan mengganti environment. Namun, sebagian nilai yang lewat jalur ini — password database, API key, kunci penandatanganan — menuntut perlakuan khusus karena kebocorannya berakibat fatal; itulah fokus chapter Managing Secrets
berikutnya.