PHP 8.5 for Laravel apps: what earns a place, what to check first
PHP 8.5 came out on 20 November 2025, and a release announcement lists features; it does not tell you which of them belong in a Laravel codebase. Some of them do change how app code reads. Some solve a problem Laravel solved years ago with a helper. And a handful of deprecations are worth searching for before you move a project over. This is that sorting, feature by feature.
The ones that earn a place in app code
array_first() and array_last(). They return the first or last value of an array, or null when it is empty, so they compose with ??. The old way to get the last event was $events === [] ? null : $events[array_key_last($events)]. Inside a collection you already have first() and last(), and Arr::first() exists, but plenty of Laravel code handles plain arrays: config values, decoded JSON, request input. That is where these two help.
The pipe operator. |> passes a value through a chain of callables, left to right, with no temporary variables. php.net's own example turns a title into a slug:
$slug = $title
|> trim(...)
|> (fn($s) => str_replace(' ', '-', $s))
|> (fn($s) => str_replace('.', '', $s))
|> strtolower(...);
// ' PHP 8.5 Released ' becomes 'php-85-released'
To be honest about it: in a Laravel app you would write Str::slug($title), and fluent strings and the Pipeline class already cover many chains like this. The pipe earns its place where there is no helper, for a sequence of your own small functions that you would otherwise nest or spread over four throwaway variables.
clone with. This one matters most for code that follows the rule this site's own code standards set: value objects over array bags. A readonly class is the right home for a value that crosses layers, but until 8.5 every "with" method meant copying each property by hand into a new self(...) call, or juggling get_object_vars(). Now clone takes the properties to replace:
final readonly class Color
{
public function __construct(
public int $red, public int $green, public int $blue,
public float $alpha = 1.0,
) {}
public function withAlpha(float $alpha): self
{
return clone($this, ['alpha' => $alpha]);
}
}
#[\NoDiscard]. Put it on a function and PHP warns when the return value is ignored; a (void) cast marks a call as intentionally unused. That fits result objects well: a method that returns a success-or-failure value instead of throwing is exactly the method someone calls and forgets to check.
The URI extension. It is built in and always available, and parses URLs according to RFC 3986 or the WHATWG URL standard, using uriparser and Lexbor. (new Uri\Rfc3986\Uri($url))->getHost() replaces parse_url($url)['host'], and you get a parser that follows a named standard instead of parse_url()'s own rules. Anywhere you check a host before redirecting, or compare a webhook URL against an allowed list, that is worth the change.
The rest of the list, persistent cURL share handles, closures in constant expressions, backtraces on fatal errors such as a timeout, attributes on constants, is useful but less likely to change everyday controller and service code.
What to check before upgrading
None of these is a reason to wait; they are a list to search for. Use of null as an array offset, or in array_key_exists(), is deprecated, which in Laravel code often hides in a lookup whose key can be null. The non-canonical casts (integer), (boolean), (double) and (binary) are deprecated, as is the backtick operator and a case ending in a semicolon. __sleep() and __wakeup() are soft-deprecated in favour of __serialize() and __unserialize(): check your own classes that get serialised into queue payloads or the cache. PHP now warns when a float that cannot be represented is cast to int, when NAN is cast, and when you destructure something that is neither an array nor null. On the server side, the disable_classes ini setting is gone.
This site is a fair example of where most projects sit. It runs Laravel 13.35.0, and its composer.json requires PHP ^8.4.1. PHP 8.5 satisfies that constraint, but 8.4 still does too, so none of the 8.5 syntax above can go into this code until the constraint is raised.
A concrete first step for a Laravel project: search the codebase for __sleep, __wakeup, (integer), (boolean) and backticks, then run the tests on 8.5. After that, the first readonly class you touch is a good place to try clone with. Source: php.net, PHP 8.5 release announcement.
Notes from readers