“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


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


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 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


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


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


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


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


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


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…


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


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.



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


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


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


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

Pure intersection types


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


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


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

Explicit octal numerical notation


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



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


Array unpacking now also works for arrays with string keys:

Also in this version…


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


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


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


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


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


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


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


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…


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.