Programster's Blog

Tutorials focusing on Linux, programming, and open-source

What's New In PHP 8.1

Below is a list of the features coming out in PHP 8.1, in order of how useful I find them. This is not a definitive list of all changes, but a summary of the biggest and most important changes.

  1. Enums
  2. MySQLi Default Error Mode - Exceptions
  3. Readonly Properties
  4. Resources Changed To Objects
  5. New in Initializers
  6. Pure Intersection Type
  7. Array Unpacking - String Keys
  8. Closures From Callables
  9. Never Return Type
  10. Fsync and FdataSync
  11. Final Class Constants
  12. Fibers

Related Posts

Enums

Enums were brought in with PHP 8.1, and there is too much to cover here, so there is a dedicated PHP enums post.

MySQLi Default Error Mode - Exceptions  

The default error handling behavior of the MySQLi extension has changed from silencing errors to throw an Exception on errors. Now when writing queries, you may wish to catch the mysqli_sql_exception like so:

try
{
    /* @var $result mysqli_result */
    $result = $mysqliConn->query($myQuery);

    while (($row = $result->fetch_assoc()) !== null)
    {
        // do something with $row
    }
}
catch (mysqli_sql_exception)
{
    // something went wrong, handle it here.
}

Readonly Properties  

Properties of a class can be marked as readonly. This is great for your immutable value-objects! E.g.

class MyValueObject
{
    private readonly $m_foo;
    private readonly $m_bar;

    public function __construct(string $foo, string $bar)
    {
        $this->m_foo = $foo;
        $this->m_bar = $bar;
    }

    public function getFoo() : string { return $this->m_foo; }
    public function getBar() : string { return $this->m_bar; }
}

Resources Changed To Objects - Pgsql\Connection, Pgsql\Result

This probably wont appear on many people's radar, but I care about it quite a bit. More of the generic "resource" types that get returned are being converted over to actual classes/objects that can be type-hinted. For example, pg_connect will now return the Pgsql\Connection object, and when querying, one will now receive a Pgsql\Result.

These appear in the deprecations and backward compatibility breaks section of the release notes.

New in Initializers  

One can now use the new keyword in function definitions in order to create default/optional parameter objects. This is particularly useful for constructors:

class Foo
{
    private $m_logger;

    public funciton __construct(Logger $logger = new MyDefaultLogger())
    {
        $this->m_logger = $logger;
    }
}

... but it is not limited to constructors, you can do this for any function/method.

Pure Intersection Type

This is best demonstrated with code, and makes perfect sense.

You could already use the union type (|) to specify that you allow multiple different types like so:

function getSomething(string|int $key, array $inputs) 
{
    return $inputs[$key];
}

That's fine with basic types, but when one starts diving into the world of interfaces, one may wish to specify that an input matches all of the specified types. E.g.

function foo(InterfaceGetAge & InterfaceGetName $input) 
{
    print "{$input->getName()} is {$input->getAge()} years old.";
}

Check Array Is List

One can now check if an array is a list by using array_is_list. E.g.

array_is_list( ["a", "b", "c"] ); // true
array_is_list( ["1", "2", "3"] ); // true
array_is_list( [1, 2, 3] ); // true
array_is_list( [0, 1, 2, 3] ); // true
array_is_list( [0 => "1", 1 => "2", 2 => "3"] ); // true - indexes line up with normal list
array_is_list( [1 => "1", 2 => "2", 3 => "3"] ); // false - does not start at 0
array_is_list( [0 => "0", 2 => "2"] ); // false - missing index 1 (a gap)

Array Unpacking Now Supports String Keys

Array unpacking with string keys now works because it will now act like performing array_merge in that:

"If the input arrays have the same string keys, then the later value for that key will overwrite the previous one."

Below is an example to demonstrate the point:

$defaultValues = ["bar" => 3, "foo" => 2];
$userPostedFormFields = ["bar" => 4];

$myFunc = function(string $foo, string $bar) { 
    print "foo: {$foo} | bar: {$bar}";
};

$myFunc(...[...$defaultValues, ...$userPostedFormFields]); // "foo: 2 | bar: 4"

The result is because $userPostedFormFields was unpacked after $defaultValues, so the "bar" was overridden to 4. We are also making use of named arguments that came out in PHP 8, so the order of parameters doesn't matter).

Closures From Callables

One can create a closure from a callable through the use of ... like so:

function sum($param1, $param2) { return $param1 + $param2; }

// create a closure for sum, so we can pass it to other things.
$summer = sum(...); 

$value = $summer(1, 2); // 3

This would mean the following is legitimate.

function sum(...$parameters) { 
    $sum =0;

    foreach ($parameters as $parameter)
    {
        $sum += $parameter;
    }

    return $sum;
}

// create a closure for sum, so we can pass it to other things.
$summer = sum(...); 

$parameters1 = [1, 2, 3, 4];
$parameters2 = [5, 6];
$value = $summer(...$parameters1, ...$parameters2); // 21

Array Sort Example

This is more likely to be useful when it comes to sorting objects with usort and uasort. See the following example where we sort the Person object by surname.

class Person
{
    public function __construct(private readonly string $firstName, private readonly string $lastName) {}

    public static function sortBySurName(Person $a, Person $b)
    {
        return $a->lastName <=> $b->lastName;
    }
}

$people = [
    new Person("Britney", "Spears"),
    new Person("Tom", "Hanks"),
    new Person("Edwina", "Currie"),
];

usort($people, Person::sortBySurName(...)); // outputs Edwina, Tom, then Britney

New Return Type - Never

One can use the keyword never to indicate that a function will never return because it stops the execution flow by throwing an exception or a call to die() or exit() etc.

function dumper(mixed $input): never
{
    die(print_r($input, true));
}

This may seem pointless, but may help other developers understand your code, and apparently useful to static code analysers.

Fsync and FdataSync

PHP 8.1 adds the fsync and fdatasync functions to force synchronization of file changes to disk and ensure operating system write buffers have been flushed before returning.

Final Class Constants

The final keyword can be used to prevent class constants being overridden through inheritance.

class Parent
{
    public final const foo = "foo";
}

class Chile extends Parent
{
    public const foo = "bar"; // this won't work now, and will throw a fatal error.
}

Fibers

Fibers is being introduced in PHP 8.1, but it seems far less useful than I had originally thought. Hence it is at the bottom of this post. Feel free to read up about it with the following links:

References

Last updated: 13th June 2024
First published: 24th November 2021

This blog is created by Stuart Page

I'm a freelance web developer and technology consultant based in Surrey, UK, with over 10 years experience in web development, DevOps, Linux Administration, and IT solutions.

Need support with your infrastructure or web services?

Get in touch