PHP 8 is here! The major update was released on November 26, 2020 and brings us some fundamental changes as well as many new features. Our developer Marten explains in which cases PHP 8 really leads to better performance and whether you as a WordPress user should already update to the new version.
Introduction to PHP 8
PHP 8 was first presented to its alpha testers on June 18, 2020 and had been in a feature freeze since July. This meant that no new additions could be added until the release on November 26. A special feature of this version is that we are skipping PHP 7.5 and going straight to PHP 8. And this leap is associated with a large number of features.
One of the most common questions about PHP 8 that WordPress users probably ask is: Will PHP 8 improve the performance of my website?
The answer to this question is (as so often): "It depends..."
When PHP was updated from version 5 to version 7, there was a huge performance boost. With PHP 8, however, you won 't see an overall performance improvement unless your application computes a lot of math functions (see our section on the JIT compiler). This is mainly because PHP's code optimization is already very well established.
But who even says that performance is limited to compile time? I for one, as a developer, would measure performance in a number of ways, including the functions available to me to write well-structured code. And PHP 8 is full of new goodies. So, let's get started!
Feature updates in PHP 8
PHP 8 has eight new main features, which I would like to briefly introduce to you:
- The JIT Compiler
- Attributes
- Named Arguments
- Match Expressions
- Throw Expressions
- Static return type
- Union Type
- Mixed Types
The JIT (just-in-time) compiler(rfc)
When PHP code is executed, it is usually done by compiling to virtual instructions that are executed on a virtual machine. JIT will change this by compiling the code into x86 machine code and then executing that code directly on the CPU. For applications that rely heavily on mathematical functions, this should lead to an increase in performance. However, this is not to be expected for average web applications (see graphic).
For an example of the performance increase that can be achieved through JIT, take a look at the following video.
Basically, what it boils down to is that if you're using PHP for a labor-intensive math task, your application will run more smoothly with the new version. But if you use PHP like most WordPress users, then you won't notice any major changes. I'll explain more about what PHP 8 means for WordPress site owners below.
Let's continue with the new features:
Attributes v2(rfc, rfc)
One of the innovations in PHP 8 (which has led to extensive discussions in the PHP community) are attributes - known as "annotations" in many other languages. Attributes replace the storage of metadata with docblocks in PHP 8. Previously, you had to use them to declare metadata for classes, methods, functions and arguments in a structured way.
As you can imagine, using code comments to apply metadata was not ideal, but it worked. Fortunately, we will no longer have this problem.
Attributes can be set with the Syntax #[...]
be declared.
Here are some examples from the RFC of how attributes can be applied to different data types.
#[ExampleAttribute]
class Foo
{
#[ExampleAttribute]
public const FOO = 'foo';
#[ExampleAttribute]
public $x;
#[ExampleAttribute]
public function foo(#[ExampleAttribute] $bar) { }
}
$object = new #[ExampleAttribute] class () { };
#[ExampleAttribute]
function f1() { }
$f2 = #[ExampleAttribute] function () { };
$f3 = #[ExampleAttribute] fn () => 1;
At this point it is worth noting that the RFC for Attributes has undergone a number of changes since its original conception, which shows the effort and thought that has gone into this update.
Named Arguments(rfc)
Named arguments give you more flexibility when calling functions. Previously, you had to call a function and pass each argument in the order specified by the function.
// Using positional arguments:
array_fill(0, 100, 50);
With Named Arguments you can define a name for each parameter. And now these can be called out of sequence, as described below:
// Using named arguments:
array_fill(start_index: 0, num: 100, value: 50);
They can also be called up as follows:
array_fill(value: 50, num: 100, start_index: 0);
A hybrid of both is also possible, which allows named parameters and positional arguments to be combined, improving the readability of the code:
htmlspecialchars($string, double_encode: false);
Match Expressions(rfc)
Match Expressions are intended to solve some long-standing problems in the functionality of the predecessor Switch.
Comparison Operator
Switch uses a type-converting comparison operator (==), which can lead to problems. In contrast, Match uses a strict comparison operator (===), independent of strict_types.
Return value
Switch statements often generate a value that is required later in the program flow. It can happen that this value is not set in the switch statement, which can subsequently lead to problems in the PHP script. In addition, the syntax of the switch statement makes it difficult to read nested switch statements.
switch (1) {
case 0:
$y = 'Foo';
break;
case 1:
$y = 'Bar';
break;
case 2:
$y = 'Baz';
break;
}
echo $y;
//> Bar
The new match expression eliminates this problem by directly assigning the return value for each match branch(=>), which is more intuitive.
echo match (1) {
0 => 'Foo',
1 => 'Bar',
2 => 'Baz',
};
//> Bar
Fallthrough
If a switch statement does not have a break after each case, it will continue to the next case, even if the code should break. This was designed in such a way that the switch functions can execute several code blocks in succession. However, this has been a frequent source of errors to date.
Match has implemented an implicit break after each branch (=>). Multiple conditions can now be executed using commas between the individual conditions:
match ($x) {
1, 2 => {
// Same for 1 and 2
},
3, 4 => {
if ($x === 3) {
// Only 3
}
// Same for 3 and 4
},
}
Throw expressions(rfc)
In PHP 8, the throw statement has become an expression. This means that throw can now technically return a value. This is helpful in the sense that throw can now be used in many more places, such as the arrow functions or coalesce operators.
Arrow Functions:
$callable = fn() => throw new Exception();
Coalesce Operators:
// $value is non-nullable.
$value = $nullableValue ?? throw new InvalidArgumentException();
// $value is truthy.
$value = $falsableValue ?: throw new InvalidArgumentException();
Ternary Operators:
// $value is only set if the array is not empty.
$value = !empty($array)
? reset($array)
: throw new InvalidArgumentException();
Static return type(rfc)
As this RFC states, Static Return Type allows the return of the special class name "static" from a method: "The static special class name in PHP refers to the class a method was actually called on, even if the method is inherited. This is known as "late static binding" (LSB). This RFC proposes to make static also usable as a return type (next to the already usable self and parent types)."
In this case, however, static cannot be used as part of a parameter. The static return will refer to the class that was called.
Union Types(rfc)
Union Types allow you to declare the type of value you expect from an input. In some languages, this is called a schema. This is implemented syntactically through the use of |
(e.g. string|array|int
) are defined. But that's not where the magic ends, because you can also use defined classes such as:
class MyClass {
}
function myFunction(string|array|int|MyClass){
}
Union types are already used in PHP. However, as shown below, they are implemented using the phpdoc annotations method.
class Number {
/**
* @var int|float $number
*/
private $number;
/**
* @param int|float $number
*/
public function setNumber($number) {
$this->number = $number;
}
/**
* @return int|float
*/
public function getNumber() {
return $this->number;
}
}
For a little more context on usage, check out this example from the RFC:
class Number {
private int|float $number;
public function setNumber(int|float $number): void {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
This all boils down to the fact that you can use multiple input types for the same function instead of just one, which allows for a higher degree of code reusability.
Mixed Types(rfc)
In newer PHP versions it was possible to declare the expected type of input and return data. However, PHP did not always support types, and this is a problem. In some cases, a type was omitted or simply forgotten. MixedTypes now tries to solve this problem.
A mixed
type would be the equivalent of array|bool|callable|int|float|null|object|resource|string
Here is an example from the RFC documentation of how this is used:
// Valid example
class A
{
public function foo(int $value) {}
}
class B extends A
{
// Parameter type was widened from int to mixed, this is allowed
public function foo(mixed $value) {}
}
Additional PHP 8 features
Make Sorting Stable(rfc)
Stability is added to all functions that fall under Sort (for example.
sort, rsort, usort, asort, arsort, uasort, ksort, krsort, uksort, array_multisort).
I would recommend that you read this in the RFC documentation and compare it with your application, as changing this functionality from unstable to stable sorting could negatively affect your code.
Constructor Property Promotion(rfc)
This feature should help speed up your dev workflow and reduce errors. Currently, defining an object of values requires a set of boilerplates, as shown below from the RFC documentation:
class Point {
public float $x;
public float $y;
public float $z;
public function __construct(
float $x = 0.0,
float $y = 0.0,
float $z = 0.0,
) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
With this method, the properties must be repeated three times. The improvement to this is the short formula below:
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
Nullsafe Operator(rfc)
There is a new operator in this block!
Anstelle des klassischen <code>!== null</code> haben wir nun das bekommen: <code>?-></code>. Es scheint zunächst merkwürdig, aber wenn man es unter dem Gesichtspunkt der Verkettung von „if-Statements“ betrachtet, dann wird die Anwendung ziemlich deutlich:
$country = null;
if ($session !== null) {
$user = $session->user;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}
// do something with $country
$country = $session?->user?->getAddress()?->country;
// do something with $country
str_contains(rfc)
This rather nice new function returns a boolean value (true/false) when a string is found in another string. It takes two arguments, the string to be searched and the string to be searched for.
str_contains('php8', '8'); // true
str_contains('php8', 'wordpress'); // false
For even more useful string filters, you should take a look at the following new features:
str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true
These two will both return a Boolean result and work in the same way as str_contains()
.
Weak Maps(rfc)
If you set a variable to the value of an object in PHP, a reference to this object is normally created, but not a new object.
In this case, you can end up with many references to an object, but only one object. The problem with this is that when it's time to delete this object, PHP counts the number of references this object has. And if it's more than one, PHP will refuse to delete that object.
Weak Maps solves this problem by creating a "weak" reference to the corresponding object. As soon as the object is deleted, all variables with the Weak Maps reference to this object are set to zero.
Non-capturing Catches(rfc)
A try-catch block is already pretty awesome when it comes to error reporting and now there is an even faster way to implement this. And no, this won't really affect readability.
The "old school" way meant that you had to pass your catch exception to a variable like this:
function catchMeWhenIFall(){
try {
throw new Exception('NOPE - GRAVITY');
} catch (Exception $e) {
return $e->getMessage();
}
}
But now you no longer need to define the variable that is to be passed to your catch block.
try {
throw new Exception('FOO!');
catch (){
//Do some code here
}
}
Further PHP 8 reading material
If you would like to know more about the release of PHP 8 or review the RFC code samples yourself, just take a look at the official release announcement.
"*" indicates required fields
Are you ready for PHP 8?
No developer is a fan of updates with big changes (we remember WordPress 5.0 and Gutenberg), where there is a risk of your code breaking and hours of work or a complete rebuild waiting for you. However, if your code already worked properly with PHP 7.4, you shouldn't have any problems with PHP 8 (more on PHP and WordPress in the next chapter).
However, if you are using an older version of PHP, you should check the list of deprecated functions before updating. In the PHP "Appendices" documentation you will find a complete list of previous features, changes and problems when updating from one PHP version to the next.
Update WordPress to PHP 8?
This quote from the Yoast "WordPress and PHP 8 Compatibility Report" already suggests that you as a WordPress user should not treat the update to PHP 8 lightly. The conclusion of the report reinforces this assumption, as Omar Reiss writes: "By just investigating a subset of breaking changes in PHP 8 we could already confirm this is likely to cause major breakage on sites with unclear origin of that breakage. Oftentimes the error will occur in one place, but is caused by a plugin or theme in a different place, making these issues hard to debug."
These compatibility issues were also the reason why PHP 8 was not immediately available for our customers. Especially after major updates, it always makes sense to give WordPress plugin developers enough time to make any necessary adjustments and to wait and see. Now you can easily update your website to PHP 8 or PHP 8.1 via the Raidboxes dashboard. How to test a new PHP version at Raidboxes correctly is explained in this Helpcenter article.
Our conclusion on PHP 8
PHP 8 is a big step forward from its predecessor versions. While you may not see a dramatic improvement in performance right away (depending on how you use PHP 8), you should consider the update - at least after you've tested it, it's stable, and it's available on your WordPress host. The new version is a natural progression, and by implementing it sooner rather than later, you'll create a solid foundation for future enhancements, growth and future performance improvements.
More questions about PHP 8?
I hope this article has given you an understanding of the exciting new features of PHP 8. Do you have any further questions on this topic? Feel free to use the comment function. Want more tips on WordPress, web design and more? Then follow us on Twitter, Facebook or via our newsletter.
using javascript as a reference is a good idea to illustrate what's lacking in php thanks for the idea ! I've used it in my article about php 8 new updates as well 😉
PHP 8 is definitely impressive in terms of performance, but the code is totally new and incompatible with earlier ones. I'll not recommend WordPress users to switch to the latest updates asap.
Please wait for the first few initial weeks, let the early adopters try it and fix everything. Many hosting companies are already pushing PHP8 in their library but stick to the PHP 7.4 and WP 5.5.3 for some time.