What is new in PHP 8.5 vs PHP 8.0

Contents
Pipe operator
URI extension
Clone with properties
#[NoDiscard] attribute
Callables in constant expressions
Persistent cURL share handles
array_first() and array_last()
Smaller improvements
Deprecations and breaks

Pipe operator

Nested calls read inside-out. The |> operator chains callables left to right, passing each result into the next function without helper variables:

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

Either a first-class callable (trim(...)) or an arrow function fits into a stage. Anything longer than one expression still belongs into a named function - pipes reward short stages.

URI extension

parse_url() returns a plain array and every project validates it differently. PHP 8.5 adds an always-available URI extension following RFC 3986 and the WHATWG URL standard:

use Uri\Rfc3986\Uri; $uri = new Uri("https://php.net/releases/8.5/en.php"); var_dump($uri->getHost()); // string(7) "php.net"

URIs can be parsed, normalized, and modified through objects instead of string surgery, which closes a whole class of validation bugs.

Clone with properties

Readonly classes cannot be modified after construction, so every "wither" rebuilt the object by hand. Now clone() accepts an associative array of property overrides:

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]); } }

#[NoDiscard] attribute

Forgetting a return value is a silent bug - the call succeeds and the result evaporates. Mark the function and PHP warns when the value is dropped:

#[NoDiscard] function getPhpVersion(): string { return "PHP 8.5"; } getPhpVersion(); // Warning: return value should be used

A deliberate (void) cast marks the value as intentionally unused and silences the warning.

Callables in constant expressions

Static closures and first-class callables now work where only constants were allowed: attribute arguments, property and parameter defaults:

final class PostsController { #[AccessControl(static function (Request $request): bool { return $request->user !== null; })] public function update(Request $request): Response { // ... } }

Persistent cURL share handles

Share handles created with curl_share_init() died at the end of each request. curl_share_init_persistent() reuses a handle with the same share options across requests, so DNS and connection caches survive:

$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); curl_exec($ch);

array_first() and array_last()

Reading an edge of an array took a ternary with array_key_first(). The new helpers return the value directly and null for an empty array, composing neatly with ??:

$lastEvent = array_last($events); $first = array_first($events) ?? new Event();

Smaller improvements

Deprecations and breaks

Released 20 November 2025. Read the other end of the timeline: what is new in PHP 8.0 vs PHP 7.

Article author: Andrei Olegovich

Related articles
Development with PHP
PHP versions: history in brief
PHP 5: the release that made PHP object-oriented
PHP 7: twice as fast, with scalar types
PHP 8: JIT, attributes, match and named arguments
What is new in PHP 8.0 vs PHP 7
What is new in PHP 8.5 vs PHP 8.0

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.ru
Write to info@urn.su, info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: