Php opcache file cache
by Patrick Kerrigan , 07/05/2018 . Tags: Php Web Performance
Opcache is one of PHP’s most powerful tools when it comes to performance. With the release of PHP 7 it received a new feature which has gone largely un-noticed: the file cache. Depending on your hosting environment and/or traffic levels the file cache may be able to help squeeze even more performance out of PHP.
Understanding Opcache
PHP generally operates with a shared-nothing execution model. For every request that hits your web app PHP will read, parse, compile and run the app’s code before throwing all of this work away to be done again for the next request.
With Opcache enabled, PHP can jump straight to the execution of your code. On the first request, the code is read, parsed and compiled. This compiled code (PHP opcodes) is then kept in memory so that these steps don’t need to be repeated for the next request. On the next request, PHP just loads the compiled code from memory and executes it, leading to a huge boost in performance.
For more information on setting up Opcache, see my previous post on PHP performance tuning.
Where Opcache falls short
There are still a few situations where Opcache won’t be able to work as effectively as it should. As the compiled code is kept in memory it gets wiped out when the PHP process is restarted or the cache is cleared. There are many causes for this, such as upgrading PHP or the underlying server, deployments, shared hosting environments that shut down your PHP process after inactivity and more.
In the best case, requests hitting a cold cache will result in a slow initial pageload. In the worst case, such as for high traffic web apps, it can result in extremely high CPU usage as many simultaneous requests all attempt to populate the cache.
File cache — a persistent opcode cache
If you’re running PHP 7 or above, Opcache now has a built in file based cache. This allows PHP to store compiled code both in memory for fast execution, and on disk for situations when the memory gets flushed. This still allows it to skip the parsing and compilation steps when executing the initial requests in a freshly restarted environment.
To enable the file cache, create a directory outside of your document root such as /home/www/opcache (assuming the PHP process runs as the «www» user in this case) and set this as the value of «opcache.file_cache» in php.ini. It’s important that this direcotry is writable by the PHP user.
opcache.file_cache = "/home/www/opcache"
Restart PHP, and if everything is working you should start to see files appear in this directory with a «.php.bin» extension after you load your first page. You can also try restarting your PHP process and should notice a considerable difference in page load times after the restart.
Security implications
You may have noticed that this requires the PHP user to have write access to the file cache directory. This means that if your application is vulnerable to unrestricted file uploads then somebody could upload a pre-compiled PHP script to the file cache directory and run it. Keep this in mind when deciding whether or not you want to make use of this feature.
Php opcache file cache
PHP as a language is an interpreted. But the interpretation happens from the binaries. These binaries are compiled into intermediate bytecodes. OPcache comes into picture during the compilation phase where it stores the precompiled script’s bytecodes into a shared memory. When next time PHP needs to compile the same PHP script, the precompile bytecodes from the shared memory are used.. Therefore removing the requirement for PHP to load and parse scripts on each request. As a result improving overall PHP performance.
The OPcache extension is bundled with PHP 5.5.0 and later versions.
Why it is needed :
Okay wait. Let’s really get into a very vital need of this OPcache in the ever growing world of PHP frameworks. Any PHP framework has some core package files which we never change. These php package files really let us use the framework capabilities. For example for laravel, we have illuminate ‘s laraval packages. Now on top of that, we install new PHP packages as and when needed using dependency managers like composer . We hardly change these files unless we update/install/remove a package dependency.
Now, independent of which PHP framework you are using, these core PHP script files which constitute framework more or less are compiled for each request to generate their bytecodes for interpretation. Similarly, the dependency package PHP script are compied as per their need for that particular request.
Why do we really need to compile these core PHP script files which we indeed hardly change?? Isn’t that an overly unneccessary overhead for PHP compilation process to compile these hundreds of PHP script files on each request and thenm intrepret them? Doesn’t make much sense right? There you go.. OPcache to the rescue.
Important settings of OPcache :
As OPcache overall sounds really fascinating which internally optimizes the compilation process of PHP scripts, it is equally important to know the important settings. Cache handling, cache clearing and cache storing and overall invalidation depends on these settings.
- Finding the php.ini to work with : You need to find the php.ini which is currently in use. There are different ways to find it. You can find it from terminal using :
OR create a temporary script file called ‘info.php` and have following content on it :
Once you open this file into browser, you would be able to see which php.ini is used.
Mostly if you have php version 7.x with fpm installed then it will be inside /etc/php/7.x/fpm/php.ini . Or if you have 7.x with apache2 installed then it will be inside /etc/php/7.x/apache2/php.ini .
- Enable opcache : In the loaded and used php.ini , you can enable and disable opcache by updating following directive :
opcache.enable=1 //Enables OPcache opcache.enable=0 //Disables OPcache
opcache.enable_cli=1 //Enables OPcache opcache.enable_cli=0 //Disables OPcache
opcache.memory_consumption=128
# Make sure you change this path as per your setup cd /var/www/html # Count number of php files find . -type f -print | grep php | wc -l
Now above will givbe you a numeric count of total PHP script files in your codebase. Now you can set OPcache setting accordingly. In the loaded and used php.ini , you can change this setting by updating following directive :
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1 //Enabled opcache.validate_timestamps=0 //Disabled
opcache.revalidate_freq=120
opcache.validate_timestamps=0 //Disables OPcache revalidation
opcache.blacklist_filename=/etc/php/7.x/fpm/opcache_blacklist.txt
#Specific file inside /var/www/html/ /var/www/html/user.php #All files starting with user* inside /var/www/html/ /var/www/html/user #Wildcare usage inside /var/www/html/ /var/www/*-user.php
opcache.interned_strings_buffer=12
Note that to have changes to take effect from php.ini, you need to restart PHP FPM or apache2 depending on your setup.
Clearing the OPcache :
When OPcache is enabled, any changes in cached PHP script files will not take effect until OPcache is cleared or it is revalidated. This is applicable when you release new updates into a OPcache enabled PHP server.
To clear cache there are multiple ways :
- Clearing from browser : You can have a file opcache_refresh.php with following content :
sudo service php7.x-fpm reload
sudo service apache2 reload
// Download the phar curl -sO http://gordalina.github.io/cachetool/downloads/cachetool.phar // Set permissions chmod +x cachetool.phar
// Using an automatically guessed fastcgi server php cachetool.phar opcache:reset --fcgi // Using php fpm socket php cachetool.phar opcache:reset --fcgi=/var/run/php7.x-fpm.sock
OPcache and the automated deployments :
When you have automated deployments set up on your PHP server, you need to incorporate OPcache clearing so that new changes take effect.
If you do not perform this manually, eventually OPcache will revalidate itself based on your OPcache revalidation settings assuming you have validate_timestamps set to 1 . But it is pretty handy to do it manually as part of your automated deployment scripts to make sure changes take immediate effect.
From my personal experience, relading FPM or apache2 to clear OPcache is not a good option. Even if it gracefully reloads PHP processes, any request which is(unfortunarely) ongoing on the server gives 502 bad gateway . I would rather use the alternative like cachetool mentioned in above section which does not affect any ongoing PHP processes at all.
The sequence when you reload OPcache matters. As soon as git or any of your deployment methods pulls the latest PHP code changes, you should first clear the OPcache. Then you can run all the remaining deployment commands which may use the updated PHP files. This makes sure the deployment commands do not use cached PHP script opcodes even when the sever has pulled the latest changes.