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 7.4, 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 whilearray_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.
Slides for my PHP 7.4 talk at #PHPRussia2019. Was a great conference!https://t.co/zLr9Bj2aKl
— Nikita Popov (@nikita_ppv) May 19, 2019
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).
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.
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.
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.
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.
Upgraded my @kinsta site to PHP 7.4 and it is like 10x faster. Woot!
— Ronald Huereca (@RonaldHuereca) December 2, 2019
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 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.
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.
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.
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.
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.
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.
Thanks for the summary.
Claiming that new features will make code more readable and maintainable represent subjective opinions or speculation. What objective measures of readability or maintainability do you refer to when making such claims? I thought so.
In the arrow functions section the first example shows a named function, not an anonymous function, and not a closure. The text uses the term “inherits” to describe capture, different concepts.
Hey Greg! Thanks for your comment. Regarding readability and maintainability, yes, part of this definitely comes down to a coders preference. However, each version of PHP has continued to show improvements, especially in terms of performance.
We can confirm that the example function above is a named function. And regarding inherit/capture, you can see the official documentation uses inherit instead of “capture.” https://www.php.net/manual/en/functions.anonymous.php
If the new release of PHP is set for Nov 2019, when will it be implemented across sites hosted Kinsta?
Hey Cathy!
So we don’t have an ETA yet because the final release will need to be tested by our sysadmin team.
PHP 7.3 came out on December 6th, 2018 and we released it on December 12th, 2018 (within a week of its release). https://kinsta.com/changelog/php-7-3/
So once PHP 7.4 is out and tested, we’ll be working to get it out as soon as possible.
Thank you for the great summary.
I guess they are trying to make php like ES6 syntax…
Wow.. The first 3 RFC gonna be super useful. I think one of my script gonna have approx 100 lines reduced. Awesome..!
people are scared to go from php56 -> php7x due to breaking changes and completely lost in the new syntax and features. php community surely blundered this language, you can just tell by how many people are still on php56. also, have you ever tried installing php on your own server? building it with the right extensions and tying it in with your webserver? It’s a nightmare.
You can hire a developer anytime and fix your site for a few $$. The problem is not with PHP 7, people simply don’t want to spend time and money keeping their sites secure and up to date. PHP 7.4 is faster than ever and being “scared” because something might brake is not a good excuse to stay on 5.6 which is not supported at all anymore.