What’s new in PHP 8

PHP 8 has been finally released on November 26, 2020. The new major version of the language contains a long-awaited JIT compiler and some new features (union types, attributes, constructor property promotion, etc.). Additionally, authors and contributors of PHP 8 took care of unifying exceptions and warnings in internal functions and made a lot of minor improvements.

JIT compiler

Authors of PHP announced their plans regarding JIT compiler many years ago. It was probably the most anticipated and desired feature for PHP community. It’s also the biggest improvement comparing to the previous version of the language.

JIT (Just-In-Time) compiler is intended to increase the performance of PHP applications. It is possible by compiling PHP code into machine code before the execution. Therefore, some intermediate steps can be skipped. Unfortunately, this is only theoretically. Most of the benchmarks clearly show that the JIT compiler can highly increase the performance of long-running processes and complex calculations but its impact on web applications is almost negligible. However, work on improving this aspect is still ongoing.

Constructor property promotion

This feature is, in fact, only a new syntax for object constructors. It allows us to reduce the number of lines of code required for declaration and initialization of the object’s properties. That is how it looks in PHP 7 and PHP 8:

// PHP 7 
class Car 
{
    private string $brand;
    private string $model;
    private int $age;

    public function __construct(string $brand, string $model, int $age) 
    {
        $this->brand = $brand;
        $this->model = $model;
        $this->age = $age;
    }
}

// PHP 8
class Car 
{
    public function __construct(
        private string $brand, 
        private string $model, 
        private int $age
    ) {}
}

By declaring properties directly in the constructor, we can completely skip declarations at the beginning of a class and assignments in a constructor.

Named arguments

This feature allows us to provide parameters to a function in a custom order or skip some optional parameters. Let’s see some examples based on internal PHP function htmlspecialchars(). Its declaration looks like this:

htmlspecialchars(
    string $string, 
    int $flags = ENT_COMPAT | ENT_HTML401,
    string $encoding = ini_get("default_charset"),
    bool $double_encode = true
): string

As you can see, only the first parameter is required. Other ones have default values, so they are optional. However, if we want to pass a custom argument $double_encode in PHP 7, we can not skip any of the previous ones, even if default values satisfy us.

// PHP 7

// Optional parameters can be skipped
echo htmlspecialchars($string);

// If we want to pass the last argument, you have to specify all of them 
echo htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

In PHP 8, we can pass arguments in any order or completely skip some of them. We just need to precede the argument with the name of the parameter. We don’t have to use parameter name if we pass parameters in the right order. Let’s see an example written in PHP 8:

// PHP 8

// By using named parameter, we are able to skip 2nd and 3rd parameter
echo htmlspecialchars($string, double_encode: false);

// We can also change the order of the parameters
echo htmlspecialchars(double_encode: false, string: $string);

Union types

Since PHP 7 we can use typing (parameter type declarations and return type declarations). It was a great step forward for the language. However, that feature was only limited to singular types. In PHP 7.1, the authors of the language introduced nullable types (type preceded by a question mark). Union types feature is an improvement of that mechanism.

// PHP 7

// The function requires $param to be a string and the returned
// value to be a string or null
function test(string $param): ?string
{
    return $param;
}

In PHP 8 we can use 2 or more combined types. We can still use the nullable type with a question mark if we expect only one type. Otherwise, we need to declare null type directly like in the example below.

// PHP 8

// The function requires $param to be a string or int and the returned
// value to be a string, int or null
function test(string|int $param): string|int|null
{
    return $param;
}

There is also a new type called mixed. It’s not a part of union types RFC, but it’s associated with that feature. Parameters and return types marked as mixed can accept or return any type.

Attributes

Following the example of other programming languages like Java, C#, Python, and many others, creators of PHP 8 introduced attributes (also known as annotations or decorators in other languages). By attributes, we can add meta-data to classes, methods, and variables. That meta-data is available through Reflection API. Annotations were present in PHP projects as a part of tools like Symfony framework, Doctrine ORM, PHPDoc, etc but were not a part of the language itself and differed between tools. In PHP 8 the syntax has been unified, adjusted to the specificity of the language, and well documented. If you want to learn more about attributes, I recommend you to read this article.

Nullsafe operator

Nullsafe operator is another example of new syntax in PHP that can simplify our code and make it more readable. The new operator allows us to remove conditional statements checking whether the value of a variable is null or not. The syntax may look a little strange (?->) but it’s consistent with nullable types introduced in PHP 7.1. Let’s take a look at pretty simple examples in PHP 7 and PHP 8. Let’s suppose that we have class User with a public method getLastLoginDatetime(). The method returns either a DateTime object or null if the user didn’t log in before. If it’s not the user’s first login, we want to store the formatted date of the last visit in a variable.

// PHP 7
$lastLogin = null;
if(!is_null($user->getLastLoginDatetime())) {
    $lastLogin = $user->getLastLoginDatetime()->format('Y-m-d H:i:s');
}

// PHP 8
$lastLogin = $user->getLastLoginDatetime()?->format('Y-m-d H:i:s');

Match expression

This feature is very similar to the switch statement. Except that match is an expression which means that it returns a value that can be assigned to a variable or printed. Match expression, contrary to the switch, does not require a break statement.

// PHP 8
$status = match($statusCode) {
    0 => 'new',
    1 => 'pending',
    2 => 'accepted',
    3 => 'rejected',
    default => 'unknown'
};

// Expression above is equal to the following example with the use of switch statement
$status = null;
switch($statusCode) {
    case 0:
        $status = 'new';
        break;
    case 1:
        $status = 'pending';
        break;
    case 2:
        $status = 'accepted';
        break;
    case 3:
        $status = 'rejected';
        break;
    default:
        $status = 'unknown';
}

Conclusion

It’s not a groundbreaking update. Rather a consistent and mature successor of PHP 7. All changes and new features seem to be really thoughtful and refined in every detail. Personally, I am convinced that this is a step in the right direction.

You May Also Like

2 Comments to “What’s new in PHP 8”

  1. JIT is a big disappointment for me. I hoped for a significant performance boost. SNAFU…
    The article itself is very helpful though!

    1. Thank you!
      Yeah, I also expected more from JIT compile, but it is what it is. It’s the very first version of the JIT. Maybe we’ll get more with the next updates.

Leave a Reply

Your email address will not be published. Required fields are marked *