HomeBlogThe new version of PHP: PHP 8, what is new and exciting about it?
#Engineering
Bjorn Voesten - DeveloperBjorn Voesten
November 26, 2020

The new version of PHP: PHP 8, what is new and exciting about it?

On November 26 PHP released version 8, bringing the most popular programming language for the web to a higher level. The new version contains some long-awaited exciting features that are expected of a modern programming language. On top of that, PHP now allows a new Just In Time compiling method to improve the performance. In this article, we discuss some new features, which makes them exciting, and look into where the performance improvements may apply.

New Feature of PHP 8

Most new features of PHP are long-awaited, and already there in most modern programming languages. Still, with this update PHP takes a big step forward. Here’s a list of the most exciting new features.

Union types and the mixed type

As you may know, PHP was designed as a loosely typed programming language where types didn’t play a role. But as time went by and applications got bigger and more complex there was a need for a more strict approach. Since PHP 5.0 in 2005 many types have been added:

  • class (PHP 5)
  • self (PHP 5)
  • array (PHP 5.1)
  • callable (PHP 5.4)
  • bool (PHP 7)
  • float (PHP 7)
  • int (PHP 7)
  • string (PHP 7)
  • iterable (PHP 7.1)
  • object (PHP 7.2)

This means all commonly used types are supported. The new PHP update will bring two much requested features, union types and the mixed type. 

Union types

Union types are a combination of multiple types, allowing you to accept values of multiple different types, rather than a single one. This can be very useful when a property, argument or method can hold multiple types of values, for example an int or float, as seen in the example below:

// PHP 7 
/**
 * @var int|float $number
 */ 
private $number;

// PHP 8
private int|float $number;

Mixed type

The mixed type gives the possibility to explicitly declare a mixed value to parameters, class properties, and function returns. This can come in handy when it can’t be specified more precisely or the programmer just did not find the need to specify it more precisely.

// PHP 7 
/**
 * @var mixed $number
 */ 
private $number;  

// PHP 8 
private mixed $number;

Named arguments

With named arguments, you can pass arguments to a function based on the parameter name, rather than the parameter position. This makes the arguments order-independent, the meaning self-documenting, and allows for skipping default values. Previously, PHP did not include named arguments. This made the calling of functions sometimes pretty inefficient. With PHP 8, named arguments are finally there.

Here’s an example with the commonly used function htmlspecialchars:

// PHP 7 
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);  
// PHP 8 
htmlspecialchars($string, double_encode: false);

Personally I am very enthusiastic about this feature because it can clean up code, improve readability because of the self-documentation and improves flexibility because the position of your arguments does matter less. With attributes, you can add metadata to classes, methods, or class properties. 

The idea behind attributes is not new and is supported by many languages, but was not supported by PHP yet. Symfony’s Doctrine, a popular PHP based ORM already used DocBlock “annotations”, which is another implementation for the same idea. The disadvantage of their approach was that they had to write their own parser.

Attributes will be PHP’s approach for “annotations” or “decorators” and gives you the possibility to add the metadata and extract this metadata using Reflection. As for a quick look, here's an example of what attributes look like, from the RFC:

use App\Attributes\ExampleAttribute;

#[ExampleAttribute]
class Foo
{
    #[ExampleAttribute]
    public const FOO = "foo";

    #[ExampleAttribute]
    public $x;

    #[ExampleAttribute]
    public function foo(#[ExampleAttribute] $bar)
    {
    }
}

For now we don’t see a lot of use cases for this feature, but this could of course change in the feature. But in the context of Doctrine, or maybe PHPUnit this could replace the custom DocBlock implementation, which could be really nice.

Constructor property promotion

This introduces a shorthand syntax, which allows combining the definition of properties and the constructor. This can reduce the overhead caused by declaring and assigning property definitions.

// PHP 7 
class Point {
    public float $x;
    public float $y;
    public function __construct(
        float $x = 0.0,
        float $y = 0.0,
    ) {
        $this->x = $x;
        $this->y = $y;
    }
}  

// PHP 8 
class Point {
    public function __construct(
        public float $x = 0.0,
        public float $y = 0.0,
    ) {
        //
    }
}

Types of the constructor’s arguments will be adopted by the eponymous class properties, which is really nice.

Added match expression

The match statement is a short version of the existing switch statement which uses strict instead of loose comparison. In many use cases this can make your code more readable and add type safety. For example: a switch that produces some value that is used afterwards.

// PHP 7
switch (1) {
    case 0:
    $result = 'Foo';
    break;
    case 1:
    $result = 'Bar';
    break;
    case 2:
    $result = 'Baz';
    break;
}
echo $result;

// PHP 8
echo match (1) {
    0 => 'Foo',
    1 => 'Bar',
    2 => 'Baz',
};

As you can see it is much easier to read and a lot shorter. The strict type check will also increase the safety of your code and may be used to reduce errors caused by loose type checking.

Added nullsafe operator

It is fairly common to only want to call a method or fetch a property on the result of an expression if it is not null. This can result in a deeply nested structure, but with the new nullsafe operator this problem will be resolved. 

// PHP 7
$country =  null;
if ($session !== null) {
    $user = $session->user;
    if ($user !== null) {
        $address = $user->getAddress();
        if ($address !== null) {
            $country = $address->country;
        }
    }
}  

// PHP 8 
$country = $session?->user?->getAddress()?->country;

This can make code much easier to read and reduces overhead and clutter.

Allowing ::class on objects

This isn’t a big new feature but a very nice one. It is now possible to use ::class on an object, instead of having to use get_class. The syntax of Foo\Bar::class was already possible to fetch a class name as a string, but was not possible on objects, which was a bit inconsistent.

New Stringable interface

The Stringable interface provides the possibility to type hint anything that implements the __toString() method. When your class already has the __toString() method, the interface will be automatically implemented, so no need to manually implement it everywhere.

Abstract trait method validation

The signature of abstract methods defined in traits is now checked against the implementing class method. This means that when you have a trait that contains an abstract method with the following signature:

trait T
{
    abstract public function test(): string;
}

Would be result in:

class C
{
    use T;

    public function test() // Allowed in PHP 7
    {
        //
    }

    public function test(): int // Forbidden in PHP 8
    {
        //
    }

    public function test(): string // Allowed in PHP 8
    {
        //
    }
}

Non-capturing catches

In the past, it was required to capture the exception being caught to a variable, even when you did not need it. From now it is possible to catch exceptions without capturing them to variables.

So instead of:

try {
    //
} catch (Exception $exception){ // Unused variable
    //
}

You can now do:

try {
    //
}
catch (Exception) {
    //
}

This allows you to write shorter code when the exception result is not used and keeps the code cleaner from unused variables.

Ensure correct signatures of magic methods

Currently, it is possible to write magic methods that don't match the expecting signatures, such as __clone(): float or __isset(): Closure. To many magic methods, there will be added a check which ensures that these signatures match. For example __isset(string $name): bool should receive a string type argument and should return a bool.

New str_contains, str_starts_with and str_ends_with functions

We ourselves use the Laravel framework for many projects, which already has a lot of neat functions for strings, including str_contains, str_starts_with, and str_ends_with. In my opinion, these functions should be in the language itself and luckily they will be in the future! 

Weak maps

The weak map will contain references to objects, which in contrast to normal maps will have weak references that don’t prevent the object from being garbage collected. 

Here’s the example used in the RFC:

$map = new WeakMap;
$obj = new stdClass; 
$map[$obj] = 42;
var_dump($map);
// object(WeakMap)#1 (1) {
//   [0] => array(2) {
//     ["key"] => object(stdClass)#2 (0) {
//     }
//     ["value"] => {
//         int(42)
//     }
// }

// The object is destroyed here, and the key is automatically removed from the weak map. 
unset($obj); 
var_dump($map);
// object(WeakMap)#1 (0) {
// }

This is a great feature to improve memory efficiency and may event prevent memory leaks.

Changes

Furthermore, there are some interesting changes to existing functions that might influence the working of your code, in case you want to switch from an older version to PHP 8. Here is a list of the most important changes. 

Stricter and consistent type checks

For user-defined functions, passing a parameter of illegal type results in a TypeError. For internal functions, the behavior depends on multiple factors, but the default is to throw a warning and return null. This Request for Change (RFC) proposes to consistently generate TypeError exceptions for all invalid parameter types, regardless of whether the function is user-defined or extension-defined. 

Reference: https://wiki.php.net/rfc/consistent_type_errors

Stable sorting

If multiple elements in the input array compare equal, they will always be sorted adjacently. However, if the sort is unstable, their relative order is not guaranteed and will appear to be random. A stable sort guarantees that equal elements will retain the order they had in the original array.

Reference: https://wiki.php.net/rfc/stable_sorting

Nested ternaries now require explicit parentheses

Deprecate and remove left-associativity for the ternary operator and require explicit use of parentheses instead.

Reference: https://wiki.php.net/rfc/ternary_associativity

Negative array keys added

In the current version of PHP, the behavior of the array_fill function is quite unexpected when you create an array with a negative index. In this case, the first index will be the negative value, after which the array starts counting from 0. This is solved in PHP 8, allowing you to set a negative index for an array, after which the array starts counting from the value, as you would expect.

Reference: https://wiki.php.net/rfc/negative_array_index

Allow a trailing comma in parameter and closure use list

In multiline parameter lists or closure use lists the last parameter was not allowed to have a trailing comma, which in my opinion was very inconsistent and even annoying sometimes. Luckily this will be possible in the near future.

https://wiki.php.net/rfc/trailing_comma_in_closure_use_list

Performance improvements with the PHP 8 JIT compiler

The new version of PHP comes with a Just In Time (JIT) compiler, promising to significantly increase performance. PHP JIT is implemented as an almost independent part of the already existing OPcache. But implementing the JIT Compiler does not necessarily mean better performance. That depends on the use case. To determine for which cases it is the most useful, it is good to know a little more about the inner workings.

With the JIT compiler, code will be compiled at runtime. The compiler monitors the code and identifies commonly used parts as “warm” or “hot”, depending on the usage. Hot parts are compiled as optimized code and cached, so these can be quickly reused instead of compiling the separate parts on the fly. This saves a lot of processing time, so in potential it can hugely increase the performance. 

However, it should be noted that the improvement is currently limited for web requests, as the current JIT compiler is mostly optimizing fractal computations. The average web application doesn’t use many fraction computations, making the increased performance very limited. That being said, the JIT compiler could be improved in upcoming minor releases of PHP, increasing the performance for web applications.

Here’s what the RFC says about performance:

JIT makes bench.php more than two times faster: 0.140 sec vs 0.320 sec. It is expected to make most CPU-intensive workloads run significantly faster.
According to Nikita, PHP-Parser became ~1.3 times faster with JIT. Amphp hello-world.php got just 5% speedup.
However, like the previous attempts - it currently doesn't seem to significantly improve real-life apps like WordPress (with opcache.jit=1235 326 req/sec vs 315 req/sec).
It's planned to provide additional effort, improving JIT for real-life apps, using profiling and speculative optimizations.

For now, the new JIT compiler opens up PHP for new areas. If your application does use these computations, the performance is significantly improved. This increased the potential for PHP as a programming language when complex computations are required, like in data science applications and machine learning.

Breaking Changes

Because this version is a major release it brings breaking changes. 

We don't expect upgrading will be a big problem for applications which don’t deviate a lot from the current standards.

All of the breaking changes can be found in the upgrade section of the change log.

Conclusion of PHP 8

First of all, this release will bring a lot more consistency to the language, which will redress a big problem that the language had in the past. Along with improved type checking and some long-awaited features will this be a great foundation for the feature of the language.

While the new Just In Time compiler doesn’t significantly increase the performance of web applications (for now), it opens up PHP for different use cases that heavily rely on calculations, such as data science applications and machine learning.

Should I upgrade to PHP 8?

If you are planning a lot of development the new changes could be very helpful. Your code could be made more future proof and maintainable. And if your application is relying on complex calculations, the performance might hugely increase. However, this is unlikely for most existing web applications.

We expect that it’s worth upgrading when you will be actively developing or maintaining existing software in the future. For new applications, we recommend using PHP 8 from the start as the new features and changes allow for more efficient and fun programming.

References

You might find this interesting:

Don't let a missing strategy ruin your project. Prevent expensive software never to gain adoption and check if your idea is ready for development. With our Digital Readiness Scan you'll know your next step on the road to succes within 5 questions.

Do the scan