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.
- Enums
- MySQLi Default Error Mode - Exceptions
- Readonly Properties
- Resources Changed To Objects
- New in Initializers
- Pure Intersection Type
- Array Unpacking - String Keys
- Closures From Callables
- Never Return Type
- Fsync and FdataSync
- Final Class Constants
- 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
- php.net - PHP 8.1 Released!
- Stitcher - What's new in PHP 8.1
- Stitcher - PHP Enums
- PHP.watch - PHP 8.1: What's New and Changed
- PHP.watch - PHP 8.1: MySQLi: Default error mode set to exceptions
First published: 24th November 2021