Skip to main content
Back to writing

PHP 8.5 released

Krodox Team February 10th, 2026 PHP

PHP 8.5 arrived on 20 November 2025. Where PHP 8.4 was mostly about removing boilerplate around properties, 8.5 is about expressions: chaining them, cloning them, and catching the ones you forgot to use.

Key features in PHP 8.5

1. The pipe operator

The change people will notice first. |> passes the value on its left into the callable on its right, so a transformation reads top to bottom instead of inside out.

$slug = $title
    |> trim(...)
    |> (fn ($str) => str_replace(' ', '-', $str))
    |> (fn ($str) => str_replace('.', '', $str))
    |> strtolower(...);

The alternative was either nesting calls in reverse reading order, or inventing a temporary variable per step. Both are worse.

2. Clone with

clone can now override properties as it copies, which finally makes the "wither" pattern practical on readonly classes.

return clone($this, [
    'alpha' => $alpha,
]);

Before this, an immutable object with several readonly properties meant a constructor call restating every field just to change one. Anyone who has written a value object will recognise the amount of code this removes.

3. A built-in URI extension

PHP now parses URLs properly, following RFC 3986 and WHATWG, rather than leaving parse_url() to approximate it.

use Uri\Rfc3986\Uri;

$uri = new Uri('https://php.net/releases/8.5/en.php');

var_dump($uri->getHost());
// string(7) "php.net"

Normalisation is included, which is the part parse_url() never did and which quietly caused a lot of security bugs in userland URL comparison.

4. The #[\NoDiscard] attribute

Marks a function whose return value is meaningless to ignore, and warns when it is.

#[\NoDiscard]
function getPhpVersion(): string
{
    return 'PHP 8.5';
}

This is aimed squarely at a real class of bug: methods that return a new value rather than mutating in place, silently doing nothing when their result is dropped. Immutable APIs — including the clone with pattern above — benefit most.

5. array_first() and array_last()

Two functions that should always have existed, closing the gap left by array_find() and friends in 8.4.

$lastEvent = array_last($events);

6. Persistent cURL share handles

curl_share_init_persistent() allows DNS and connection data to be reused across requests rather than rebuilt each time.

$sh = curl_share_init_persistent([
    CURL_LOCK_DATA_DNS,
    CURL_LOCK_DATA_CONNECT,
]);

For an application making many outbound calls to the same hosts, this removes repeated DNS and TLS setup.

7. Smaller changes worth knowing

  • Fatal errors now include backtraces, which makes production diagnosis dramatically less guesswork.
  • Closures and first-class callables in constant expressions, so they can appear in attributes, constants and default values.
  • Attributes can target constants, and #[\Override] now applies to properties.
  • Static properties support asymmetric visibility, extending the 8.4 feature.
  • Closure::getCurrent() gives a closure a reference to itself, which makes recursion in closures straightforward.

Deprecations to plan for

8.5 deprecates more than 8.4 did, and these will appear as notices before they become errors:

  • The backtick operator as an alias for shell_exec()
  • Non-canonical cast names — (boolean), (integer), (double), (binary)
  • case statements terminated with a semicolon rather than a colon
  • null used as an array offset
  • __sleep() and __wakeup() are soft-deprecated

Two behaviours now emit warnings rather than passing silently: destructuring a non-array value, and casting a float to an int where precision is lost. Both were sources of quiet data corruption, so the warnings are worth reading rather than suppressing.

The disable_classes INI setting has been removed outright.

Why upgrade

  1. More readable transformations. The pipe operator changes how data-shaping code reads.
  2. Practical immutability. clone with removes the main ergonomic objection to readonly value objects.
  3. Fewer dependencies. A correct URI parser in core replaces a common third-party need.
  4. Better failure signals. Backtraces on fatal errors and #[\NoDiscard] both surface problems earlier.

Where it fits

Laravel 12 supports PHP 8.2 through 8.5, so 8.5 is already a supported target rather than something to wait on. This site runs 8.4, and the deprecation list above is the reason we have not moved yet — it is worth auditing before the notices become errors.

If you are still on 8.3 or earlier, PHP 8.4 is the more valuable jump. Property hooks and asymmetric visibility change how you write classes; 8.5 refines what you already have.