To use this site please enable javascript on your browser! New Features in PHP 8.1 Release

We use cookies, as well as those from third parties, for sessions in order to make the navigation of our website easy and safe for our users. We also use cookies to obtain statistical data about the navigation of the users.

See Terms & Conditions

New Features in PHP 8.1 Release

by Bryce Andy 01:10 Oct 25 '21

As we prepare for the official release a month away, let's deep dive into all the anticipated new features of the PHP 8.1 release.

PHP 8.1
PHP 8.1 Illustration

PHP 8.1 Enums

Enumerations (enums) is a data type that has a fixed number of possible values. This is a feature that was needed for a long time in the community and it has now arrived.

If you have no idea how enums would work, imagine how you normally represent the status of an order in an e-commerce application. The status could be PENDING, CONFIRMED, SHIPPED or DELIVERED.

With enums it will be easier to read and also maintain these constant values:

use App\Enums\OrderStatus;

// Normally you would do this
if ($order->status == 2) {
    // ...
}

// But with enums
if ($order->status == OrderStatus::confirmed) {
    // ...
}

Creating enums

When defining enums, you are going to declare all the possible cases using the case keyword:

enum OrderStatus 
{
    case PENDING;
    case CONFIRMED;
    case CANCELLED;
    case SHIPPED;
    case DELIVERED;
}

This is the most basic form of an enum. Any order can be initialized with an order status:

use App\Enums\OrderStatus;

class Order
{
    public function __construct(
        public OrderStatus $status,
    ) {}
}

$order = new Order(OrderStatus::PENDING);

Since enums are represented by objects, we can make them have values and as a result this will help in serialization in the database. To do that, let's make use of backed enums (enums with values):

enum OrderStatus: int 
{
    case PENDING = 1;
    case CONFIRMED = 2;
    case CANCELLED = 3;
    case SHIPPED = 4;
    case DELIVERED = 5;
}

Now you can retrieve the value and store it in the database. For every enum, there is a value read-only property:

$valueToSerialize = OrderStatus::SHIPPED->value; // 4

Conversely, you can obtain the status from the value using the from and tryFrom methods:

$statusObject = OrderStatus::from(4); // OrderStatus::SHIPPED

$statusObject = OrderStatus::tryFrom(90); // null if the value is unknown. Unlike from, it won't throw an exception.

Methods in enums

One of the most powerful features of enums is the ability to use custom methods in them. A perfect example would be to make use of the PHP 8.0 match method to find the progress of status:

enum OrderStatus: int 
{
    case PENDING = 1;
    case CONFIRMED = 2;
    case CANCELLED = 3;
    case SHIPPED = 4;
    case DELIVERED = 5;

    public function progress(): float
    {
        return match($this)
        {
            self::PENDING => 0.1,
            self::CONFIRMED => 0.5,
            self::CANCELLED => 0,
            self::SHIPPED => 0.75,
            self::DELIVERED => 1,
        };
    }
}

Now you can find the progress of the status by chaining the progress property:

$progress = OrderStatus::SHIPPED->progress();

// Or by using the serialized value
$progress = OrderStatus::tryFrom(4)?-> progress();

Readonly Properties

In PHP 8.1 you can define class properties as readonly, meaning once they are written they can't be changed:

class User {
    public function __construct(
        public readonly string $name,
        public readonly DateTimeImmutable $birthday,
    ) {}
}

The next time you want to change the values of the name or birthday, you will encounter an error. Note, for a property to be readonly, it must have a type declared.

new in Initializers

When initializing default values, it is now possible to use the new keyword to cleanup your function definitions and arguments:

class OrderRepository
{
    // Instead of this
    public function save(array $arguments, Order $order = null)
    {
        $order ??= new Order;

        $order->fill($arguments)->save();

        return $order;
    }

    // We do this
    public function save(array $arguments, Order $order = new Order)
    {
        $order->fill($arguments)->save();

        return $order;
    }
}

Pure Intersection Types

We've already seen how union types worked in PHP 8.0, where either of the type belongs to the parameter or return value.

With intersection types, all of the declared types must belong to the parameter or return value:

public function notify(Authenticatable & Notifiable $user)
{
    // ...
}

Array Unpacking with String Keys

This is my second favorite feature in this release. After introducing array unpacking (only with numeric keys) in PHP 7.4, I found myself constantly trying to achieve the same with string keys only to realize it's still not possible.

$array1 = ["a" => 1];

$array2 = ["b" => 2];

$array = ["a" => 0, ...$array1, ...$array2];

var_dump($array); // ["a" => 1, "b" => 2] Since there are two "a" keys, the last one will stand

First Class Callable Syntax

Starting PHP 7.1, it was possible to make a function like strlen a closure using the format below:

use \Closure;

$fn = Closure::fromCallable('strlen');

$fn('hey there'); // 9

With this new syntax intoduced, we can shorten it to:

$fn = strlen(...);

$fn('hey there'); // 9

 This is not limited to function names as we can see below:

// class methods
$fn = $object->method(...);

// static methods
$fn = $object::method();

// anonymous functions
function squareRoot() {};

$fn = squareRoot(...);

never Return Type

The new never type can be used to indicate the flow of a program will stop, unlike the void which allows continuation of a program.

This is achievable if there's no return value and if an exception is thrown or program ends with exit / die terminators, otherwise PHP will throw a TypeError:

function dump($value): never
{
    dd($value);

    exit();
}

New array_is_list Fuction

This new function checks if the keys of the array are ordered numerically starting from the 0 index:

$list1 = ["a", "b", "c"];

array_is_list($list1); // true

$list2 = [1 => "a", 2 => "b", 3 => "c"];

array_is_list($list2); // false

Final Class Constants

In PHP 8.1 its is now possible to prevent overriding constants by inheritance using final constants:

class Foo
{
    final public const X = "foo";
}
 
class Bar extends Foo
{
    public const X = "bar";
    
    // Fatal error: Bar::X cannot override final constant Foo::X
}

These are some of the interesting features of the upcoming release. One thing to take note before migrating in the future are the deprecations.

Happy PHP-ing ✨

Updated 01:11 Nov 27 '21

If you like this content, please consider buying me coffee.
Thank you for your support!

Become a Patron!