
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
- Install the Homebrew package manager if it’s not done already.
- Run
brew update
to make sure Homebrew and the formulae are up to date. - Add a new tap (basically a GitHub repository) for PHP 8.5’s formula:
brew tap shivammathur/php
. - 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
. - 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:
"Hello, World!"
goes intostrlen(...)
- That result goes into
strtoupper(...)
- 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.
- Capture variables from outside (
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:
- Preview PHP 8.4 features to understand the progression to PHP 8.5
- Read quality PHP blogs to keep growing your PHP skills beyond PHP 8.5
- Deepen your PHP knowledge with best PHP packages to use in 2025
- Learn to install PHP quickly and easily on macOS using Laravel Valet
- Learn to lock PHP extensions and manage versions with Composer
- Explore PHP 8.3's Override attribute, complementing PHP 8.5 features
- Stay updated with PHP 8.3 release notes and features leading to PHP 8.5
- Get a look ahead at future PHP developments with PHP 9.0 changes preview
- Master array-related PHP functions to complement new language features in PHP 8.5
- Understand and leverage PHP’s null coalescing operators for cleaner code
0 comments