Cara Kerja PHP

Siklus hidup request, model shared-nothing, serta pengenalan opcache dan JIT.

Daftar isi

Topik ini membedah urutan kejadian sejak browser meminta sebuah halaman PHP sampai halaman tersebut siap ditampilkan. Pembahasan mencakup siklus hidup permintaan (request), model shared-nothing yang menjadi karakter khas PHP, serta peran OPcache dan JIT dalam kecepatan eksekusi. Pemahaman ini menjelaskan perilaku PHP yang sering mengejutkan pemula, misalnya mengapa variabel tidak tetap hidup antara dua kunjungan halaman.

Dari permintaan sampai respons#

Semuanya bermula ketika browser mengirim permintaan HTTP ke server, misalnya untuk halaman halo.php. Server web menerima permintaan tersebut, menemukan berkas .php yang diminta, lalu menyerahkannya kepada PHP untuk dieksekusi. PHP membaca berkas itu, menjalankan kode di dalamnya, dan menghasilkan keluaran yang biasanya berupa HTML. Keluaran tersebut dikirim kembali oleh server sebagai respons (response) HTTP kepada browser, yang kemudian menampilkannya sebagai halaman.

Diagram berikut merangkum empat langkah dalam siklus tersebut.

Browsermeminta halamanServer webApache, Nginx, atauserver bawaanPHPmengeksekusi kode1. permintaan HTTP2. berkas .php3. keluaran HTML4. respons HTML
Siklus hidup satu permintaan halaman PHP

Server web pada diagram dapat berupa Apache, Nginx yang berpasangan dengan PHP-FPM, atau server bawaan PHP yang dibahas pada topik Penyiapan Lingkungan. Meskipun rinciannya berbeda, pola besarnya sama, yaitu server web yang meneruskan pekerjaan kepada PHP dan mengirim ulang hasilnya. Dari sudut pandang pengunjung situs, PHP tidak terlihat sama sekali karena yang tiba di browser hanya keluaran akhirnya.

Model shared-nothing#

PHP mengikuti arsitektur yang dikenal sebagai shared-nothing. Setiap permintaan dieksekusi secara terisolasi, dan segala hal yang dibuat selama permintaan, mulai dari variabel sampai koneksi basis data, dibersihkan begitu permintaan selesai. Permintaan berikutnya dimulai dari nol tanpa mengetahui apa pun tentang permintaan sebelumnya. Tidak ada status yang dibagikan antarpermintaan, dan dari situ nama arsitekturnya berasal.

Perilaku ini dapat dibuktikan dengan skrip kecil. Variabel static pada berkas berikut bertahan sepanjang hidup satu proses, tetapi tidak lebih dari itu.

php hitung.php
<?php
function naikSatu(): int
{
    static $hitung = 0;
    $hitung++;
    return $hitung;
}

echo 'Panggilan pertama: ' . naikSatu() . PHP_EOL;
echo 'Panggilan kedua: ' . naikSatu() . PHP_EOL;
echo 'Panggilan ketiga: ' . naikSatu() . PHP_EOL;
bash
# eksekusi pertama
php hitung.php
# eksekusi kedua, proses baru yang dimulai dari nol
php hitung.php
text Keluaran
Panggilan pertama: 1
Panggilan kedua: 2
Panggilan ketiga: 3
Panggilan pertama: 1
Panggilan kedua: 2
Panggilan ketiga: 3

Proses kedua memulai hitungan dari 1 lagi karena variabel static hanya hidup di dalam satu proses, sedangkan setiap eksekusi php merupakan proses yang baru. Pada lingkungan web, pola yang sama berlaku untuk setiap permintaan. Data yang harus bertahan antarpermintaan, misalnya status login pengguna, karenanya disimpan di luar siklus hidup permintaan, seperti pada session, berkas, atau basis data.

Arsitektur ini membawa manfaat yang nyata. Satu permintaan yang bermasalah tidak menular ke permintaan lain, kebocoran memori tidak menumpuk antarpermintaan, dan kapasitas dapat ditambah dengan menambah server karena tidak ada status yang perlu disinkronkan. Harga yang dibayarkan adalah pekerjaan tambahan ketika status memang diperlukan, sehingga alat seperti session dan basis data menjadi bagian yang wajar dari aplikasi web.

OPcache dan penyimpanan bytecode#

Menyerahkan berkas yang sama berulang kali kepada PHP berarti membaca dan menafsirkan kode yang sama pula. PHP tidak mengeksekusi kode sumber secara langsung. Kode sumber terlebih dahulu dikompilasi menjadi opcode, yaitu instruksi menengah yang dipahami mesin PHP. Tanpa penyimpanan, kompilasi itu berulang pada setiap permintaan, padahal hasilnya tidak berubah selama berkasnya tidak disunting.

OPcache menutup celah tersebut dengan menyimpan bytecode hasil kompilasi di memori bersama, sehingga PHP tidak perlu memuat dan menafsirkan skrip yang sama pada setiap permintaan. Ekstensi ini ikut dibundel sejak PHP 5.5, dan direktif opcache.enable mengendalikan statusnya untuk web dengan nilai default 1.

Beberapa direktif yang paling sering diatur beserta nilai defaultnya pada PHP 8.4 adalah sebagai berikut.

ini Direktif OPcache beserta nilai default pada PHP 8.4
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1

Direktif opcache.memory_consumption menentukan besar memori bersama dalam megabyte, sedangkan opcache.max_accelerated_files membatasi jumlah skrip yang dapat disimpan. Direktif opcache.validate_timestamps membuat PHP memeriksa ulang waktu perubahan berkas, dan selang pemeriksaannya ditentukan oleh opcache.revalidate_freq. Pada lingkungan production, berkas jarang berubah, sehingga sebagian pemasangan menonaktifkan pemeriksaan tersebut demi kecepatan.

JIT dan sasarannya#

JIT adalah lapisan tambahan di atas OPcache yang hadir sejak PHP 8.0. Nama lengkapnya just-in-time compiler, yaitu kompiler yang bekerja saat program berjalan. Alih-alih menjalankan opcode satu per satu, JIT mengubah bagian kode yang sering dieksekusi menjadi kode mesin sehingga bagian tersebut berjalan lebih cepat. Mode yang direkomendasikan untuk pemakaian umum adalah tracing.

Pada PHP 8.4, nilai default opcache.jit adalah disable dengan opcache.jit_buffer_size sebesar 64M, sehingga JIT tidak aktif kecuali diaktifkan secara sengaja. Percepatan yang ditawarkannya paling terasa pada tugas yang menghabiskan waktu untuk menghitung, misalnya pemrosesan matematis. Aplikasi web pada umumnya lebih banyak menunggu basis data dan jaringan, sehingga peningkatannya biasanya jauh lebih kecil. Dokumentasi resmi mencatat bahwa perbaikan performa untuk sebagian besar situs web tidak sebesar lompatan dari PHP 5 ke PHP 7. Apabila suatu saat diperlukan, aktivasi cukup dilakukan dengan mengubah opcache.jit menjadi tracing lalu memulai ulang server. Dampaknya diukur dengan tolok ukur milik aplikasi itu sendiri sebelum dijadikan dasar keputusan.

Kesalahan umum#

Tiga kekeliruan yang paling sering muncul seputar cara kerja PHP adalah sebagai berikut. Ketiganya berakar pada dua sifat PHP, yaitu siklus hidup permintaan yang pendek dan kode yang tidak perlu dibaca ulang setiap saat karena OPcache.

  • Mengharapkan data tetap ada antarpermintaan. Nilai variabel, koneksi, dan sumber daya lain hilang begitu permintaan selesai karena arsitekturnya shared-nothing. Simpan data yang harus bertahan pada session, berkas, atau basis data. Perilaku ini sudah terlihat pada hitung.php yang selalu memulai hitungan dari nol.
  • Menganggap suntingan kode tidak berpengaruh. Bila OPcache menyimpan bytecode dan pemeriksaan ulang berkas dinonaktifkan, PHP masih menjalankan versi lama. Pastikan opcache.validate_timestamps aktif selama pengembangan, atau mulai ulang server setelah menyunting kode.
  • Mengandalkan JIT untuk mempercepat aplikasi web apa pun. Kecepatan aplikasi web umumnya dibatasi oleh basis data dan jaringan, bukan oleh eksekusi opcode. Ukur dahulu letak hambatannya sebelum mengubah pengaturan JIT, karena pengaturan yang tepat mengikuti hasil pengukuran dan bukan dugaan.

Ringkasan#

Browser mengirim permintaan, server web menyerahkan berkas .php kepada PHP, dan keluaran HTML yang dihasilkan dikembalikan sebagai respons. Arsitektur shared-nothing membuat setiap permintaan terisolasi, sehingga data lintas permintaan harus disimpan pada session, berkas, atau basis data. Kode sumber dikompilasi menjadi opcode pada setiap eksekusi, dan OPcache memangkas kerja itu dengan menyimpan bytecode di memori bersama. JIT melengkapi OPcache sejak PHP 8.0, tetapi manfaatnya paling nyata pada beban yang banyak menghitung, bukan pada aplikasi web umum.

Referensi#