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

PHP 8.5 is coming soon: 15 new features and changes

11 minutes read

PHP 8.5 is coming soon: 15 new 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

General Availability (GA) is planned for November 20, 2025, according to the preparation tasks list. It will be tested through three alpha releases, three betas, and four release candidates. You can also track the schedule on PHP.Watch’s PHP 8.5 page.

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

How to install PHP 8.5 (Homebrew and Docker)

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. The tap’s status is documented in the tap README.

  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 (this overwrites the php shim).

Tip if you have multiple PHP versions installed: run brew unlink php (or another version like php@8.3) before linking 8.5; later run brew link for 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

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 in a callable that accepts one parameter, for example (|> (fn ($_): string => phpversion())) inside a chain.
  • Precedence is left to right. When mixing with math, comparisons, null coalescing (??), and the ternary operator, use parentheses to force the order you intend.
  • Gotchas: wrap arrow-function steps in parentheses when used in a chain; by-reference callables are not allowed on the right-hand side.

A quick example showing an arrow function in a chain:

$result = "  a  "
    |> trim(...)
    |> (fn ($s) => $s . "!")
    |> strlen(...);

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

URI extension (RFC 3986 and WHATWG URL)

PHP 8.5 includes a new, always-available URI extension with standards-compliant parsers for both RFC 3986 and the WHATWG URL standard. You get immutable value objects, withers for safe changes, normalization, and exceptions for invalid input.

use Uri\Rfc3986\Uri;

$u = new Uri('https://example.com/a/../b?x=1#frag');
$u = $u->withPath('/b');

echo (string) $u; // https://example.com/b?x=1#frag

Overview and background: The PHP Foundation: the PHP 8.5 URI extension and the URI extension RFC.

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.

Let’s say you have an array with inconsistent keys:

$numbers = [10, 20, 30];

unset($numbers[0]);

array_first($numbers); // 20

array_last($numbers); // 30;

$numbers[0] doesn’t exist anymore and you have can’t know it in advance. And obviously, you usually never know which key is the last one of an array, so array_last()’s usefulness is immediately obvious.

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 (defaults to On in both development and production), 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.

Clone with v2

PHP 8.5 upgrades clone so you can pass a second array argument to adjust properties on the cloned object. This works with readonly, honors __clone, and respects property hooks. It is a nice quality-of-life win for immutable patterns. Learn more: clone with v2 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: RFC for handler introspection and the 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 adds 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. Also see the 8.5 alpha notes for compact decimals and locale helpers in PHP.Watch’s 8.5 alpha 1 summary.

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](PHP 8.5 RFC: Grapheme cluster for levenshtein, grapheme_levenshtein function).

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.

See only the settings you changed with 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"

Ops note: a new max_memory_limit INI can cap memory_limit at the system level (INI_SYSTEM). See PHP.Watch: max_memory_limit.

The new PHP_BUILD_DATE constant

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).

Under the hood

  • OPcache is no longer optional: it is always compiled in from PHP 8.5 onward, while enablement remains controlled by INI settings. This affects packaging and “what is included by default” in many environments. See the OPcache required RFC.
  • A new max_memory_limit INI caps memory_limit at the system level (INI_SYSTEM), which helps lock down production defaults. See PHP.Watch: max_memory_limit.

What else is coming in 8.5

  • DOM in the 8.5 line: outerHTML, insertAdjacentHTML, and children. See alpha 1 notes.
  • Smaller items worth noting: CHIPS/partitioned cookies; #[Deprecated] support for traits; #[Override] allowed on properties; FILTER_THROW_ON_FAILURE; locale support for case-insensitive grapheme functions; and the accepted RFC for persistent cURL share handles. See the PHP 8.5 RFC list.

Conclusion

The pipe operator (|>) steals the spotlight in PHP 8.5, making code cleaner and more readable. The new URI extension gives PHP a modern, standards-based URL/URI API, and clone with v2 improves immutable workflows. 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 8.5 RFC list.


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 tools for developers

Search for posts and links

Try to type something…