php runtime and server environment
OPcache
Every time PHP runs a script, the source code has to be parsed and compiled into opcodes before it can execute. OPcache keeps that compiled form in shared memory so later requests can skip most of that work.
On a real PHP web application, OPcache is one of the first runtime features you should expect in production. Without it, each request does more CPU work than necessary. With it misconfigured, deployments can appear to "ignore" new code or cache memory can fill up and reduce performance.
What OPcache stores
OPcache stores compiled PHP scripts, not database rows, rendered HTML pages, HTTP responses, or arbitrary application values. It is different from Redis, Memcached, framework caches, and browser caches.
If a file such as src/Controller/HomeController.php is loaded often, OPcache can keep the compiled opcodes for that file in memory. The next request can reuse them instead of parsing and compiling the file again.
That means OPcache helps most with applications that load many PHP files on every request, especially framework applications with Composer autoloading.
Common settings
A typical production starting point looks like this:
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
opcache.revalidate_freq = 0
opcache.enable turns OPcache on for web SAPIs such as PHP-FPM.
opcache.enable_cli controls OPcache for command-line PHP. It is often off because CLI scripts are short-lived, but it may be useful for long-running CLI workers or benchmarking.
opcache.memory_consumption is the shared memory size in megabytes. If it is too small, OPcache cannot hold all the scripts the application uses.
opcache.max_accelerated_files is the maximum number of script files OPcache can track. Framework applications can load thousands of files, so this should not be set too low.
opcache.validate_timestamps controls whether PHP checks source files for changes. In local development it is usually on. In production it is often off, which means deployments must reload PHP-FPM or explicitly reset OPcache.
Inspecting OPcache
You can inspect whether OPcache is loaded and enabled from PHP:
<?php
declare(strict_types=1);
echo 'extension_loaded: ' . (extension_loaded('Zend OPcache') ? 'yes' : 'no') . PHP_EOL;
echo 'opcache.enable: ' . ini_get('opcache.enable') . PHP_EOL;
echo 'opcache.enable_cli: ' . ini_get('opcache.enable_cli') . PHP_EOL;
echo 'validate_timestamps: ' . ini_get('opcache.validate_timestamps') . PHP_EOL;
// Prints:
// extension_loaded: yes
// opcache.enable: 1
// opcache.enable_cli: 0
// validate_timestamps: 1
If opcache.enable_cli is off, some OPcache functions may behave differently from the command line than they do through PHP-FPM. Check the web runtime when investigating production behaviour.
Reading status safely
When OPcache is enabled for the current SAPI, opcache_get_status() gives useful information about memory, cached scripts, and hit rate.
<?php
declare(strict_types=1);
$status = function_exists('opcache_get_status') ? opcache_get_status(false) : false;
if ($status === false) {
echo 'OPcache status is not available for this SAPI.' . PHP_EOL;
exit;
}
echo 'enabled: ' . ($status['opcache_enabled'] ? 'yes' : 'no') . PHP_EOL;
echo 'cached_scripts: ' . $status['opcache_statistics']['num_cached_scripts'] . PHP_EOL;
echo 'hits: ' . $status['opcache_statistics']['hits'] . PHP_EOL;
echo 'misses: ' . $status['opcache_statistics']['misses'] . PHP_EOL;
// Prints:
// enabled: yes
// cached_scripts: 842
// hits: 120450
// misses: 1093
Do not expose a raw OPcache status page publicly. It can reveal filesystem paths, code structure, and runtime details that should not be available to users.
Development versus production
Local development usually favours convenience:
opcache.enable = 1
opcache.validate_timestamps = 1
opcache.revalidate_freq = 0
This lets PHP check file modification times so edits appear quickly.
Production usually favours predictable performance:
opcache.enable = 1
opcache.validate_timestamps = 0
With timestamp validation off, PHP does not keep checking source files for changes. That is faster, but deploys must include a PHP-FPM reload, container restart, or explicit OPcache reset. Otherwise, users may keep running old compiled code after new files have been deployed.
Resetting and invalidating
opcache_reset() clears the entire cache for the current SAPI. opcache_invalidate() clears one script.
<?php
declare(strict_types=1);
$file = __FILE__;
if (function_exists('opcache_invalidate')) {
$result = opcache_invalidate($file, true);
echo 'invalidate_result: ' . ($result ? 'success' : 'not available') . PHP_EOL;
}
// Prints:
// invalidate_result: success
Use these deliberately. Resetting OPcache on every request destroys the benefit of the cache. Most applications handle OPcache refresh as part of deployment rather than application request code.
Preloading
PHP can preload selected files when the runtime starts. Preloaded classes and functions are available before a request loads application code, which can improve startup performance for large applications.
Preloading is an advanced production feature. It ties code loading to process startup, so deployments must restart the PHP process to load changed preloaded files. A junior developer does not need to design a preload strategy, but should recognise it in php.ini or deployment configuration:
opcache.preload = /var/www/app/config/preload.php
opcache.preload_user = www-data
What you should be able to do
After this lesson, you should be able to explain that OPcache stores compiled PHP scripts, inspect whether it is enabled, understand why production deploys may need a PHP-FPM reload, and distinguish OPcache from application data caching.
Practice
Task: Inspect OPcache Configuration
Create a small PHP script that reports whether OPcache is available for the current SAPI.
Requirements
- Print whether the
Zend OPcacheextension is loaded. - Print
opcache.enable,opcache.enable_cli, andopcache.validate_timestamps. - If
opcache_get_status()is available, print whether OPcache is enabled and how many scripts are cached. - If status is not available, print a clear message instead of failing.
- Add a short note explaining why CLI results may not match PHP-FPM results.
Check your work
Run the script from the command line, then explain what you would check differently if the bug only happened in the browser.
Show solution
One possible solution is to guard every OPcache-specific call so the script still explains what happened when OPcache is disabled for CLI.
<?php
declare(strict_types=1);
echo 'Zend OPcache loaded: ' . (extension_loaded('Zend OPcache') ? 'yes' : 'no') . PHP_EOL;
echo 'opcache.enable: ' . ini_get('opcache.enable') . PHP_EOL;
echo 'opcache.enable_cli: ' . ini_get('opcache.enable_cli') . PHP_EOL;
echo 'opcache.validate_timestamps: ' . ini_get('opcache.validate_timestamps') . PHP_EOL;
if (!function_exists('opcache_get_status')) {
echo 'OPcache functions are not available.' . PHP_EOL;
exit;
}
$status = opcache_get_status(false);
if ($status === false) {
echo 'OPcache status is not available for this SAPI.' . PHP_EOL;
exit;
}
echo 'opcache_enabled: ' . ($status['opcache_enabled'] ? 'yes' : 'no') . PHP_EOL;
echo 'cached_scripts: ' . $status['opcache_statistics']['num_cached_scripts'] . PHP_EOL;
// Prints:
// Zend OPcache loaded: yes
// opcache.enable: 1
// opcache.enable_cli: 0
// opcache.validate_timestamps: 1
// OPcache status is not available for this SAPI.
CLI output may not match the web application because opcache.enable_cli is separate from opcache.enable. If the problem happens through the browser, inspect OPcache through a protected diagnostic route, a temporary admin-only script, server metrics, or the PHP-FPM configuration used by that virtual host.