PHP 8.5 in practice: the features you will actually use
What actually landed
PHP 8.5 arrived in November 2025, and unlike some releases it is easy to look at the feature list and tell which parts will change your day and which parts are for other people. I have been running it on small projects since then. These are the four things I use.
The pipe operator
The pipe takes the value on the left and feeds it as the first argument of the call on the right. That sounds small. It is not, because string transformations are exactly where PHP code gets ugly.
$slug = strtolower(
str_replace('.', '',
str_replace(' ', '-', trim($title))
)
);
The same thing with pipes reads top to bottom, which is how you actually think about it:
$slug = $title
|> trim(...)
|> (fn($s) => str_replace(' ', '-', $s))
|> (fn($s) => str_replace('.', '', $s))
|> strtolower(...);
I reach for it in slugs, normalising input and building cache keys. Not everywhere. A pipe chain of two steps is usually worse than nesting.
clone() with new values
If you write value objects, this removes a small pile of boilerplate. Instead of a hand written withStatus() for every property, the clone syntax lets you change fields while copying.
$draft = new Article('Title', 'draft');
$review = clone $draft with { status: 'review' };
The original stays untouched. For readonly classes this is the difference between writing four methods and writing none.
array_first() and array_last()
Two functions that should have existed a decade ago. They return the first and last element of an array, or null when it is empty, and they accept a callable.
$latest = array_last($posts, fn($p) => $p->isPublished());
Before this I was writing $posts[array_key_first($posts)] ?? null. Good riddance.
The one you will be glad about at 2am
Fatal errors, including a timeout, now include a backtrace. That alone is worth the upgrade on a production box, because a timeout used to tell you the script died and nothing else. Now it tells you where it was standing when it died.
The smaller items
#[\NoDiscard]puts a warning in the log when a return value is thrown away. Useful on functions that exist only for their side effect of returning a value, like a validator.- Attributes can now be applied to constants, and
#[\Override]works on properties, so the engine will tell you when you rename something in a parent class and forget the child. - Static closures and first class callables are allowed in constant expressions, which means cleaner attribute arguments.
- OPcache is now built into the binary rather than an optional extension. Fewer deployment surprises.
Should you upgrade now
Check your dependencies first. The upgrade itself has been boring for me, which is the compliment you want here. If you are on 8.3 or 8.4 and your framework supports 8.5, there is little reason to wait, and the backtrace improvement pays for the afternoon it costs.
Need a hand with a WordPress site that keeps breaking? See what I do and how I work.