Setiap kali PHP mengeksekusi sebuah berkas, engine harus membaca teks program, mengurainya, dan mengompilasinya menjadi instruksi yang bisa dijalankan. Untuk satu skrip kecil pekerjaan itu sepele — tetapi bayangkan sebuah framework dengan ribuan berkas yang mengulang seluruh proses itu pada setiap permintaan HTTP. OPcache menghilangkan pekerjaan berulang tersebut dengan menyimpan hasil kompilasi di memori bersama. Chapter ini membahas apa yang disimpan OPcache, cara memeriksa dan mengonfigurasinya, serta dua fitur lanjutannya: preloading dan JIT.
Siklus Hidup Permintaan Tanpa Cache #
Tanpa opcode cache, setiap permintaan PHP menjalani empat tahap yang sama untuk setiap berkas yang disentuhnya: membaca source dari disk, mengurainya menjadi pohon sintaks (AST), mengompilasi AST menjadi opcode — instruksi mesin virtual milik Zend Engine — lalu mengeksekusi opcode tersebut. Hanya tahap terakhir yang benar-benar berguna bagi pengguna; tiga tahap pertama menghasilkan produk yang sama persis selama berkas tidak berubah.
| Tahap | Pekerjaan | Tanpa OPcache | Dengan OPcache |
|---|---|---|---|
| Baca berkas | membaca source dari disk | setiap permintaan | hanya saat berkas berubah |
| Parse | source menjadi AST | setiap permintaan | dilewati bila cache valid |
| Compile | AST menjadi opcode | setiap permintaan | dilewati bila cache valid |
| Eksekusi | menjalankan opcode | setiap permintaan | tetap setiap permintaan |
Alur lengkapnya tergambar pada diagram berikut: jalur panjang diambil sekali per berkas, jalur pendek diambil pada setiap permintaan berikutnya.
OPcache Menyimpan Bytecode di Shared Memory #
OPcache menyimpan opcode beserta struktur data pendukungnya di segment shared memory yang dibagi oleh seluruh worker PHP-FPM dari pool yang sama. Ketika satu worker mengompilasi sebuah berkas, worker lain cukup mengambil hasilnya dari memori bersama tersebut. Karena kunci berbasis path berkas dipakai untuk mencari cache, pengecekan jauh lebih murah daripada membaca dan mengompilasi ulang source.
Mekanisme ini juga menjelaskan cara OPcache menangani pembaruan kode. Selama opcache.validate_timestamps aktif, engine memeriksa waktu modifikasi berkas setiap opcache.revalidate_freq detik dan membuang cache yang sudah basi — itulah mengapa di lingkungan pengembangan perubahan kode langsung terlihat tanpa me-restart apa pun.
Memeriksa OPcache pada Build Anda #
Sebelum mengonfigurasi OPcache, kita perlu tahu kondisi runtime yang sedang dipakai. PHP menyediakan beberapa fungsi pemeriksaan yang murah:
<?php
echo "SAPI saat ini: " . PHP_SAPI . "\n";
echo "Ekstensi Zend OPcache termuat: ";
var_dump(extension_loaded("Zend OPcache"));
echo "Fungsi opcache_status() tersedia: ";
var_dump(function_exists("opcache_status"));
echo "Nilai ini_get(\"opcache.enable\"): ";
var_dump(ini_get("opcache.enable"));
echo "Nilai ini_get(\"opcache.enable_cli\"): ";
var_dump(ini_get("opcache.enable_cli"));Output yang Dihasilkan:
SAPI saat ini: cli
Ekstensi Zend OPcache termuat: bool(false)
Fungsi opcache_status() tersedia: bool(false)
Nilai ini_get("opcache.enable"): bool(false)
Nilai ini_get("opcache.enable_cli"): bool(false)Mari kita baca hasil pemeriksaan tersebut:
- Baris 2: konstanta
PHP_SAPImenunjukkan antarmuka yang dipakai —cliberarti kita berada di command line, bukan di web server. - Baris 3-4:
extension_loaded("Zend OPcache")adalah pemeriksaan paling fundamental —falseberarti ekstensi tidak termuat sama sekali. - Baris 5-6:
opcache_status()hanya terdefinisi bila ekstensinya termuat;function_exists()menghindari fatal error sebelum kita memanggilnya. - Baris 7-10:
ini_get()mengembalikan nilai pengaturan sebagai string (misalnya"1"), ataufalsebila pengaturannya tidak dikenal.
Pada build PHP CLI yang dipakai course ini, ekstensi Zend OPcache memang tidak termuat, sehingga keempat pemeriksaan menolak — output di atas adalah bukti nyatanya, dan justru itulah gunanya pemeriksaan seperti ini. Di server produksi dengan OPcache aktif, extension_loaded() dan function_exists() mengembalikan true, ini_get("opcache.enable") mengembalikan string "1", dan opcache.enable_cli sering tetap "0" karena cache per proses CLI nyaris tak berguna untuk skrip yang berjalan sekali lalu selesai.
Mengaktifkan dan Pengaturan Penting #
OPcache diaktifkan lewat php.ini atau konfigurasi pool FPM. Cuplikan berikut adalah titik awal yang wajar untuk aplikasi menengah:
; php.ini atau konfigurasi pool FPM
opcache.enable = 1
opcache.memory_consumption = 192
opcache.max_accelerated_files = 10000
opcache.validate_timestamps = 1
opcache.revalidate_freq = 2Arti tiap pengaturan penting untuk dipahami sebelum disalin:
| Pengaturan | Arti | Nilai tipikal |
|---|---|---|
opcache.enable | mengaktifkan opcode cache | 1 |
opcache.memory_consumption | ukuran shared memory dalam MB | 128-256 |
opcache.max_accelerated_files | jumlah berkas yang dapat ditampung | 10.000-100.000 |
opcache.validate_timestamps | mengecek perubahan berkas secara berkala | 1 di dev, 0 di prod |
opcache.revalidate_freq | interval pengecekan timestamp dalam detik | 2 di dev |
Perbedaan terpenting ada di validate_timestamps. Di pengembangan, nilainya 1 agar perubahan kode langsung berlaku. Di produksi, praktik terbaiknya justru 0: pengecekan timestamp per berkas per permintaan dihilangkan sepenuhnya, dan pembaruan kode diterapkan lewat reload PHP-FPM sebagai bagian dari proses deploy — lebih cepat dan lebih deterministik.
Preloading #
Preloading, tersedia sejak PHP 7.4, memindahkan biaya kompilasi lebih jauh lagi: ke saat proses PHP-FPM dinyalakan. Sebuah skrip preload mengeksekusi opcache_compile_file() untuk daftar berkas penting sehingga opcode-nya masuk shared memory dan saling terhubung sebelum permintaan pertama tiba:
opcache.preload = /var/www/app/preload.php
opcache.preload_user = www-data<?php
// preload.php — dijalankan satu kali saat PHP-FPM start, bukan per permintaan.
// Daftarkan class paling sering dipakai agar terkompilasi dan terhubung
// di shared memory sejak awal.
$berkasPenting = [
__DIR__ . "/vendor/autoload.php",
__DIR__ . "/app/Service/LaporanService.php",
__DIR__ . "/app/Repository/ProdukRepository.php",
];
foreach ($berkasPenting as $berkas) {
opcache_compile_file($berkas);
}Konsekuensinya perlu dipertimbangkan sebelum dipakai: setiap perubahan pada berkas yang di-preload menuntut restart FPM, dan daftar yang rakus hanya memakan memori tanpa manfaat. Preloading paling cocok untuk class framework dan dependensi yang stabil — bukan untuk seluruh kode aplikasi yang berubah setiap sprint. Skrip di atas membutuhkan PHP-FPM dengan OPcache aktif sehingga tidak dijalankan pada build CLI course ini; perilakunya dijelaskan di sini sebagai konsep.
JIT Secara Singkat #
JIT (Just In Time compilation) adalah bagian dari OPcache yang menerjemahkan opcode panas menjadi instruksi mesin asli pada saat berjalan. Untuk kode numerik yang padat perhitungan — simulasi, pemrosesan gambar pixel demi pixel, matematika murni — JIT dapat memberikan percepatan berlipat. Untuk aplikasi web yang sebagian waktunya habis menunggu I/O, dampaknya biasanya kecil hingga nyaris tidak terasa. Karena hasilnya sangat bergantung pada bentuk kode, keputusan mengaktifkan JIT harus lahir dari benchmark (chapter sebelumnya), bukan dari mitos. Pengaturannya, opcache.jit dan opcache.jit_buffer_size, terdokumentasi bersama seluruh opsi OPcache lainnya di halaman konfigurasi resmi.
Verifikasi di Produksi #
Di server sungguhan, verifikasi paling lugas dilakukan lewat opcache_status() yang mengembalikan array berisi status, statistik, dan daftar berkas ter-cache:
<?php
$status = opcache_status();
if ($status === false || !$status["opcache_enabled"]) {
echo "OPcache tidak aktif\n";
exit(1);
}
$statistik = $status["opcache_statistics"];
echo "Berkas di cache: " . $statistik["num_cached_scripts"] . "\n";
echo "Total hit: " . $statistik["num_hits"] . "\n";Angka num_hits yang terus bertambah dan rasio hit yang tinggi menandakan cache bekerja; bila angkanya stagnan padahal traffic berjalan, kemungkinan memori atau kuota berkas terlalu kecil sehingga entri saling mengusir. Skrip di atas memerlukan OPcache yang aktif sehingga ditampilkan sebagai contoh untuk server Anda sendiri — pada build course ini opcache_status() memang tidak terdefinisi, sesuai pemeriksaan pada bagian sebelumnya. Sekarang bahwa biaya kompilasi sudah tertangani, sasaran berikutnya adalah hasil perhitungan aplikasi itu sendiri — materi chapter Application Caching.
Referensi #
- PHP: OPcache — halaman utama ekstensi Zend OPcache.
- PHP: Konfigurasi OPcache — daftar lengkap seluruh pengaturan beserta nilai bakunya.
- PHP: Preloading — cara kerja dan batasan preloading.