BONUS: We have discussed this topic with an expert in the PHP community in our podcast:
Out of the proverbial, box, PHP provides decent performance. However, there are several things that we, as PHP developers and systems administrators, can do to increase its performance even further; sometimes for almost no effort.
In this post, I’m going to step through five of those ways. By the time you’re finished reading, you should see at least a notable increase in the performance of your PHP application. Let’s begin.
Use PHP 8 (or at least 7)
One of the best ways to improve PHP’s performance is to run the latest version (PHP 8). Or if you are struggling with old legacy code, at least a version of PHP 7. They offer a significant speed improvement over any previous PHP 5 version. Now, results will always vary, as no two applications are ever the same. However, based on reports by a range of different developers and notable hosting companies, the performance improvements are significant.
Here’s a small sample:
- Official PHP benchmarks report that PHP 7 can execute twice as many requests per second when compared to PHP 5.6.
- Kinsta’s PHP performance benchmarks show WordPress 5.0 can execute 3x as many transactions as PHP 5.6; and
- Pantheon reported that PHP 7 gave a 64% improvement over version 5.3.
If that’s not enough for you, check your benchmarks and see what they say.
Additionally, PHP 7.0.0 was released on December 3rd, 2015, which is already almost a decade ago.
What’s more, the final release of PHP 5, 5.6, went EOL (End of Life) on Dec 31, 2018. So, if you haven’t already, it’s well past time you made the transition to PHP 7. Make the upgrade and, as Rasmus said at phpCE 2018, go green(er) by reducing your hosting costs.
The next thing to check is that Xdebug is not installed on your production servers. Sure, Xdebug is one of the most sophisticated and comprehensive profilers and debuggers for PHP, but it should never be enabled (even installed) on a production server.
While the performance benchmarks vary (they always do), one report on Stack Overflow showed a 50% performance boost by completely removing Xdebug. Though for some modes in Xdebug 3, the overhead is lower. What’s more, it’s important to note that even though Xdebug was installed on the server — it wasn’t even enabled!
To check if it’s installed, run
php -m | grep -i xdebug or check your hosting provider’s administration panel. And if you’d like to know more about how Xdebug works, check out the Xdebug documentation.
Use Composer Optimize Autoloader
While we’re all likely very familiar with using Composer to handle package management for us, if we don’t consider optimizing the configuration it generates, our applications aren’t going to perform as well as possible.
Here’s a quote from the composer documentation which explains why:
due to the way PSR-4 and PSR-0 autoloading rules are set up, it Composer needs to check the filesystem before resolving a class name conclusively. This slows things down quite a bit, but it is convenient in development environments because when you add a new class, it can immediately be discovered/used without having to rebuild the autoloader configuration.
To improve performance, Composer offers three optimization levels; these are:
- Class map generation
- Authoritative class maps
- APCu cache
1: Class map generation
To enable this optimization level, run the following command:
composer dump-autoload --optimize
2/A: Authoritative class maps
In addition to automatically enabling Level 1, when using this level, if a class is not found in the generated classmap, the autoloader will not attempt to look on the filesystem according to PSR-4 rules. To enable this optimization level, run the following command:
composer dump-autoload --classmap-authoritative
2/B: APCu cache
This level adds an APCu cache as a fallback for the class map. However, it does not generate the classmap. Given that, level one would need to be enabled manually. To enable this optimization level, run the following command:
composer dump-autoload --apcu
There are Trade-Offs
While each of these levels can improve application performance, they each have trade-offs which need to be understood before they’re used. Make sure you consult the Composer documentation before using them.
Since PHP is an interpreted, not a compiled, language, the PHP runtime needs to convert source code to executable byte code, before it can be executed. And as PHP is a shared-nothing architecture, this process needs to happen on each request.
However, with Opcode cache (OPcache), this step only needs to happen once for each file, as the generated Opcodes can be cached in shared memory and referenced there instead.
So, as you can imagine,OPcache is one of the least intensive ways to improve the performance of a PHP application, because no code needs to change. Some reports show up to a 70% speed improvement.
There have been several Opcode caches for PHP over the years. OPCache (formerly Zend Cache) has been bundled with PHP since version 5.5 — and is enabled by default in PHP 7.
If you’ve not heard of PHP 7.4’s new preloading feature, yet, it’s very cool! In a nutshell, the feature takes OPcache functionality further than its ever gone before.
To quickly recap, the first time that a PHP source file is encountered, it has to be parsed, then compiled into machine-dependent bytecode, before the Zend Engine can execute it. OPcache significantly reduce the overhead of this process, as after the first time that source code is parsed and compiled down the bytecodes are then stored in an Opcode cache in shared memory.
The next time a request for that file is encountered, PHP checks to see if the Opcode cache has bytecodes for the file. If it does, they’re returned and used. If not (or if the source file has changed since the bytecodes were compiled), the source file is parsed, compiled, and cached. This gives a notable performance boost to PHP.
Now, let’s look at how preloading works. To quote the implementing RFC:
On server startup – before any application code is run – we may load a certain set of PHP files into memory – and make their contents “permanently available” to all subsequent requests that will be served by that server. All the functions and classes defined in these files will be available to requests out of the box, exactly like internal entities. Preloading ensures that:
- All functions and most classes, defined in these files will be permanently loaded into PHP’s function and class tables and become permanently available in the context of any future request.
- PHP resolves class dependencies and links with parent, interfaces, and traits (something that doesn’t happen with Opcode caching).
- PHP removes unnecessary includes and performs other optimizations.
By having the code for an entire application, including its framework (such as Zend Expressive, Symfony, and Laravel) preloaded into memory, code that only changed on server restart, most applications will perform significantly better.
That said, preloading has a few, potential, drawbacks that you should know about:
- A server restart is required when source files change.
- Preloading won’t work in shared hosting environments.
- Preloading won’t work when there are multiple versions of the same application.
Preloading isn’t magical. Both your code and deployment processes will have to be refactored to take advantage of it. For example, someone will have to develop a custom loader script to determine which files to load on server startup, and that script will have to be run at server startup. Regardless, it’s a significant improvement, one worth investing in!
Use a Profiler
Now for an option that will take a little more work than any of the previous five. Often we jump in and attempt to guess where the performance bottlenecks in our applications are, using intuition and educated guesses.
While these can work, they’re not the most efficient approaches. Instead, we can use code profilers to analyze our code and show where the bottlenecks are. Specifically, they help answer questions such as the following:
- How many times was each method called?
- What was the maximum execution time of each method?
- What was the average execution time of each method?
- How many times was a file included?
- What path did a request take through an application (from the first to the last code file)?
By drilling down into a profiler’s results, you can often be pleasantly surprised, though more likely shocked, to find that your application is executing code paths and classes that you never expected.
Based on the information in the profiler report, you and your team can then begin to understand better what your application is doing and make informed refactorings to change it, as and where required.
If you’re just getting started with profiling, there are several options available for PHP. The most commonly used are:
And those are five ways to improve the quality of your PHP applications notably. Any one of them on their own will deliver you a notable performance improvement. However, when used together, you should expect to see a significant performance, not to mention quality, improvement.
August 2023: Updated to reflect current version of PHP (8) and other references.