New PHP 8 language features that you may have missed
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 was released on 26 November 2020. It’s no longer actively supported, but still receives security support until 26 November 2023.
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:
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 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:
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 thematch
expression returns a value. Nobreak
statements are needed. -
match
uses strict identity checks (===
), whereasswitch
uses weak equality checks (==
).
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:
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.
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.
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 ofget_class($object)
to retrieve the name of a class. -
str_contains()
,str_starts_with()
, andstr_ends_with()
have finally made their way into the standard library.
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 enum
s 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 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:
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.
Objects can now be used as default parameter values, static variables, and global constants, as well as in attribute arguments.
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
:
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()
.
Class constants can now be declared final so that they cannot be overridden in child classes.
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 now also works for arrays with string keys:
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 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.
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 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.
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.
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.
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.
The creation of dynamic properties is now deprecated. This should help avoid
mistakes and typos. Dynamic properties are still allowed on stdClass
objects.
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()
andstrtoupper()
are no longer locale-sensitive.
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.
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:
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:
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!
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:
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()
.
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.
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:
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.