Benjamin Crozat “Heard about Sevalla? They let you deploy PHP apps with ease.” Claim $50 →

A peek at PHP 8.5's features and changes

7 minutes read

A peek at PHP 8.5's features and changes

Introduction

PHP is an open-source project. Knowing what’s going on for PHP 8.5 only takes a minute of research. For instance, this page lists all the accepted RFCs for different versions of PHP, including PHP 8.5.

When will PHP 8.5 be released?

PHP 8.5 will be released on November 20, 2025, according to the preparation tasks list. It will be tested through three alpha releases, three betas, and six release candidates.

Date Release of PHP 8.5
Jul 03 2025 Alpha 1
Jul 17 2025 Alpha 2
Jul 31 2025 Alpha 3
Aug 12 2025 Feature freeze
Aug 14 2025 Beta 1
Aug 28 2025 Beta 2
Sep 11 2025 Beta 3
Sep 25 2025 RC1
Oct 09 2025 RC2
Oct 23 2025 RC3
Nov 06 2025 RC4
Nov 20 2025 GA

Install and test PHP 8.5 on macOS

  1. Install the Homebrew package manager if it’s not done already.
  2. Run brew update to make sure Homebrew and the formulae are up to date.
  3. Add a new tap (basically a GitHub repository) for PHP 8.5’s formula: brew tap shivammathur/php.
  4. Install the pre-compiled binary for PHP 8.5 (also called “a bottle” in Homebrew’s context). This will make the install so much faster. brew install php@8.5.
  5. Link it to make sure that the php alias targets the right binary: brew link --overwrite --force php@8.5.

If you want to learn more about how to install PHP on your Mac, I wrote something for you: PHP for Mac: get started fast using Laravel Valet

New features and changes ready for PHP 8.5

The new pipe operator

PHP 8.5 introduces a new |> operatorcalled the pipe operator that helps make code cleaner and easier to follow. It lets you take the result of one thing and feed it straight into another function. I couldn’t be happier!

It works like this:

$value = "Hello, World!"
    |> strlen(...)
    |> strtoupper(...)
    |> htmlentities(...);

Here’s what happens above:

  1. "Hello, World!" goes into strlen(...)
  2. That result goes into strtoupper(...)
  3. Then that result goes into htmlentities(...)

This is the same as writing:

$tmp = strlen("Hello, World!");
$tmp = strtoupper($tmp);
$value = htmlentities($tmp);

But it’s way shorter and easier to read.

A few rules, though:

  • You can only use one-argument callables with |>. More arguments will cause an error.
  • You can use arrow functions like fn ($foo) => ... in the chain.
  • It has smart precedence rules, so it works well with math, comparisons, and even null‑coalescing (??). You can add parentheses if you need to clarify order.

Learn more: PHP RFC: Pipe operator v3

Handy new CLI feature: php --ini=diff

PHP 8.5 sneaks in a little gem for anyone who’s ever wasted time chasing down weird config issues: php --ini=diff. This command prints out just the INI settings that are different from PHP’s built-in defaults. This means no more hunting through a wall of phpinfo() output or digging in random .ini files.

Here’s how it works:

php --ini=diff

You’ll get a clean summary like:

Non-default INI settings:
html_errors: "1" -> "0"
implicit_flush: "0" -> "1"
max_execution_time: "30" -> "0"

No more “wait, which setting changed this?” headaches.

Why I’ll actually use this:

  • Debug faster: I’ll instantly see which settings have been changed (by me or my host).
  • Works anywhere: Perfect for debugging on local, CI, Docker, or remote servers.
  • Plays nice with other flags: Combine with -n, -c, or -d to see how temporary or custom settings change things.

Final property promotion

PHP 8.5 makes classes easier to write and understand with a new feature called final property promotion. This means you can now use the final keyword directly in your constructor to say a property can’t be changed or overridden in child classes.

Before PHP 8.5, you couldn’t make a promoted property final right in the constructor. If you wanted a property to be final, you had to do extra work:

  • You had to declare the property separately in the class,
  • Then, make the constructor final to stop child classes from messing with it.

That made your code longer and messier.

Example before:

class User {
    public readonly string $name;

    final public function __construct(string $name) {
        $this->name = $name;
    }
}

With PHP 8.5, it’s way easier:

class User {
    public function __construct(
        final public readonly string $name
    ) {}
}

Now, just one line in your constructor, and the property is final. Child classes can’t override or change it. This keeps your code short, safe, and clear.

Learn more: PHP RFC: Final Property Promotion

Attributes on constants

PHP 8.5 now lets you add attributes (those #[…] notes) directly to regular constants declared with const. This was not possible before!

What changed?

  • You can now add attributes to constantsas long as each constant is on its own line.

  • Example:

    #[\MyAttribute]
    const EXAMPLE = 1;
    

    But this won’t work:

    #[\MyAttribute]
    const A = 1, B = 2; // Error
    
  • You can use ReflectionConstant::getAttributes() to read these attributes at runtime.

Bonus with deprecated constants: The built-in #[\Deprecated] attribute now works on constants too! Using it marks them as deprecated and shows warnings when someone uses them.

Learn more: PHP RFC: Attributes on Constants

Marking return values as important (#[\NoDiscard])

PHP 8.5 adds a new attribute called #[\NoDiscard] that you can put on functions or methods whose return values must not be ignored. If someone calls a function with this attribute and doesn’t use its return value, PHP will issue a warning—helping developers spot silent bugs.

Why it matters? Some functions return important information about success or failure. If you ignore the result, you might miss errors that don’t crash the script but still break things. #[\NoDiscard] helps avoid that oversight .

How it works:

  • Add #[\NoDiscard("message")] before a function or method.

  • If you call it and don’t use the result (no assignment, no cast), PHP warns.

  • Example:

    #[\NoDiscard("as processing might fail for individual items")]
    function bulk_process(array $items): array { … }
    
    bulk_process($items); // Warning.
    $results = bulk_process($items); // No warning.
    
  • You can suppress the warning with a (void) cast if ignoring the result is intentional.

Native support: PHP puts #[\NoDiscard] on a few built-in functions like some DateTimeImmutable::set*() methods (which return new objects) to protect against misuse.

Tool compatibility: Static analyzers (Psalm, PHPStan) and IDEs already track unused return values. This attribute gives them a standard hint to improve checks.

Learn more: Marking return values as important (#[\NoDiscard])

Closures in constant expressions

PHP 8.5 now allows you to use static closures (anonymous functions) inside things like constants, default values, and attributes at compile time!

  • You can write static closures in places where only compile-time code runs: class constants, default property values, attribute arguments, and even default parameter arrays.

    class Foo {
        public const SETTER = static function (string $value) {
            return strtoupper($value);
        };
    }
    

    This static closure is valid as a constant.

  • You can’t:

    • Capture variables from outside (use ($foo)), nor use arrow functions, because they auto-capture and break the compile-time rules.
    • Use $this or context inside closures. They must be static, pure, and evaluable during compile time.

Learn more: Closures in Constant Expressions

First-class callables in constant expressions

PHP 8.5 introduces a new feature allowing first-class callables. Like Foo::bar(...) or strtoupper(...) to be placed in constant expressions. This brings callables in line with static closures, making constant-time code more powerful and flexible.

What changed?

  • You can now use callables in compile-time-safe places:
    • Class constants, like:

      class X {
        public const CB = 'strtolower'(...);
      }
      
    • Default property values, default parameter values, and attribute arguments.

  • These callables are treated like constants; evaluated at compile time, giving better performance and consistency.
  • Restrictions apply:
    • Only first-class callables (stack-safe versions) are allowed, not closures or lambdas.
    • They resolve names properly, including self, static, and global functions.

Learn more: PHP RFC: First Class Callables in constant expressions

Conclusion

The new pipe operator (|>) definitely steals the spotlight in PHP 8.5, making code cleaner and more readable than ever. But constants also got a lot of love with support for attributes, static closures, and first-class callables. Combined with final property promotion and #[\NoDiscard], PHP 8.5 is looking like a pretty damn fine release!

There are some boring features I didn’t talk about and you can learn more about them here if you want.


Did you like this article? Then, keep learning:

0 comments

Guest

Great deals for developers

Check all deals →