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

9 minutes read

A peek at PHP 8.5's features and changes

Introduction

PHP is an open-source project. Knowing what’s going on in 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. In this post, I highlight PHP 8.5 features you’ll actually use.

PHP 8.5 Release Date and Schedule

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 four 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

Note: dates may shift slightly.

How to Install PHP 8.5 on macOS (Homebrew)

If you’re on macOS and want to test the PHP 8.5 release early, Homebrew makes it easy. Bottles are available and these builds track 8.5.0-dev (nightlies) until GA. The safest way to avoid conflicts with Homebrew core is to use the dedicated tap and fully qualified formula.

  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 the PHP tap: brew tap shivammathur/php.
  4. Install PHP 8.5 from the tap: brew install shivammathur/php/php@8.5.
  5. Link it so the php shim targets 8.5: brew link --overwrite --force shivammathur/php/php@8.5.

Tip if you have multiple PHP versions installed: brew unlink php (or another version like php@8.3) before linking 8.5, and later brew link the one you want active.

If you prefer not to touch your Homebrew PHP, you can test using the official PHP Docker images.

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 in PHP 8.5

What changed since 8.4?

  • The new PHP 8.5 pipe operator makes readable, left-to-right function chaining possible.
  • array_first/array_last are built-in helpers for common array access.
  • Fatal error backtraces are now available by default for faster debugging.

Pipe operator (|>)

The PHP 8.5 pipe operator (|>) is my favorite of the PHP 8.5 features. It passes the result of the left expression as the first argument to the callable on the right, letting you write readable, left-to-right pipelines.

A type-safe example that stays string-only until the end:

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

Rules to remember for the PHP 8.5 pipe operator:

  • Each callable in the chain must accept the piped value as its first parameter and have at most one required parameter.
  • You can pipe into closures, arrow functions, first-class callables, array callables, and invokable objects.
  • Built-ins that accept no parameters cannot be used directly; wrap them with an arrow function that ignores the piped value, e.g. fn ($_): string => phpversion().
  • Precedence is left-to-right. When mixing with math, comparisons, null coalescing (??), and the ternary operator, use parentheses to force the order you intend.

Learn more: the pipe operator overview on PHP.Watch and the RFC on wiki.php.net.

array_first and array_last

Two small helpers you’ll actually use: array_first and array_last return the first and last element of an array, making code a little clearer.

$numbers = [10, 20, 30];
array_first($numbers); // 10
array_last($numbers);  // 30

Learn more: RFC: array_first and array_last.

Fatal error backtraces (fatal_error_backtraces)

Debugging gets easier with fatal error backtraces. With the new fatal_error_backtraces INI (on by default), PHP shows a stack trace for fatal errors and respects #[\SensitiveParameter] and zend.exception_ignore_args.

; php.ini
fatal_error_backtraces = 1

Details: PHP.Watch: Fatal error backtraces and the RFC.

Handler introspection (get_error_handler/get_exception_handler)

New functions get_error_handler() and get_exception_handler() let you introspect the current handlers at runtime.

set_error_handler(fn () => true);
var_dump(get_error_handler());
restore_error_handler();

set_exception_handler(fn (Throwable $e) => null);
var_dump(get_exception_handler());
restore_exception_handler();

Learn more: PHP.Watch overview.

Intl updates (list formatter, RTL check, NumberFormatter updates)

Internationalization gets a nice bump in PHP 8.5: IntlListFormatter formats human-readable lists; locale_is_right_to_left() and Locale::isRightToLeft() help with layout for right-to-left scripts; NumberFormatter gets compact decimal formats; and Locale::addLikelySubtags()/::minimizeSubtags() help normalize locale tags.

$lf = new IntlListFormatter('en', IntlListFormatter::TYPE_CONJUNCTION);
echo $lf->format(['apples', 'bananas', 'oranges']); // apples, bananas, and oranges

$isRtl = locale_is_right_to_left('ar'); // true

Learn more: IntlListFormatter and RTL checks on PHP.Watch.

grapheme_levenshtein

grapheme_levenshtein computes a Unicode-aware Levenshtein distance that understands grapheme clusters (what users perceive as characters), which is perfect for strings like “café”.

grapheme_levenshtein('café', 'cafe'); // 1

Learn more: PHP.Watch explainer.

Attributes on constants

PHP 8.5 lets you add attributes directly to const declarations. The main constraint: one constant per statement.

#[\MyAttribute]
const EXAMPLE = 1;

#[\MyAttribute]
const A = 1; // OK
// const A = 1, B = 2; // Not allowed with attributes

You can reflect these with ReflectionClassConstant::getAttributes() or ReflectionConstant::getAttributes(). The built-in #[\Deprecated] works on constants too, so you can mark constants as deprecated and surface warnings.

Learn more: RFC: Attributes on constants.

Final property promotion

Final property promotion rounds out the work started in 8.4 with final properties and property hooks. In PHP 8.5 you can mark a promoted property as final directly in the constructor, preventing redeclaration or overrides in child classes. Visibility is optional when using final (defaults to public), and you can combine this with property hooks.

Before PHP 8.5, you had to declare the final property in the class body, then assign it in the constructor:

class User {
    final public readonly string $name;

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

With PHP 8.5, you can promote and finalize the property in one place:

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

Learn more: RFC: Final property promotion.

Closures and first-class callables in constant expressions

PHP 8.5 allows static closures in constant expressions and also allows first-class callables, which complements the closures-in-constants feature. Both are supported now, with a few restrictions.

class Foo {
    // Static closure as a constant
    public const UPPER = static function (string $v): string { return strtoupper($v); };

    // First-class callable as a constant
    public const LOWER = 'strtolower'(...);
}

Notes:

  • Closures must be static. No variable capture (use ($x)) or arrow functions (they auto-capture) are allowed in constant expressions.
  • First-class callables like 'strtolower'(...) or Bar::baz(...) are allowed. (Closures are covered by the separate “closures in constant expressions” RFC.)

Learn more: Closures in constant expressions (RFC) and first-class callables in constants on PHP.Watch.

cURL: curl_multi_get_handles

curl_multi_get_handles lets you retrieve all easy handles attached to a multi handle, which is useful for debugging and housekeeping.

$mh = curl_multi_init();
$ch = curl_init('https://example.com');
curl_multi_add_handle($mh, $ch);
$handles = curl_multi_get_handles($mh); // array of CURLHandle

Learn more: PHP.Watch note.

CLI: php –ini=diff

A small but powerful CLI addition: php --ini=diff prints only INI settings that differ from PHP’s built-in defaults, which is great for quick diagnostics.

php --ini=diff

Example output:

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

Learn more: PHP.Watch: php –ini=diff.

PHP_BUILD_DATE

There’s a new PHP_BUILD_DATE constant that exposes the timestamp of the binary you’re running, which helps when comparing nightlies or deployment artifacts.

echo PHP_BUILD_DATE; // e.g., 2025-08-31

Learn more: PHP.Watch: PHP_BUILD_DATE.

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

#[\NoDiscard] marks functions or methods whose return values must not be ignored, helping developers spot silent bugs.

#[\NoDiscard("processing might fail for individual items")]
function bulk_process(array $items): array { /* ... */ }

bulk_process($items);           // Warning.
$results = bulk_process($items); // OK.
(void) bulk_process($items);     // Explicitly ignore (suppresses the warning).

Engine details: ignoring a #[\NoDiscard] return value emits E_WARNING for internal functions and E_USER_WARNING for userland; functions that return void/never, and magic methods that must be void, cannot use it. Tip: the (void) cast is a statement that explicitly discards a value.

Learn more: Marking return value as important (RFC).

What else is coming in 8.5

Here’s a quick summary of additional PHP 8.5 features mentioned above so you can scan fast:

  • PHP 8.5 pipe operator (|>) for readable pipelines.
  • array_first and array_last helpers.
  • Fatal error backtraces via fatal_error_backtraces (on by default).
  • Handler introspection with get_error_handler()/get_exception_handler().
  • Intl updates: IntlListFormatter, RTL checks, compact decimals, and new Locale helpers.
  • Unicode-aware grapheme_levenshtein.
  • Attributes on constants (one constant per statement).
  • Final property promotion (final on promoted properties).
  • Closures and first-class callables allowed in constant expressions.
  • cURL: curl_multi_get_handles.
  • CLI: php --ini=diff.
  • PHP_BUILD_DATE constant.
  • Honorable mention for web devs: DOM additions like outerHTML and insertAdjacentHTML landed in the 8.5 line as well.

Conclusion

The pipe operator (|>) definitely steals the spotlight in PHP 8.5, making code cleaner and more readable. Constants also get love with support for attributes, static closures, and first-class callables. Combined with final property promotion and #[\NoDiscard], PHP 8.5 is shaping up to be a strong release. You can explore more proposals and accepted changes on the PHP RFC index.


Did you like this article? Then, keep learning:

Help me reach more people by sharing this article on social media!

0 comments

Guest

Markdown is supported.

Hey, you need to sign in with your GitHub account to comment. Get started →

Great deals for developers

Search for posts and links

Try to type something…