Pertanyaan klasik pemula PHP berbunyi: “Apakah nginx yang menjalankan kode PHP saya?” Jawabannya menarik: bukan — nginx dan PHP adalah dua program berbeda yang bekerja sama, dan keduanya punya wilayah tanggung jawab masing-masing. Web server mengurusi jaringan dan berkas, sedangkan PHP runtime mengurusi eksekusi kode. Pada chapter penutup section ini kita bedah pembagian kerja tersebut, kenali SAPI sebagai jembatan di antara keduanya, bandingkan mod_php dengan PHP-FPM, dan tutup dengan arsitektur shared-nothing yang menjadi identitas PHP.

Siapa Mengerjakan Apa #

Begitu request tiba, kedua program berbagi tugas dengan batas yang cukup tegas. Tabel berikut memetakan pembagian kerjanya:

TugasWeb server (nginx, Apache)PHP runtime
Menerima koneksi TCP dan jabatan tangan TLSmengerjakan—
Menyajikan berkas statis (CSS, JS, gambar)mengerjakan—
Menerapkan aturan routing dan rewrite URLmengerjakandapat membantu lewat kode
Menjalankan kode PHP—mengerjakan
Mengisi superglobals dari data request—mengerjakan
Logika aplikasi, database, session—mengerjakan

Dua baris pertama adalah alasan web server ada: ia melayani style.css dan logo.png dengan kecepatan tinggi tanpa menyentuh PHP sama sekali. Baris-baris terakhir adalah wilayah PHP — termasuk mengisi $_GET dan $_SERVER yang sudah kita bedah pada Chapter PHP Superglobals . Keputusan “request ini milik siapa” sendiri bisa dipetakan dengan aturan web server, dan pola keputusannya sederhana: ekstensi berkas yang dikenal statis dilayani langsung, sisanya diteruskan:

php
<?php
function apakahBerkasStatis(string $path): bool
{
    $ekstensiStatis = ['css', 'js', 'png', 'jpg', 'svg', 'woff2'];
    $ekstensi = strtolower(pathinfo($path, PATHINFO_EXTENSION));

    return in_array($ekstensi, $ekstensiStatis, true);
}

var_dump(apakahBerkasStatis('/assets/style.css'));
var_dump(apakahBerkasStatis('/produk/daftar.php'));
var_dump(apakahBerkasStatis('/tentang'));

Output yang Dihasilkan:

text
bool(true)
bool(false)
bool(false)

Fungsi apakahBerkasStatis() mengambil ekstensi berkas dengan pathinfo() lalu memeriksa keanggotaannya pada daftar ekstensi statis. Path /assets/style.css menghasilkan bool(true) — kandidat kuat dilayani langsung oleh web server — sedangkan /produk/daftar.php dan /tentang (tanpa ekstensi) menghasilkan bool(false) dan wajib diteruskan ke PHP. Versi produksi dari logika ini hidup di konfigurasi location nginx atau directive FilesMatch Apache, tetapi idenya sama persis.

SAPI: Jembatan Server dan Runtime #

Agar web server dan PHP bisa bekerja sama, keduanya butuh kesepakatan tentang cara bertukar data request dan respons. Kesepakatan itu diwujudkan dalam lapisan bernama SAPI (Server API) — himpunan antarmuka yang menyediakan data request ke PHP dan mengirim hasil eksekusi keluar. SAPI jugalah yang menentukan nilai PHP_SAPI yang kita pakai pada Chapter Request Lifecycle :

Nilai PHP_SAPIArti bagi kode kita
cliPHP berjalan dari terminal, tanpa web server
cli-serverBuilt-in web server php -S (khusus pengembangan)
fpm-fcgiPHP-FPM menerima request lewat FastCGI
apache2handlermod_php menempel di dalam proses Apache

Karena nilai tersebut bisa dicek saat runtime, kode kita bisa menyesuaikan diri terhadap lingkungan eksekusinya:

php
<?php
$sapi = PHP_SAPI;

$lingkungan = match (true) {
    $sapi === 'cli' => 'antarmuka baris perintah (CLI)',
    $sapi === 'cli-server' => 'built-in web server (php -S)',
    str_contains($sapi, 'fpm') => 'PHP-FPM di belakang web server',
    default => 'SAPI lain: ' . $sapi,
};

echo "PHP_SAPI: $sapi\n";
echo "Lingkungan: $lingkungan\n";

Output yang Dihasilkan:

text
PHP_SAPI: cli
Lingkungan: antarmuka baris perintah (CLI)

Ekspresi match(true) memeriksa kondisi dari atas ke bawah: pada lingkungan uji kita, PHP_SAPI bernilai cli sehingga cabang pertama menang. Cabang str_contains($sapi, 'fpm') menangkap keluarga nilai FPM seperti fpm-fcgi, sedangkan cabang default menampung SAPI lain yang mungkin dijumpai — misalnya apache2handler pada server yang masih memakai mod_php. Nilai cli-server akan kita lihat hidup pada bagian pengembangan lokal di bawah.

mod_php versus PHP-FPM #

Dua cara klasik menyambungkan web server dengan PHP tumbuh dari dua filosofi berbeda. mod_php menempelkan interpreter PHP langsung ke dalam proses Apache sebagai modul, sehingga tidak ada protokol perantara — tetapi setiap worker Apache membawa interpreter PHP di dalam tubuhnya. PHP-FPM (FastCGI Process Manager) memilih jalan sebaliknya: berdiri sebagai proses mandiri yang mengelola sekumpulan worker PHP, dan web server berbicara dengannya lewat protokol FastCGI:

Aspekmod_phpPHP-FPM
Letak interpreterMenempel di dalam proses ApacheProses worker terpisah dari web server
Protokol ke web serverTidak ada — modul dipanggil langsungFastCGI
Pasangan web server umumApachenginx, Apache, lighttyd, dan lainnya
Isolasi antar aplikasiBerbagi ruang dalam worker ApachePool terpisah per aplikasi atau per pengguna
Jumlah prosesMengikuti konfigurasi worker ApacheDiatur directive pm FPM (static, dynamic, ondemand)
Kecocokan saat iniWarisan lama, jarang dipilih proyek baruStandar de facto untuk produksi modern

Baris “isolasi” pada tabel itu penting untuk hosting dan keamanan: dengan FPM, dua aplikasi berbeda bisa berjalan pada pool berbeda dengan pengguna sistem dan batas sumber daya berbeda pula. Baris “pm” menunjukkan fleksibilitas proses — FPM bisa menjaga sejumlah worker tetap siap (static), menyesuaikan jumlah dengan beban (dynamic), atau membangunkan worker sesuai permintaan (ondemand). Perlu dicatat bahwa worker FPM memang bertahan hidup antar-request, tetapi sebagaimana dibahas pada Chapter Request Lifecycle , seluruh state request tetap dibongkar ulang setiap saat — proses yang awet bukan berarti data yang awet. Panduan lengkap konfigurasi FPM tersedia di dokumentasi resmi PHP-FPM .

Document Root dan Keamanannya #

Document root adalah folder akar yang dipetakan ke / pada URL: request /images/logo.png berarti berkas document_root/images/logo.png di disk. Konsekuensi keamanannya besar — apa pun yang berada di dalam document root bisa dicapai lewat URL. Karena itu berkas sensitif seperti konfigurasi berisi kredensial sebaiknya tinggal di luar document root, dan struktur proyek yang sehat biasanya menyimpan hanya berkas publik di dalamnya. Serangan klasik pada pemetaan ini adalah path traversal: pelaku menyisipkan ../ pada path agar keluar dari document root, misalnya /../../etc/passwd. PHP menyediakan realpath() yang menyelesaikan seluruh referensi relatif menjadi path absolut, sehingga pemeriksaan batas menjadi bisa diandalkan:

php
<?php
function resolusiAman(string $documentRoot, string $berkasDiminta): string|false
{
    $pathAsli = realpath($documentRoot . '/' . ltrim($berkasDiminta, '/'));
    if ($pathAsli === false) {
        return false;
    }

    $akarAsli = realpath($documentRoot);

    if ($pathAsli === $akarAsli) {
        return $pathAsli;
    }

    return str_starts_with($pathAsli, $akarAsli . DIRECTORY_SEPARATOR)
        ? $pathAsli
        : false;
}

Mari kita bedah pertahanannya:

  1. Baris 3 (realpath(...)): menggabungkan document root dengan path yang diminta, sekaligus menyelesaikan semua ../ menjadi path absolut. Bila hasilnya tidak ada di disk, realpath() mengembalikan false dan fungsi langsung berhenti.
  2. Baris 8 ($akarAsli): document root pun diselesaikan ulang dengan realpath() agar perbandingan berpijak pada bentuk absolut yang sama.
  3. Baris 10-15: path hanya dianggap aman bila berada tepat di akar atau diawali akar plus pemisah direktori — sehingga /var/www/app-aman/rahasia.php tidak bisa lolos dengan menyamar sebagai kerabat /var/www/app.

Built-in Web Server untuk Pengembangan #

Untuk pengembangan lokal, PHP membawa web server kecilnya sendiri: php -S. Chapter Built-in Web Server sudah membahas cara pemakaiannya secara tuntas, jadi di sini kita fokus pada posisinya dalam pembagian kerja web server versus runtime. Menariknya, php -S memainkan kedua peran sekaligus — sebagai web server yang menyajikan berkas statis dan sebagai rumah bagi PHP runtime yang mengeksekusi skrip. Kami mengujinya dengan document root -t public dan sebuah router script:

bash
php -S 127.0.0.1:8099 -t public router.php

Router memeriksa tiap request: bila path menunjuk berkas statis yang ada, router mengembalikan false dan built-in server menyajikannya sendiri; selain itu, request diteruskan ke public/index.php. Skrip sederhana di bawah ini memberi tahu kita siapa yang menangani request yang sampai ke PHP:

php
<?php
// public/index.php — dicapai lewat router saat URL bukan berkas statis
echo 'Request ditangani PHP, SAPI: ' . PHP_SAPI;

Hasil pengujian kami dengan curl memisahkan kedua peran dengan jelas. Request /style.css dijawab langsung oleh server dengan status 200 OK dan header Content-Type: text/css; charset=UTF-8 — tanpa header X-Powered-By — bukti bahwa berkas statis dilayani tanpa menyentuh PHP sama sekali. Sebaliknya, request /produk dijawab oleh index.php dengan body Request ditangani PHP, SAPI: cli-server — nilai cli-server sesuai tabel SAPI di awal chapter, dan header X-Powered-By hadir menandai partisipasi PHP.

Arsitektur Shared-Nothing #

Satu istilah untuk mengikat seluruh chapter ini: PHP adalah runtime shared-nothing. Tidak ada memori bersama antar-request — setiap request mendapat lingkungan eksekusi segar, dan seluruhnya dibongkar saat request berakhir, sebagaimana dibuktikan lewat eksperimen penghitung pada Chapter Request Lifecycle . Arsitektur ini adalah akibat langsung dari pembagian kerja yang kita pelajari: web server hanya meminta PHP “jalankan skrip ini dengan data request ini”, tanpa mengharapkan PHP mengingat request sebelumnya.

Bagi kita sebagai pengembang, shared-nothing membawa dua konsekuensi praktis yang saling menyeimbangkan. Sisi menyenangkan: bug berupa kebocoran state antar-pengguna hampir mustahil terjadi secara default, dan menambah kapasitas hanya berarti menambah mesin yang saling bebas. Sisi yang menuntut perhatian: data yang harus bertahan — sesi login, keranjang belanja, hitungan pengunjung — wajib menempatkan diri di luar memori skrip, pada session, database, berkas, atau layanan cache eksternal. Ekstensi OPcache memberi pengecualian kecil yang cerdas: yang dibagikan antar-request hanyalah hasil kompilasi bytecode, bukan data aplikasi.

Ringkasan #

Web server dan PHP runtime adalah dua program dengan wilayah yang jelas: yang pertama menguasai jaringan, TLS, dan berkas statis; yang kedua menguji logika lewat eksekusi kode. SAPI menjadi jembatan di antara keduanya, dengan PHP-FPM sebagai pilihan modern menggantikan mod_php. Document root menandai batas dunia publik di disk, dan arsitektur shared-nothing menjadikan setiap request mandiri. Dengan Content-Type, URL, superglobals, request lifecycle, dan pembagian kerja ini, fondasi web PHP Anda sudah lengkap — Section Form Handling dan Validation dan HTTP State: Sessions dan Cookies membangun aplikasi nyata langsung di atasnya.

Referensi #