Tilly The Coder
Tilly The Coder
What's New in PHP 8.5
Tilly The Coder

What's New in PHP 8.5

Tilly The Coder
5 mins
This is the estimated time it takes to read the article.

PHP 8.5.0 was released on Nov 20, 2025. By the Apr 15, 2026 crawl date, PHP 8.5 was already a stable production target. The headline features are practical rather than cosmetic: a standard URI extension, the pipe operator, and clone() support for changing properties during cloning.

If you build Laravel apps, the upgrade is less about rewriting your app and more about spotting the places where PHP 8.5 makes existing code cleaner.

URI handling becomes a language-level tool

PHP 8.5 ships an always-available URI extension with APIs for RFC 3986 and WHATWG URL handling. That matters because URL parsing looks simple until edge cases appear: percent encoding, odd hosts, query strings, unicode, and browser-style URLs.

use Uri\Rfc3986\Uri;

$uri = new Uri('https://tillythecoder.com/articles?topic=php');

$host = $uri->getHost(); // tillythecoder.com

In Laravel projects, this is useful around crawlers, webhook callbacks, sitemap generation, canonical URLs, and import scripts.

Use RFC 3986 handling when you are working with backend URLs, API callbacks, signed routes, or canonical links. Use WHATWG-style handling when you need behavior closer to a browser. The split is useful because server URLs and browser URLs do not always follow the same edge-case rules.

use Uri\WhatWg\Url;

$url = new Url('https://example.com/articles?tag=php#comments');

$path = $url->getPathname();
$hash = $url->getHash();

Laravel already has helpers for many URL tasks. The URI extension is not a replacement for route(), url(), signedRoute(), or request URL helpers. It is most useful when the input is an arbitrary URL string that your app needs to parse, validate, normalize, or inspect before storing it.

The pipe operator makes data transformations read forward

The new |> operator passes the value on the left into the callable on the right. The main benefit is readability: nested transformations can become a top-to-bottom pipeline.

$slug = ' PHP 8.5 Released '
    |> trim(...)
    |> strtolower(...)
    |> fn (string $value): string => str_replace(' ', '-', $value);

This is a good fit for small transformations where each step is pure and obvious. It is not a replacement for Laravel pipelines, jobs, actions, or collections.

The pipe operator works best with first-class callables and tiny pure steps. If a transformation needs container resolution, authorization, side effects, or branching, put it in an action or a normal method. The point is clarity, not forcing every piece of code into an expression.

$summary = $body
    |> strip_tags(...)
    |> html_entity_decode(...)
    |> trim(...)
    |> fn (string $text) => Str::limit($text, 180);

Clone with is built for immutable objects

PHP 8.5 lets you clone an object and update properties in one expression. This is especially helpful for readonly value objects and with-er methods.

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

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

This is a natural fit for DTOs, money values, color values, search filters, date ranges, and immutable configuration objects. It removes boilerplate from withX() methods while keeping the original object untouched.

NoDiscard helps catch ignored return values

PHP 8.5 adds the NoDiscard attribute for functions where ignoring the return value is probably a bug. The matching void cast lets you document that a return value is intentionally ignored.

#[\NoDiscard]
function normalizeEmail(string $email): string
{
    return strtolower(trim($email));
}

$email = normalizeEmail(' TILLY@example.com ');

(void) normalizeEmail('ignored@example.com');

In application code, this can be useful for immutable methods, normalization helpers, and command objects that return a result. Do not scatter it everywhere. It is valuable when the ignored value would hide a real bug.

Constant expressions and attributes are more flexible

PHP 8.5 allows closures and first-class callables in more constant-expression positions, including attribute parameters, default values, constants, and class constants. Attributes can also be applied to compile-time non-class constants, and the Deprecated attribute can be used there.

These changes are mostly library-facing, but Laravel developers will see them in packages that build richer attribute APIs. Routing, validation, mapping, and metadata packages can express intent closer to the code they describe.

Smaller language improvements

  • Override can now apply to properties, which helps catch inheritance mistakes in object-heavy code.

  • Static properties can use asymmetric visibility, matching the direction PHP started with instance properties.

  • Fatal errors can include backtraces, which makes production debugging less blind.

  • Constructor property promotion now supports final properties.

  • Casts are allowed in constant expressions, which helps configuration-like code stay explicit.

  • Persistent cURL share handles improve safe connection reuse in repeated requests.

Migration notes to check before upgrading

  • The backtick operator as an alias for shell_exec() is deprecated.

  • Non-canonical cast names such as (boolean), (integer), (double), and (binary) are deprecated.

  • __sleep() and __wakeup() are soft-deprecated in favor of __serialize() and __unserialize().

  • Using null as an array offset is deprecated.

  • Incrementing non-numeric strings is deprecated; use str_increment() for that behavior.

  • Casting NAN now emits a warning in more situations.

  • Destructuring non-array values, other than null, now warns.

  • Some float or numeric-string to int casts warn when the value cannot be represented.

Most Laravel applications will not hit these every day, but older packages, import scripts, and legacy integrations are the places to look first.

Where PHP 8.5 changes real app code

Start with the edges of your application: URLs, immutable DTOs, crawlers, API payload transformations, and command-line maintenance scripts. Those are the places where URI parsing, clone-with, and pipe syntax can remove noise without making the app clever.

For a Laravel codebase, I would not run through the app replacing every helper call with a pipe. I would upgrade PHP, fix deprecations, then use the new features in the next piece of code that genuinely becomes easier to read. PHP 8.5 is a good maintenance release because it lets good code get smaller without asking a team to adopt a new architecture.