Articles

PHP 8.5 Released What’s New Features and Improvements

PHP 8.5 What's New Features and Improvements

On November 20, 2025, the PHP development team officially released PHP 8.5, marking a significant step forward in the language’s evolution. This version brings a suite of new features designed to improve expressiveness, readability, performance, and safety.

 

 

 

Key Features and Improvements

  • URI Extension
  • Pipe Operator
  • Clone With
  • #[\NoDiscard] Attribute
  • Closures in Constant Expressions
  • Recusion in Closures
  • New Array functions array_first() and array_last()

 

– New URI Extension

A major addition in PHP 8.5 is the built-in URI extension, which provides a robust API to parse, normalize, and manipulate URIs following RFC 3986 and WHATWG URL standards.

This extension is now ships natively, providing a safer, more standard-compliant way to work with URLs in your code.

Usage looks like:

use Uri\Rfc3986\Uri;

$uri = new Uri('https://example.com/path?query=php');
echo $uri->getHost();     // "example.com"
echo $uri->getScheme();   // "https"

 

– Pipe Expression

The pipe operator (|>). This operator makes it much easier to compose chained function calls in a left-to-right, readable manner, eliminating deeply nested calls and minimizing the use of intermediate variables.

Previously:

$output = strtolower(
    str_replace(['.', '/', '…'], '',
        str_replace(' ', '-',
            trim($input)
        )
    )
);

With the pipe operator:

$output = $input
    |> trim(...)
    |> (fn (string $s) => str_replace(' ', '-', $s))
    |> (fn (string $s) => str_replace(['.', '/', '…'], '', $s))
    |> strtolower(...);

 

Clone With

In PHP 8.5 the clone() function that accepts an array of property overrides, allowing you to create a modified clone of an object in a more declarative and concise way.

Before PHP 8.5:

readonly class Color
{
    public function __construct(
        public int $red,
        public int $green,
        public int $blue,
        public int $alpha = 255,
    ) {}

    public function withAlpha(int $alpha): self
    {
        $values = get_object_vars($this);
        $values['alpha'] = $alpha;

        return new self(...$values);
    }
}

$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);

PHP 8.5:

readonly class Color
{
    public function __construct(
        public int $red,
        public int $green,
        public int $blue,
        public int $alpha = 255,
    ) {}

    public function withAlpha(int $alpha): self
    {
        return clone($this, [
            'alpha' => $alpha,
        ]);
    }
}

$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);

 

– #[\NoDiscard] Attribute

Most of the time we call functions directly without consuming it’s return value. In PHP 8.5 when marking a function with the #[\NoDiscard] attribute PHP will check whether the returned value is consumed and emit a warning if it is not:

#[NoDiscard("you must use this return value.")]
function helloWorld(): string {
    return 'Hello World';
}

helloWorld();
// Warning:
// The return value of function helloWorld() is expected to be consumed,
// you must use this return value.

The associated (void) cast can be used to indicate that a value is intentionally unused.

(void) helloWorld();  // explicitly discards without warning

 

Closures in Constant Expressions

PHP 8.5 now allows static closures and first-class callables to be used inside constant expressions — for instance, as attribute arguments or default values.

#[SomeAttribute(static function (Container $c): bool {
    return $c->get(Service::class) !== null;
})]
class MyClass { … }

 

Recursion in Closures

PHP 8.5 provides the static Closure::getCurrent() method that refers to the currently execution closure, which is helpful when the need to do recursive call inside the closure:

$countdown = function (int $number) {
    if ($number <= 0) {
        echo "Done\n";
        return;
    }

   echo $number . "\n";
   
   $f = Closure::getCurrent();
   
   $f($number - 1);
};


$countdown(10);

 

– New array_first and array_last functions

New additions to the array functions in PHP 7.3 array_key_first() and array_key_last() to retrieve the first and last array keys. Now with PHP 8.5 includes built-in array_first() and array_last() functions to retrieve the first and last array values.

$arr = ['red', 'blue', 'green', 'black'];

$first = array_first($arr);     // red
$last  = array_last($arr);      // black

 

Persistent cURL Share Handles

Through curl_share_init_persistent(), PHP 8.5 allows cURL share handles to persist across requests.

Example from php.net:

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

$ch = curl_init('https://php.net/');
curl_setopt($ch, CURLOPT_SHARE, $sh);

// This may now reuse the connection from an earlier SAPI request
curl_exec($ch);

 

Fatal Error Backtraces

Previously, fatal errors (e.g., timing out, memory exhaustion) would generally give only a message. With PHP 8.5, fatal errors now include backtraces, giving more context about where and how they occurred.

Fatal error: ..... in helpers.php on line 200
Stack trace:
#0 helpers.php(200): upgrade()
#1 helpers.php(210): checkPerms()
#2 helpers.php(210): getPerms()

 

Other Notable Improvements

Beyond the headline features, PHP 8.5 brings several smaller but impactful enhancements. These include:

  • Asymmetric visibility for static properties, allowing more control over who can read/write static props. PHP.net

  • Constructor property promotion for final properties, meaning you can now declare final properties directly in the constructor parameters.

  • Attributes on constants, i.e., non-class compile-time constants can now have PHP attributes.

  • A new #[\DelayedTargetValidation] attribute: this delays attribute target validation to runtime rather than compile time, useful for backward compatibility when using attributes on “invalid” targets.

  • Overridable #[\Override] on properties, not just methods.

  • New functions: get_error_handler() and get_exception_handler() to introspect current handlers.

0 0 votes
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments