Chuniversiteit logomarkChuniversiteit.nl
“Heap, Heap, Array!”

New PHP 8 language features that you may have missed

PHP has gained a lot of new language features over the years. Some of them are frankly a bit insane – but in a good way.

An elephant (PHP’s mascot) farts fire
Spicy elephart

PHP has come a long way since Rasmus Lerdorf released its first version in 1993. It used to be a pretty crazy programming language, with a mishmash of naming conventions, nonsensical equality checks, needlessly cryptic error messages (T_PAAMAYIM_NEKUDOTAYIM, anyone?), the ability to use variable variables, and many other weird things that you wouldn’t find in most other programming languages.

The bad news is that almost all that crap still exists and can be used even in 2023. The good news is that the community has spent a lot of effort over the years to turn PHP into a nice programming language.

This blog post summarises all major changes that were introduced in PHP 8.0–8.2.

PHP 8.0

Link

PHP 8.0 was released on 26 November 2020. It’s no longer actively supported, but still receives security support until 26 November 2023.

Named arguments

Link

Named arguments provide a way to explicitly pass arguments to functions based on parameter names. This can make the code more readable, especially if a function accepts many parameters. Unlike positional arguments, named arguments are order-independent, so you can pass them in any order you wish:

Attributes

Link

Attributes make it possible to annotate classes, properties, functions, methods, parameters and constants with configuration metadata. This replaces hacky PHPDoc-based solutions that had to be used in older versions of PHP:

Constructor property promotion

Link

Class constructors are often used to assign values to properties. This often means that the same variable names are repeated four times: once as class properties, once in the constructor parameter list, and twice within the constructor body.

PHP 8.0 introduces a shorthand syntax that greatly reduces the amount of boilerplate code. The following two snippets are functionally equivalent:

Union types

Link

Union types make it possible to declare variables or parameters that can hold different types of values. In previous versions union types could only be described using PHPDoc annotations, but PHP 8.0 introduces native union type declarations that are validated at runtime:

Match expression

Link

A match expression can be used to concisely handle multiple conditions by returning values based on an input. Match expressions differ from switch statements in several important ways:

  • The result of the match expression can be immediately returned or assigned to a variable.

  • match arms are evaluated one by one. As soon as an arm matches the subject expression, the evaluation stops and the match expression returns a value. No break statements are needed.

  • match uses strict identity checks (===), whereas switch uses weak equality checks (==).

Nullsafe operator

Link

The nullsafe operator (?->) makes it possible to safely access properties and methods of an object, even if the object is null. As soon as the left hand side of the nullsafe operator evaluates to null, the overall result immediately evaluates to null. In older PHP versions this would often require the use of explicit null checks:

Saner string to number comparisons

Link

When the equality operator (==) is used to compare values of different types, PHP implicitly converts one of the compared values to a different type.

In previous versions, comparisons between a number and a string would result in the string being implicitly converted to a number. Starting with PHP 8.0, only comparisons between a number and a numeric string will use numeric comparison. In all other cases, the number will be converted to a string.

Consistent type errors for internal functions

Link

Calling internal functions with invalid arguments would often cause them to fail silently, i.e. throw a warning and return null. Starting with PHP 8.0 most internal functions now throw Error exceptions regardless of whether you have enabled strict_types. This makes it easier to detect and handle errors.

Also in this version…

Link

PHP 8.0 also includes many smaller quality-of-life improvements:

  • PHP 8 introduces two JIT compilation engines which show about 3 times better performance on synthetic benchmarks and 1.5–2 times improvement on some specific long-running applications. However, performance is mostly the same as PHP 7.4 for normal web applications, so don’t get your hopes up. 🙃

  • You now have the ability to include trailing commas in parameter lists and closure use lists:

  • Non-capturing catches make it possible to omit variables for exceptions if you don’t use them:

  • throw is now an expression instead of a statement. This means that you can now do this:

  • $object::class can now be used instead of get_class($object) to retrieve the name of a class.

  • str_contains(), str_starts_with(), and str_ends_with() have finally made their way into the standard library.

PHP 8.1

Link

PHP 8.1 was released on 25 November 2021. The active support for this version ends after 25 November 2023. However, it will still receive security support until 25 November 2024.

Enumerations

Link

Until now, it was often necessary to use reflection-based third-party libraries or (ab)use class constants to define enumerations:

PHP 8.1 provides native support for enumerations, which can be used in place of a set of constants. A major benefit of enums over constants is that they can be type-checked.

A basic enumeration looks like this:

Enumerations can also be backed by a value of a certain type, which can be useful if values need to be (de)serialised:

Readonly properties

Link

Readonly properties in PHP allow you to set a value only once and prevent further modifications. This reduces the risk of accidental changes in your code. Readonly properties are especially useful for value objects and data transfer objects:

First-class callable syntax

Link

The first-class callable syntax ((...)) allows functions to be treated as data, which makes it easier to pass them into functions, store them in variables, or return them from functions.

New in initialisers

Link

Objects can now be used as default parameter values, static variables, and global constants, as well as in attribute arguments.

Pure intersection types

Link

Union types can be used to indicate that a value can be one of multiple types. Intersection types are similar, except that the value has to satisfy all type constraints. For example, in the snippet below $value must be both instanceof Iterator and Countable:

Never return type

Link

The never return type can be used to annotate functions that will not return a value and either throw an exception or end the script’s execution with a die(), exit(), trigger_error() or similar functions like dd().

Final class constants

Link

Class constants can now be declared final so that they cannot be overridden in child classes.

Explicit octal numerical notation

Link

Octal numbers can now be explicitly prefixed with 0o, which makes them easier to spot:

Fibers

Link

Fibers allow for more efficient and responsive asynchronous programming, making it easier to handle multiple tasks simultaneously without the need for traditional, resource-intensive threading.

You’re unlikely to do anything with fibers yourself, but PHP 8.1’s support for fibers should mean that if you use a (coroutine-based) concurrency library you’ll need considerably less boilerplate code to get things done.

Array unpacking support for string-keyed arrays

Link

Array unpacking now also works for arrays with string keys:

Also in this version…

Link

Most other changes that come with PHP 8.1 aren’t really worth mentioning here, except for this one: performance improvements! A benchmark with a Symfony demo app suggests that PHP 8.1 may run up to 23% faster than PHP 8.0.

PHP 8.2

Link

PHP 8.2 was released on 8 December 2022. The active support for this version ends after 8 December 2024, but it will still receive security support until 8 December 2025.

Readonly classes

Link

If you mark a class as readonly, PHP implicitly adds the readonly modifier to every declared property and prevents the creation of dynamic properties.

Disjunctive normal form (DNF) types

Link

Disjunctive normal form (DNF) types make it possible to arbitrarily combine intersection and union types, as long as intersections are grouped with brackets. This is a nice feature for those who like their code strongly-typed.

Allow null, false, and true as types

Link

Until now it was possible to use ? to indicate that a function may return null, but not that a function will always return null.

Similarly, it’s now also possible to use true and false as return types, which is useful for a function like strpos(), which either returns the position of a substring within a string as an int or false if the substring does not appear within the string.

New “Random” extension

Link

PHP 8.2 introduces a new “Random” extension that is more cryptographically secure, promotes safe coding conventions by marking built-in implementations as final, and makes it possible to store state in objects rather than in a hidden, global state.

Constants in traits

Link

Traits allow horizontal reuse of code across classes, but for some reason did not support constants yet. Now they do. Note that constant values can only be accessed from the class that uses a trait.

Deprecate dynamic properties

Link

The creation of dynamic properties is now deprecated. This should help avoid mistakes and typos. Dynamic properties are still allowed on stdClass objects.

Also in this version…

Link

PHP 8.2 also contains a few other small changes that are worth mentioning:

  • Now that dynamic properties are deprecated, you can use the #[\AllowDynamicProperties] attribute to suppress deprecation notices when you try to use dynamic properties anyway.

  • You can now annotate parameters that may contain sensitive information and that should have their values redacted when they appear in a stack trace:

  • String interpolation makes it possible to use variables within strings. The ${} notation has been deprecated, but you can still use {$foo} and $foo in your interpolated strings.

  • strtolower() and strtoupper() are no longer locale-sensitive.

PHP 8.3

Link

PHP 8.3 was released on 23 November 2023. The active support for this version ends after 23 November 2025, but it will still receive security support until 23 November 2026.

Typed class constants

Link

A lot of things in PHP can be typed, but strangely enough this wasn’t possible yet for constants. Starting with PHP 8.3, you can:

Dynamic class constant fetch

Link

Another quality-of-life improvement is the ability to retrieve constants dynamically using native PHP syntax. Previously, you had to pass a manually constructed string to the constant() function to dynamically get the value of a constant:

This becomes much easier in PHP 8.3:

New #[\Override] attribute

Link

Java was my first object-oriented language, so I’ve been annotating methods that override a parent method with /** @override */ even though it has no effect in PHP.

The new #[\Override] attribute can be added to methods to ensure that a method with the same name exists in a parent class or an implemented interface. Neat!

Deep cloning of readonly properties

Link

Read-only classes are read-only, which means that you can never modify any of their values. It turns out that this limits the usefulness of such classes a bit too much.

In PHP 8.3 you are allowed to modify readonly properties at most once when you clone an object, which makes it possible to make deep clones:

New json_validate() function

Link

PHP 8.3 introduces a json_validate() function that returns true if you pass it a string that is syntactically valid JSON. This is more efficient than using json_decode().

New Randomizer methods

Link

PHP 8.2’s Random extension has a three new methods. The first, getBytesFromString() makes it easier to generate random strings that only consist of specific bytes:

The other two are getFloat() and nextFloat(). These functions make it easier to generate unbiased random floats, which apparently can be pretty hard to implement correctly yourself.

Command line linter supports multiple files

Link

Frankly I didn’t even know that PHP has a built-in linter. Not only does it exist, it has also been improved a bit, as it can now check the syntax of multiple files at the same time:

Also in this version…

Link

PHP 8.3 also includes a number of other small improvements. Some can be useful for those who work with Document Object Models (DOMs) and calendars, while others like str_increment()… I’m not quite sure what to think about this.