The latest and greatest release of the world's most widely used web development language is now in the wild. The PHP development team has removed the packing peanuts from PHP 7.4.0, the most exciting release in several years.
As you'd expect, it's also ready to deploy on Platform.sh, whether Friday or any other day. Doing so involves the highly difficult task of replacing a "3" with a "4" in your .platform.app.yaml
file:
type: "php:7.4"
Update your .platform.app.yaml
file, push it to a new branch, and try it out. If everything seems to be in good working order, you're clear to merge that to your master
branch. Congratulations, you're now running the latest version of PHP.
So what's new in PHP 7.4?
PHP 7.4 has a host of additions and changes, more than we have space to go into here. The most notable for developers include:
- Typed properties in objects: Class members can now have an explicit type attached, which prevents them from being assigned any other value.
class Employee
{
protected Employee $manager = null;
protected string $firstName;
protected string $lastName;
protected int $employeeId;
public function __construct(int $id, string $first, string $last)
{
$this->employeeId = $id;
$this->firstName = $first;
$this->lastName = $last;
}
}
- Short-lambda syntax: Similar to parallel constructs in Javascript, Python, or Rust, single-line anonymous functions can now be written with a more compact, single-line statement. The following two statements are exactly identical:
$arr = [1, 2, 3, 4, 5];
$factor = 5;
array_map(function(int $item) use ($factor) {
return $item * $factor;
}, $arr);
array_map(fn($x) => $x * $factor, $arr);
- Null coalesce assignment: PHP 7.0 brought the "null coalesce" operator,
??
, which returns the left side if it's defined and not null, otherwise the right side. That greatly reduces boilerplate typing, but still requires some duplication in common scenarios where you want to assign a default value. In PHP 7.4, that can be shortened even further, like so:
// This one liner...
$config['some']['key'] ??= $default;
// ... is the same as this mess
If (is_null($config['some']['key'])) {
$config['some']['key'] = $default;
}
Preloading: the perfect feature for Platform.sh
From a Platform.sh point of view, though, the most exciting feature in PHP 7.4 is Opcache preloading. Under normal circumstances, PHP executes by re-including every needed file on each request. The contents of the file are compiled once to opcodes (a very low-level platform agnostic format, similar to Java bytecode) and cached in memory, saving an enormous amount of time. On each request, however, the cached file still needs to be reloaded from the cache into the process memory, connected with any parent classes, and so on. That still involves triggering the autoload process, too. For large applications that time adds up, despite being largely wasted as the code doesn't change.
With opcache preloading, you can specify a special script to run once when PHP-FPM starts up. That script can specify certain files to be loaded into shared memory once, compiled, linked, and made available instantly to all processes. They're just as available as functions and classes that ship with PHP itself; there's no re-linking cost, no autoload executed, and the memory is used only once for all processes.
The larger your application, the bigger a deal that is. The one catch is that those files will not be reloaded from disk without restarting PHP-FPM. That would be a problem in a development environment, or an old-style shared host, but if you're using a production environment with a read-only file system (which you should be) that restarts PHP-FPM on a new deploy anyway (which you should), then there is no downside at all. And guess what hosting system works exactly that way? (Hint: It's Platform.sh.)
To leverage opcache preloading, first set the new php.ini
value opcache.preload
to a file that will run once on startup and specify which files to preload. On Platform.sh the easiest way to do that is via the .platform.app.yaml
file:
variables:
php:
opcache.preload: "preload.php"
The opcache.preload
value will be evaluated relative to the application root. Then preload.php
can look something like this:
$directory = new RecursiveDirectoryIterator(getenv('PLATFORM_APP_DIR') . '/vendor');
$iterator = new RecursiveIteratorIterator($directory);
$regex = new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
foreach ($regex as $key => $file) {
// This is the important part!
opcache_compile_file($file[0]);
}
This example will load all PHP files in the vendor/
directory into the preload cache, where they will be available to all requests with zero additional overhead.
How much of your application should be preloaded is an open question. The PHP community in general is still experimenting to figure out an optimal configuration, or if "include everything all the time" is close enough to optimal to be the best approach. To Platform.sh, it doesn't matter. Whatever preloading strategy you want to include in your application will work just the same for us. Overall though, it boils down to "free performance upgrade"— tailor-made for a setup like Platform.sh. We call that a win.
PHP 7.4 deprecations
As usual, PHP 7.4 does have a few deprecations. Most of them won’t impact most users, but it's worth reviewing the list in case you need to make any adjustments.
As one additional Platform.sh-specific note: starting with PHP 7.4, we’re no longer including the redis
PECL extension with the PHP application image. Unfortunately, that library has had some unfortunate API breaks recently that make it much harder for us to support reliably. Instead, we recommend users install the predis
package, which will work across all supported PHP versions.
Speaking of supported, be aware that the release of PHP 7.4 means that PHP 7.1 is now out of support, and PHP 7.2 moves down to security-only support. If you're still running one of those versions (or even older), now is an excellent time to upgrade your application. (Instructions at the top of this blog post.)
Happy deploying!