Pernahkah Anda berhenti sejenak dan bertanya apa saja yang terjadi di antara momen menekan Enter dan momen halaman tampil? Perjalanan tersebut melibatkan DNS, koneksi jaringan, web server, dan PHP runtime yang bekerja berurutan dalam hitungan milidetik. Memahami urutannya bukan teori kosong: urutan inilah yang menjelaskan mengapa header() harus dipanggil sebelum output, mengapa data hilang setiap kali halaman dimuat ulang, dan mengapa ekstensi seperti OPcache ada. Chapter ini menelusuri siklus hidup request PHP dari awal sampai teardown, lalu membedah model memori per-request yang menjadi ciri khas bahasa ini.
Delapan Tahap Perjalanan Request #
Mari kita petakan perjalanan lengkap sebuah request GET /produk?halaman=2:
- Resolusi DNS. Browser menerjemahkan nama domain menjadi alamat IP dengan bertanya ke server DNS.
- Koneksi TCP dan TLS. Browser membuka koneksi TCP ke server; pada HTTPS, jabatan tangan TLS mengikuti untuk menyiapkan enkripsi.
- Web server menerima request. nginx atau Apache menerima request HTTP mentah lalu mem-parsing method, path, query string, dan header.
- Keputusan statis atau PHP. Web server memeriksa aturannya: berkas
.cssatau.jpgdisajikan langsung dari disk; permintaan yang menurut aturan merupakan tanggung jawab PHP diteruskan. - SAPI membangunkan runtime. Titik sambung antara web server dan PHP — bernama SAPI — menyerahkan data request: mod_php menjalankannya di dalam proses Apache, PHP-FPM menerimanya lewat protokol FastCGI.
- Startup request. PHP menyiapkan lingkungan eksekusi: mengisi
$_GET,$_SERVER, dan kawanannya dari data request, lalu menjalankan berkasauto_prepend_filebila dikonfigurasi. - Eksekusi skrip. Kode berjalan baris demi baris; keluaran ditampung output buffer, dan header dikirim mendahului body saat buffer mulai terkirim.
- Teardown. Setelah skrip selesai, shutdown function dijalankan, data session ditulis, memori dibebaskan, dan worker PHP kembali siap menerima request berikutnya.
Alur pada Chapter Request dan Response memandang siklus ini dari sisi protokol HTTP; fokus kita sekarang adalah sisi mesin PHP yang mengeksekusi kode. Alur sederhana versi Chapter Bagaimana PHP Bekerja — browser, web server, interpreter — kini kita perdalam hingga ke tahap startup dan teardown.
Diagram Urutan Request #
Diagram berikut memvisualisasikan delapan tahap di atas sebagai urutan pesan antar-pelaku:
Membaca diagram dari atas ke bawah: browser menyelesaikan alamat lewat DNS (baris 1-2), lalu request HTTP tiba di web server (baris 3). Web server memutuskan bahwa path /produk bukan berkas statis dan meneruskannya ke PHP-FPM lewat FastCGI (baris 4-5). Worker PHP menjalani startup — superglobals terisi — kemudian mengeksekusi skrip (baris 6-7). Hasil eksekusi mengalir balik melalui web server menuju browser (baris 8-9), dan setelah itu worker menjalani teardown (baris 10). Satu detail penting dari diagram ini: proses PHP-FPM tetap hidup antar-request, tetapi seluruh state request dibongkar pada tahap teardown — tema yang kembali kita bahas pada bagian akhir chapter.
SAPI dan Konstanta PHP_SAPI #
SAPI (Server API) adalah lapisan yang menyediakan data request ke PHP dan mengirim hasil eksekusi keluar — pintu masuk yang berbeda untuk setiap cara menjalankan PHP. Karena setiap SAPI punya perilaku sedikit berbeda, PHP menyediakan konstanta PHP_SAPI dan fungsi php_sapi_name() untuk memberi tahu kode kita sedang berjalan lewat pintu mana:
Nilai PHP_SAPI | Cara PHP dijalankan |
|---|---|
cli | Skrip dipanggil dari terminal |
cli-server | Built-in web server php -S |
fpm-fcgi | PHP-FPM lewat FastCGI |
apache2handler | mod_php di dalam Apache |
<?php
echo PHP_SAPI . "\n";
echo php_sapi_name() . "\n";Output yang Dihasilkan:
cli
cliPada lingkungan uji kita, kedua baris mencetak cli karena skrip dijalankan dari terminal. Informasi SAPI berguna untuk menjaga kode tetap waras di beberapa lingkungan sekaligus — misalnya session sebaiknya hanya diaktifkan pada SAPI web:
<?php
// Hanya aktifkan session pada SAPI web
if (PHP_SAPI !== 'cli') {
session_start();
}Pada request web sungguhan, pemeriksaan semacam ini mencegah skrip CLI — misalnya cron job yang mengimpor data — mengeluarkan peringatan karena mencoba memulai session tanpa request. Pembahasan SAPI yang lebih lengkap, termasuk perbandingan mod_php dan PHP-FPM, menjadi pokok Chapter Web Server vs PHP Runtime .
Output Buffering di Sepanjang Eksekusi #
Aturan “header sebelum output” yang kita pakai sejak Chapter Content Types
bekerja karena output buffering: keluaran echo tidak langsung meluncur ke jaringan, melainkan ditampung di buffer perantara, sementara header menunggu di antrean tersendiri. Selama belum ada satu byte body pun yang benar-benar terkirim, header masih bisa ditambah atau diubah. Fungsi ob_start() memungkinkan kita mengendalikan buffer itu secara manual:
<?php
ob_start();
echo "Baris ini ditampung output buffer, belum sampai STDOUT\n";
$tampungan = ob_get_contents();
ob_end_clean();
echo "Isi buffer yang diambil: " . trim($tampungan) . "\n";Output yang Dihasilkan:
Isi buffer yang diambil: Baris ini ditampung output buffer, belum sampai STDOUTMari kita ikuti urutannya:
- Baris 2 (
ob_start()): membuka buffer baru; sejak titik ini, semuaechomasuk ke buffer tersebut. - Baris 4: teks ditampung — buktinya, baris ini tidak muncul lebih dulu pada output.
- Baris 5 (
ob_get_contents()): mengambil salinan isi buffer tanpa mengosongkannya; hasilnya string yang sama dengan yang di-echo-kan. - Baris 6 (
ob_end_clean()): menutup buffer dan membuang isinya, sehingga teks pada baris 4 tidak pernah sampai ke keluaran. - Baris 8: keluaran sesungguhnya baru terjadi di sini, dengan isi buffer yang diambil secara manual.
Mekanisme inilah yang membuat framework bisa mengirim header Location di tengah proses render lalu membatalkan body, atau menangkap render partial ke variabel. Konfigurasi output_buffering pada php.ini mengatur perilaku bawaan (berapa byte yang ditampung sebelum terkirim), tetapi jangan menjadikannya alasan menunda panggilan header() — kode yang mengirim header sebelum keluaran tetap lebih mudah dibaca dan diandalkan.
Fase Teardown dan Shutdown Function #
Tahap kedelapan siklus hidup — teardown — tidak berarti “langsung mati”. PHP terlebih dahulu menjalankan seluruh fungsi yang didaftarkan lewat register_shutdown_function(), memberi kesempatan terakhir untuk mencatat log, menulis penanda selesai, atau melepas kunci berkas:
<?php
register_shutdown_function(function (): void {
echo "[teardown] register_shutdown_function berjalan di sini\n";
});
echo "[skrip] Eksekusi utama selesai\n";Output yang Dihasilkan:
[skrip] Eksekusi utama selesai
[teardown] register_shutdown_function berjalan di siniUrutan output membuktikan urutan eksekusinya: baris [skrip] tercetak lebih dulu, disusul [teardown] setelah skrip utama selesai. Shutdown function bahkan tetap berjalan ketika eksekusi berakhir karena fatal error — sifat yang membuatnya tempat favorit untuk logging error terakhir pada aplikasi produksi. Setelah semua shutdown function selesai, PHP menutup sesi (data $_SESSION ditulis kembali ke penyimpanan), menutup koneksi yang masih terbuka, dan membebaskan seluruh memori request.
Model Memori Per-Request #
Inilah sifat PHP yang paling memengaruhi cara berpikir kita: setiap request memulai dari kertas kosong. Tabel simbol, objek, koneksi database, konstanta yang didefinisikan runtime — semuanya dibangun ulang dari nol pada setiap request dan dibongkar total saat teardown. Sebagai bukti kecil yang bisa dijalankan sendiri, mari pakai variabel static di dalam fungsi — satu-satunya cara state bertahan di dalam satu request:
<?php
function tambahKunjungan(): int
{
static $kunjungan = 0;
$kunjungan++;
return $kunjungan;
}
echo tambahKunjungan() . "\n";
echo tambahKunjungan() . "\n";
echo tambahKunjungan() . "\n";Output yang Dihasilkan:
1
2
3Dalam satu eksekusi, penghitung naik menjadi 1, 2, 3 karena variabel static bertahan hidup antar-panggilan fungsi. Kami menjalankan berkas yang sama dua kali sebagai dua proses terpisah — padanan dua request yang berbeda — dan eksekusi kedua kembali mencetak 1, 2, 3: penghitung tidak ingat apa pun dari eksekusi pertama. Itulah shared-nothing architecture: tidak ada memori bersama yang bocor antar-request.
Model ini punya dua wajah. Sisi baiknya, request yang satu tidak bisa meracuni request lain — kebocoran memori ikut mati saat request berakhir, dan menambah server berarti menambah salinan yang saling bebas (mudah diskalakan horizontal). Sisi menuntutnya, state yang harus bertahan antar-request — login, keranjang belanja — wajib disimpan di luar memori skrip: session, database, berkas, atau cache eksternal. Arsitektur ini pula yang menjelaskan mengapa runtime Node.js atau Java — yang menyimpan state di memori proses panjang umur — membutuhkan kehati-hatian lain yang tidak kita perlukan di PHP.
Terakhir, mari lihat wujud tahap startup pada request nyata. Skrip kecil berikut mencetak beberapa kunci $_SERVER yang diisi SAPI saat request tiba:
<?php
// info.php — jalankan lewat server: php -S 127.0.0.1:8099
// lalu uji: curl "http://127.0.0.1:8099/info.php?halaman=2"
header('Content-Type: text/plain; charset=utf-8');
foreach (['REQUEST_METHOD', 'REQUEST_URI', 'QUERY_STRING', 'HTTP_HOST'] as $kunci) {
echo $kunci . ' = ' . ($_SERVER[$kunci] ?? '(tidak ada)') . "\n";
}Hasil pengujian kami dengan curl: REQUEST_METHOD = GET, REQUEST_URI = /info.php?halaman=2, QUERY_STRING = halaman=2, dan HTTP_HOST = 127.0.0.1:8099. Nilai-nilai ini hadir begitu eksekusi dimulai — bukti bahwa tahap startup bekerja sebelum baris pertama kode kita berjalan. Pada pengujian yang sama, PHP_SAPI bernilai cli-server, sesuai tabel SAPI di awal chapter.
Ringkasan #
Sebuah request menempuh perjalanan panjang: DNS, TCP dan TLS, keputusan web server, SAPI, startup yang mengisi superglobals, eksekusi dengan output buffering, lalu teardown yang menjalankan shutdown function dan membebaskan memori. Model per-request membuat setiap eksekusi mulai dari nol — state lintas request harus tinggal di session, database, atau cache. Seluruh alur ini menimbulkan satu pertanyaan yang belum terjawab tuntas: siapa sebenarnya mengerjakan apa di antara web server dan PHP runtime? Chapter penutup section ini, Web Server vs PHP Runtime , menjawabnya.