PHP 7.4, the next PHP 7 minor release, has been released for General Availability as of November 28th, 2019. So it’s time for us to dive into some of the most exciting additions and new features that will have made PHP faster and more reliable.

Update: PHP 8.1 (official release) is now available to all Kinsta clients. PHP 7.4 is no longer supported at Kinsta. Please note that we support PHP versions 8.0, 8.1, 8.2 and 8.3.

Even if PHP 7.4 significantly boosts performance and improves code readability, PHP 8 will be the real milestone for PHP performance, as the proposal for JIT inclusion has already been approved.

Anyway, today we’re going through some of the most interesting features and changes we’re expecting with PHP 7.4. Just for the record, these were the important dates for the 7.4 version:

  • June 6th, 2019: PHP 7.4 Alpha 1
  • July 18th, 2019: PHP 7.4 Beta 1 – Feature freeze
  • November 28th, 2019: PHP 7.4 GA Release

You can check out the full list of features and additions on the official RFC page.

What’s New in PHP with PHP 7.4?

In this post we’re covering several changes and features that should be added to the language with the final release of PHP 7.4:

Forget array_merge: PHP 7.4 Brings Spread Operator in Array Expression

Available since PHP 5.6, argument unpacking is a syntax for unpacking arrays and Traversables into argument lists. To unpack an array or a Traversable, it has to be prepended by … (3 dots), as shown in the following example:

function test(...$args) { var_dump($args); }
test(1, 2, 3);

Now this PHP 7.4 RFC proposes to extend this feature to array definitions:

$arr = [...$args];

The first declared benefit of Spread Operator in array expression is all about performance. In fact, the RFC doc states:

Spread operator should have better performance than array_merge. That’s not only because the spread operator is a language structure while array_merge is a function, but also because compile time optimization can be performant for constant arrays.

A significant advantage of Spread operator is that it supports any traversable objects, while the array_merge function only supports arrays.

Here is an example of argument unpacking in array expression:

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
var_dump($fruits);

If you run this code with in PHP 7.3 or earlier, PHP throws a Parse error:

Parse error: syntax error, unexpected '...' (T_ELLIPSIS), expecting ']' in /app/spread-operator.php on line 3

Instead, PHP 7.4 would return an array:

array(5) {
	[0]=>
	string(6) "banana"
	[1]=>
	string(6) "orange"
	[2]=>
	string(5) "apple"
	[3]=>
	string(4) "pear"
	[4]=>
	string(10) "watermelon"
}

The RFC states that we can expand the same array multiple times. Moreover, we can use the Spread Operator syntax everywhere in the array, as normal elements can be added before or after the spread operator. So the following code will work as we may expect:

$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$arr3 = [...$arr1, ...$arr2];
$arr4 = [...$arr1, ...$arr3, 7, 8, 9];

It’s also possible to unpack arrays returned by a function directly into a new array:

function buildArray(){
	return ['red', 'green', 'blue'];
}
$arr1 = [...buildArray(), 'pink', 'violet', 'yellow'];

PHP 7.4 outputs the following array:

array(6) {
	[0]=>
	string(3) "red"
	[1]=>
	string(5) "green"
	[2]=>
	string(4) "blue"
	[3]=>
	string(4) "pink"
	[4]=>
	string(6) "violet"
	[5]=>
	string(6) "yellow"
}

We can also use the generator syntax:

function generator() {
	for ($i = 3; $i <= 5; $i++) {
		yield $i;
	}
}
$arr1 = [0, 1, 2, ...generator()];

But we are not allowed to unpack arrays passed by reference. Consider the following example:

$arr1 = ['red', 'green', 'blue'];
$arr2 = [...&$arr1];

If we’d try to unpack an array by reference, PHP throws the following Parse error:

Parse error: syntax error, unexpected '&' in /app/spread-operator.php on line 3

Anyway, if the elements of the first array are stored by reference, they are stored by reference in the second array, as well. Here is an example:

$arr0 = 'red';
$arr1 = [&$arr0, 'green', 'blue'];
$arr2 = ['white', ...$arr1, 'black'];

And here is what we get with PHP 7.4:

array(5) {
	[0]=>
	string(5) "white"
	[1]=>
	&string(3) "red"
	[2]=>
	string(5) "green"
	[3]=>
	string(4) "blue"
	[4]=>
	string(5) "black"
}

The Spread operator proposal passed with 43 to 1 votes.

Arrow Functions 2.0 (Short Closures)

In PHP, anonymous functions are considered to be quite verbose and difficult to implement and maintain. This RFC proposes the introduction of the shorter and clearer syntax of the arrow functions (or short closures), that should allow us to clean up significantly our PHP code.

Consider the following example:

function cube($n){
	return ($n * $n * $n);
}
$a = [1, 2, 3, 4, 5];
$b = array_map('cube', $a);
print_r($b);

PHP 7.4 allows to use a more concise syntax, and the function above could be rewritten as follows:

$a = [1, 2, 3, 4, 5];
$b = array_map(fn($n) => $n * $n * $n, $a);
print_r($b);

Currently, anonymous functions (closures) can inherit variables defined in the parent scope thanks to the use language construct, as shown below:

$factor = 10;
$calc = function($num) use($factor){
	return $num * $factor;
};

But with PHP 7.4, variables defined in the parent scope are implicitly captured by value (implicit by-value scope binding). So we can write the whole function seen above on a single line:

$factor = 10;
$calc = fn($num) => $num * $factor;

The variable defined in the parent scope can be used in the arrow function exactly as if we were using use($var), and it’s not possible to modify a variable from the parent scope.

The new syntax is a great improvement to the language as it allows us to build more readable and maintainable code. We can also use parameter and return types, default values, variable-length argument lists (variadic functions), we can pass and return by reference, etc. Finally, short closures can also be used in class methods, and they can make use of the $this variable just like regular closures.

This RFC has been approved with 51 to 8 votes, so we can expect it to be part of PHP 7.4 additions.

Null Coalescing Assignment Operator

Added with PHP 7, the coalesce operator (??) comes in handy when we need to use a ternary operator in conjunction with isset(). It returns the first operand if it exists and is not NULL. Otherwise, it returns the second operand. Here is an example:

$username = $_GET['user'] ?? ‘nobody';

What this code does is pretty straightforward: it fetches the request parameter and sets a default value if it doesn’t exist. The meaning of that line is clear, but what if we had much longer variable names as in this example from the RFC?

$this->request->data['comments']['user_id'] = $this->request->data['comments']['user_id'] ?? 'value';

In the long run, this code could be a bit difficult to maintain. So, aiming to help developers to write more intuitive code, this RFC proposes the introduction of the null coalescing assignment operator (??=). So, instead of writing the previous code, we could write the following:

$this->request->data['comments']['user_id'] ??= ‘value’;

If the value of the left-hand parameter is null, then the value of the right-hand parameter is used.
Note that, while the coalesce operator is a comparison operator, ??= is an assignment operator.

This proposal has been approved with 37 to 4 votes.

Typed Properties 2.0

Argument type declarations, or type hints, allow to specify the type of a variable that is expected to be passed to a function or a class method. Type hints are a feature available since PHP 5, and since PHP 7.2 we can use them with the object data type. Now PHP 7.4 brings type hinting a step forward by adding support for first-class property type declarations. Here is a very basic example:

class User {
	public int $id;
	public string $name;
}

All types are supported, with the exception of void and callable:

public int $scalarType;
protected ClassName $classType;
private ?ClassName $nullableClassType;

The RFC explains the reason why void and callable are not supported:

The void type is not supported, because it is not useful and has unclear semantics.

The callable type is not supported, because its behavior is context dependent.

So we can safely use bool, int, float, string, array, object, iterable, self, parent, any class or interface name, and nullable types (?type).

Types can be used on static properties:

public static iterable $staticProp;

They are also allowed with the var notation:

var bool $flag;

It’s possible to set default property values, which of course must match the declared property type, but only nullable properties can have a default null value:

public string $str = "foo";
public ?string $nullableStr = null;

The same type applies to all properties in a single declaration:

public float $x, $y;

What happens if we make an error on the property type? Consider the following code:

class User {
	public int $id;
	public string $name;
}

$user = new User;
$user->id = 10;
$user->name = [];

In the code above, we declared a string property type, but we set an array as property value. In such scenario, we get the following Fatal error:

Fatal error: Uncaught TypeError: Typed property User::$name must be string, array used in /app/types.php:9

This RFC has been approved with 70 to 1 votes.

Weak References

With this RFC, PHP 7.4 introduces the WeakReference class, which allows programmers to retain a reference to an object that doesn’t prevent the object itself from being destroyed.

Currently, PHP supports Weak References by using an extention like pecl-weakref. Anyway, the new API is different from the documented WeakRef class.

Here is an example from the author of this proposal, Nikita Popov:

$object = new stdClass;
$weakRef = WeakReference::create($object);

var_dump($weakRef->get());
unset($object);
var_dump($weakRef->get());

The first var_dump prints object(stdClass)#1 (0) {}, while the second var_dump prints NULL, as the referenced object has been destroyed.

This RFC passed with 28 to 5 votes.

Covariant Returns and Contravariant Parameters

Variance is a property of class hierarchies describing how the types of a type constructor affect subtypes. In general, a type constructor can be:

  • Invariant: if the type of the super-type constrain the type of the subtype.
  • Covariant: if the ordering of types is preserved (types are ordered from more specific to more generic).
  • Contravariant: if it reverses the order (types are ordered from more generic to more specific).

Currently, PHP has mostly invariant parameter and return types, with few exceptions. This RFC proposes to allow covariance and contravariance on parameter types and return types, also providing several examples of code.

Here is an example of covariant return type:

interface Factory {
	function make(): object;
}

class UserFactory implements Factory {
	function make(): User;
}

And here is an example of contravariant parameter type:

interface Concatable {
	function concat(Iterator $input); 
}
 
class Collection implements Concatable {
	// accepts all iterables, not just Iterator
	function concat(iterable $input) {/* . . . */}
}

See the RFC for a closer look at covariance and contravariance in PHP 7.4.

This RFC passed with 39 to 1 votes.

Preloading

This proposal from Dmitry Stogov is one of our favorite because it should bring a significant boost in performance. Preloading is the process of loading libraries and frameworks into the OPCache at module initialization (read more about PHP lifecycle).

PHP lifecycle
PHP lifecycle (Image source: PHP Internals)

Here is how preloading works in the words of Dmitry:

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.

These files are loaded on server startup, are executed before any application and stay available for any future requests. That’s great in terms of performance.

Preloading is controlled by a specific php.ini directive: opcache.preload. This directive specifies a PHP script to be compiled and executed at server start-up. This file can be used to preload additional files, either including them or via the opcache_compile_file() function (read more on PHP documentation).

But there’s a downside. In fact, the RFC explicitly states:

preloaded files remain cached in opcache memory forever. Modification of their corresponding source files won’t have any effect without another server restart.

However, all functions defined in preloaded files will be permanently loaded into PHP function and class tables, and remain available for every future request. This will lead to good performance improvements, even if these improvements could be considerably variable.

You can read more about the limitations and exceptions of preloading on the official Preloading RFC page.

New Custom Object Serialization Mechanism

This is another proposal from Nikita Popov approved with a large majority of votes.

Currently, we have two different mechanisms for custom serialization of objects in PHP:

  • The __sleep() and __wakeup() magic methods
  • The Serializable interface

According to Nikita, both these options have issues that lead to complex and unreliable code. You can dive deep into this topic in the RFC. Here I just mention that the new serialization mechanism should prevent these issues by providing two new magic methods, __serialize() and __unserialize(), that combine the two existing mechanisms.

This proposal passed with 20 to 7 votes.

Deprecations

The following functions/functionalities will be deprecated with PHP 7.4. For a more comprehensive list of deprecations, check out PHP 7.4 Upgrade Notes.

Change the Precedence of the Concatenation Operator

Currently, in PHP the “+” and “-” arithmetic operators, and the “.” string operator are left associative and have the same precedence (read more about Operator Precedence).

As an example, consider the following line:

echo "sum: " . $a + $b;

In PHP 7.3 this code produces the following warning:

Warning: A non-numeric value encountered in /app/types.php on line 4

This because the concatenation is evaluated from left to right. It’s the same as writing the following code:

echo ("sum: " . $a) + $b;

This RFC proposes to change the precedence of operators, giving “.” a lower precedence than “+” and “-” operators, so that additions and subtractions would always be performed before the string concatenation. That line of code should be equivalent to the following:

echo "sum: " . ($a + $b);

This is a two-step proposal:

  • Starting from version 7.4, PHP should emit a deprecation notice when encountering an unparenthesized expression with “+”, “-” and “.”.
  • The actual change of precedence of these operators should be added with PHP 8.

Both proposals have been approved with a large majority of votes.

Deprecate left-associative ternary operator

In PHP the ternary operator, unlike many other languages, is left-associative. According to Nikita Popof, this can be confusing for programmers who switch between different languages.

Currently, in PHP the following code is correct:

$b = $a == 1 ? 'one' : $a == 2 ? 'two' : $a == 3 ? 'three' : 'other';

It’s interpreted as:

$b = (($a == 1 ? 'one' : $a == 2) ? 'two' : $a == 3) ? 'three' : 'other';

And this could lead to errors because it may not be what we intend to do. So this RFC proposes to deprecate and remove the use of left-associativity for ternary operators and force developers to use parentheses.

This is another two-step proposal:

  • Starting from PHP 7.4, nested ternaries without explicit use of parentheses will throw a deprecation warning.
  • Starting from PHP 8.0, there will be a compile runtime error.

This proposal has been approved with 35 to 10 votes.

What Does PHP 7.4 Mean for WordPress Users?

PHP is the most widely used server-side programming language on the web. According to W3Techs, as of December 2nd, 2019, PHP usage is used by 78.9% of all the websites whose server-side programming language they can detect.

PHP usage (December 2019)
PHP usage (December 2019)

Unfortunately, PHP 5 is still used by 44.0% of all websites with a known server-side programming language. If you add the number of users still using PHP 7.0 and 7.1, it turns out that a large majority of websites are running unsupported versions of PHP.

Supported PHP Versions for WordPress
Supported PHP versions (Image source: Supported Versions)

According to the official WordPress Stats page, as of writing this, a whopping 64% of all WordPress websites are running unsupported versions of PHP. Only a little over 13% are using PHP 7.3. And the latest version, PHP 7.4, doesn’t even show up yet. You can see that a large majority of users, over 23%, are still running on PHP 5.6.

WordPress PHP version Stats
WordPress PHP version Stats

We highly recommend asking your host for a supported version of PHP, preferably according to WordPress official requirements. As of this writing, May 2019, WordPress requires:

  • PHP version 7.3 or greater.
  • MySQL version 5.6 or greater OR MariaDB version 10.1 or greater.
  • HTTPS support

PHP 7 Performance

The numbers above are especially discouraging coming from a performance point of view, as PHP 7 has shown to be significantly faster. Here are a few stats:

  • Official PHP benchmarks show that PHP 7 allows the system to execute twice as many requests per second in comparison with the PHP 5.6, at almost half of the latency.
  • Christian Vigh also published a PHP performance comparison in which he found that PHP 5.2 was 400% slower than PHP 7.
  • Andrei Avram saw faster execution times and less memory usage in PHP 7.4 verses that of PHP 7.3
  • Phoronix ran some early benchmark tests with PHP 7.4 Alpha and saw that it was slightly faster than PHP 7.3.

We ran our own PHP performance benchmarks with PHP 7.3. We saw that WordPress 5.0 on PHP 7.3 could execute almost three times as many transactions (requests) per second as compared to PHP 5.6. We’ll be releasing PHP 7.4 benchmarks soon!

WordPress 5.0 PHP benchmarks
WordPress 5.0 PHP benchmarks
  • WordPress 5.0 PHP 5.6 benchmark: 91.64 req/sec
  • WordPress 5.0 PHP 7.0 benchmark results: 206.71 req/sec
  • WordPress 5.0 PHP 7.1 benchmark results: 210.98 req/sec
  • WordPress 5.0 PHP 7.2 benchmark results: 229.18 req/sec 
  • WordPress 5.0 PHP 7.3 benchmark results: 253.20 req/sec 🏆

Many are slow to update simply because of the time involved with testing new all their third-party plugins and themes to ensure they function properly. But a lot of times, it comes down to they simply haven’t done it yet.

Checking Your PHP Version

Not sure what version of PHP you’re running? One of the easiest ways to check is to use a tool like Pingdom or Google Chrome Devtools. The first HTTP request header will typically show you the version.

Check PHP version in Pingdom
Check PHP version in Pingdom

This relies on the host not modifying the X-Powered-By header value. However, many do because of security reasons (including Kinsta). If so, you might not see your PHP version. In which case, if you’re running WordPress 5.2 or higher, there is a new Site Health tool you can use. Head over to “Tools” → “Site Health” → “Info” and under the “Server” section you’ll find your server’s PHP version.

Check PHP version with WordPress Site Health tool
Check PHP version with WordPress Site Health tool

Alternatively, you could install a free plugin like Version Info which will show you some basic server information in the footer of your WordPress admin dashboard. A few other ways to see your PHP version include uploading a file via FTP, or simply reaching out to your host and asking.

Updating to PHP 7.4

The final version of PHP 7.4 is available right now at Kinsta. Alternatively, you could test your WordPress site locally or check your scripts in an environment like Docker, which allows you to test different versions of PHP from the command line.

The easiest way to start testing is to create a staging environment as this will closely resemble your live production site. In the MyKinsta dashboard click into your site and change the environment over to staging.

Create WordPress staging environment
Create WordPress staging environment

Then under the “Tools” menu change your PHP engine over to PHP 7.4. You can then start testing to ensure the compatibility of your code, third-party plugins, and themes.

As with previous versions of PHP, our self-healing PHP feature fully supports PHP 7.4. If PHP goes down for any reason, we will automatically restart it. If the restart does not fix the issue, our monitoring system will alert our sysadmin team to investigate the cause of the problem.

Change to PHP 7.4
Change to PHP 7.4

See details regarding PHP 7.4 release at Kinsta.

Installing and Running PHP 7.4 on Docker

Want to test on Docker? Luckily, you don’t need to compile and configure PHP 7.4 manually. If you already have Docker installed on your system, you just need to install the unofficial PHP-FPM 7.4 Docker Image and run your tests from the command line in few seconds.

Installing Nginx Docker Image
Installing Nginx Docker Image

If you’d prefer to run your PHP 7.4 code in your browser, you also need to install an Nginx or Apache image. But no worries. Just follow the developer’s directions. Copy and paste the commands from the Docker Image page to your command line tool, and you’re ready to go.

Summary

In this post, we covered a good number of changes and additions that we can expect with the release of PHP 7.4. If you’re looking for the full list of features, along with the official RFC documentation, check out the following resources:

We’ll keep you updated with all the latest info regarding PHP 7.4. If you’re a Kinsta client, you can already start moving your WordPress sites to the latest version.

Are you ready to install and test the upcoming PHP features? Which one is your favorite? Share your thoughts with us in the comments below.

Carlo Daniele Kinsta

Carlo is a passionate lover of webdesign and front-end development. He has been playing with WordPress for more than 20 years, also in collaboration with Italian and European universities and educational institutions. He has written hundreds of articles and guides about WordPress, published both on Italian and international websites, as well as on printed magazines. You can find him on LinkedIn.