Blog
September 18, 2025
PHP 8.5 is set to release later this year. Packed with a laundry list of new features, PHP 8.5 continues the language's trend of improving developer and user experience. However, as with any new version release, PHP 8.5 also introduces a number of deprecations. While these will streamline the language and improve security, it's important for developers to review and adapt their applications accordingly, particularly before upgrading.
In this blog, I walk through the most notable changes in PHP 8.5, then cover the key deprecations to watch out for when planning your upgrade. I also provide tips and insights on migration or upgrade plans to help you transition smoothly to this latest version of PHP.
PHP 8.5 Release Date
PHP 8.5 is scheduled to release on November 20, 2025.
This is following a series of alpha, beta, and release candidates. As always, the release managers for 8.5 may choose to extend the RC phase should issues arise during testing; if this happens, the next window for release would fall on December 4.
Back to topMake Your Migration Plan Today
Whether you're looking for full Migration and Modernization Services or prefer to take advantage of flexible Black Belt Service hours, Zend is here to help. Explore your options via the buttons below.
New PHP 8.5 Features
All minor releases introduce new features, ranging from small additions (e.g. new optional arguments to existing functions) to larger features (e.g. property hooks, introduced in PHP 8.4).
PHP 8.5 builds on a number of features introduced in previous versions — such as expanding asymmetric visibility to static properties, or allowing first class callables in constant expressions — and also introduces some new functionality, such as the "clone with" functionality and the pipe operator.
We won't go through every change introduced in PHP 8.5, but instead highlight some of the more notable and impactful changes. You can read through the full list on the PHP RFC wiki.
RFC 3986 and WHATWG URL API
PHP was built for the web, and the web operates on URIs and URLs. Unfortunately, PHP's primary tools for working with URLs (notably parse_url()
) are dated, and don't even follow published internet standards. On top of that, the fact that URIs and URLs can have decoding built in, or use Unicode characters via punycode makes even comparing URLs that are equivalent problematic.
RFC 3986 and WHATWG URL are two specifications for URLs that are widely adopted within browsers and tooling such as cURL. PHP 8.5 adds new classes that target each of these specifications:
Uri\Rfc3986\Uri
Uri\WhatWg\Url
Both are readonly
classes, and each supports parsing URI strings via either the constructor, or a parse()
factory method. Additionally, they allow resolving relative URLs, either from the existing instance, or by passing a base URL when using parse()
.
Each class allows retrieving any individual component of the URL (e.g. scheme, host, path, query string arguments, fragment). You can also use a number of "with" methods to create new instances with changes for the specified components (e.g. $updated = $uri->withHost('https://example.com')
), similar to how PSR-7 operates (the expectation is that these classes will be used internally by PSR-7 implementations in the future). From there, you can use dedicated methods to get string representations of the URIs, as well as compare two instances for equivalence.
This is a large addition to the language, and has a pretty expansive API; we highly recommend that you read the full RFC on the PHP wiki.
Clone With
When readonly
properties were introduced to PHP, developers experienced an unexpected side effect: cloning suddenly became convoluted, as you couldn't readily change the value of a property on the clone itself. If you initialize all properties in your constructor, you can do something like the following:
$values = get_object_vars($this);
$values['propertyToChange'] = $newValue;
return new self(...$values);
This approach leverages named arguments, and assumes that the arguments and the properties are identically named. More often than not, however, developers needed to fall back to ReflectionProperty::setValue()
to make cloning work correctly.
PHP 8.5 introduces a change to how cloning works. Previously, clone
was a standalone keyword. As of PHP 8.5, it now acts like a language construct and accepts an optional second argument, an array of named properties, and their values as they should be set in the cloned object. It means that clone
now operates like a function:
function clone(object $object, array $withProperties = []): object
In fact, with this change, clone
can now also be used as a callable
, including as a first class callable, which means it can be passed to functions like array_map()
to provide the ability to hydrate objects!
Additionally, it honors the __clone()
magic method, as well as property hooks, ensuring that there are no surprises after cloning is complete.
For more information, read the clone with v2 RFC.
Pipe Operator
A number of PHP contributors have been working towards functional programming additions to the PHP language; the addition of first class callables was an introductory step in this direction. PHP 8.5 now introduces the "pipe operator" ("|>"
), which provides a mechanism for chaining execution of several functions, each operating on the return value of the previous.
How does it work? The pipe operator has the following definition:
mixed |> callable;
It evaluates left to right, accepting a single parameter callable on the right, and passing the left side value to it, evaluating to the callable's result.
Object-oriented developers will often deal with these sorts of operations via collections or models. As an example, we may have a UserRepository
from which we fetch a Users
collection that allows us to filter the users based on some callable criteria, and then count how many were returned:
$numberOfAdmins = $userRepository
->fetchUsers()
->filter(fn(User $user): bool => $user->isAdmin())
->count();
The pipe operator allows us to do the same, but without requiring building this functionality into a dedicated "collection" class. Our repository could instead return an array of User
instances, and we could pipe this to additional functions:
$numberOfAdmins = $userRepository->fetchUsers()
|> (fn (array $list) => array_filter($list, fn(User $user) : bool => $user->isAdmin()))
|> count(...);
You may notice the second line looks... convoluted. This is because array_filter()
takes two arguments: a list and a callable. However, with first class callables, you expect to pass an entire list of arguments at once, which cannot happen with the pipe operator. Another RFC is in the works to add partial function application, which would simplify the above to:
|> array_filter(?, fn(User $user): bool => $user->isAdmin())
At time of writing, the earliest we will likely see partial function application is with PHP 8.6, planned for release in 2026.
array_first and array_last
PHP 7.3 added array_key_first()
and array_key_last()
, which simplified how we grab the first and last keys, respectively, of an array. However, getting the values associated is fairly cumbersome:
$value = $array[array_key_first($array)];
Additionally, if you were to grab the index, and then pull the value later, after performing other operations, it's entirely possible that the value and/or position may have since changed (e.g., if you performed sort()
, or array_shift()
, or array_push()
).
PHP 8.5 adds corollary functions to immediately grab those values:
array_first()
array_last()
get_error_handler() and get_exception_handler()
Prior to PHP 8.5, if you wanted to grab the current error or exception handler, you had to set it to something else, and then restore it:
$errorHandler = set_error_handler('some_valid_callback');
restore_error_handler();
$exceptionHandler = set_exception_handler('some_valid_callback');
restore_exception_handler();
Needless to say, this is unintuitive, and it's easy to forget to restore the handler once you've retrieved it this way.
PHP 8.5 adds dedicated functions for retrieving the currently active handler in each scenario:
get_error_handler(): ?callable
get_exception_handler(): ?callable
Other New PHP 8.5 Features
As noted before, we won't be covering every new PHP 8.5 feature or change. However, there are a few other new features we want to call attention to, as they extend features previously introduced or add safety to existing features.
- Attributes can now be applied to constants.
#[Deprecated]
can now be used with traits (not just classes and interfaces), as well as constants (since constants cannow have attributes).#[Override]
can now target class properties; this will cause the engine to validate that a property of the same name exists in a parent class or implemented interface, and emit a compilation error if it does not.- The OpCache extension is now a non-optional part of PHP, and will always be present in the binary, regardless of how packagers package and distribute PHP.
You can now declare a property
final
when using constructor property promotion:public function __construct( final readonly DateTimeInterface $date, ) { }
- Static properties can now apply asymmetric visiblity.
- Both closures and first class callables can now be used anywhere a constant expression is allowed — for instance, as an attribute parameter, a default value of a property or function parameter, a constant definition.
- You can now enable backtraces for fatal errors. When enabled, if you catch errors,
error_get_last()
will include a trace key with the associated backtrace. This feature will make it easier to identify the root cause of a fatal error. setcookie()
andsetrawcookie()
will now accept "secure" and "partitioned" options, allowing you to control Cookies Having Independent Partitioned State (CHIPS) behavior. Additionally,session_start()
andsession_set_cookie_params()
allow you to enable cookie partitioning for the session cookie.
Back to topOn-Demand Webinar: Managing Mission-Critical Web App Migrations
Watch along as Zend Senior Professional Services Engineer Clark Everetts explores how to manage successful PHP web application migrations — including how to plan and implement upgrades to the latest PHP version.
PHP 8.5 Deprecations
Every new feature release also includes deprecations. Deprecations serve as a warning of what changes will come in the next major version, allowing you to adapt your code early. Depending on the functionality, you may never need to even think about the impact (e.g., if you never call the affected function, its deprecation likely won't affect you); on the flip side, some deprecations may require a massive rewrite or refactor of your application.
The PHP project tries very hard to consider the impact of a deprecation before introducing it. Most often, these are introduced because functionality is insecure, undocumented, or has unexpected side effects.
As with listing new features, we cannot list every single deprecation being made in PHP 8.5; if you want to dive deeper, you can read the PHP 8.5 deprecations RFC.
Type | PHP 8.5 Deprecation |
---|---|
Language/Syntax Deprecations | Terminating a case statement with a semicolon. Always terminate with a colon. |
Non-standard cast names. Use int (not integer ), float (not double ), bool (not boolean ), string (not binary ). | |
Usage of backticks ("`" ). Use shell_exec()` instead. | |
Usage of null as an array offset when calling array_key_exists() . | |
__sleep() and __wake() for serialization. Use __serialize() and __unserialize() instead. | |
Constant redeclaration. | |
INI Directive Deprecations | report_memleaks . Memory leaks are always reported in debug builds. (This setting has no effect on production builds.) |
register_argc_argv . . This flag is hard coded on in the CLI, phpdbg, and embed SAPIs already. In HTTP-based SAPIs, enabling it can be dangerous, as it allows defining the $argv and $argc variables from the query string, which can lead to errors and security issues. The flag is now deprecated in PHP 8.5, and defaults to off in HTTP SAPIs. | |
disable_classes is now removed entirely. Using the setting led to runtime instability, and often could crash the runtime due to missing functionality. | |
Standard Library Deprecations |
Previously, passing null would tell each to use the directory handle previously opened via For example, if a library followed that usage, it could make changes that are unexpected in user code. |
PDO Deprecations | There are a huge number of driver-specific constants and methods defined on the main PHP 8.5 deprecates usage of the |
Resource Deprecations | Since PHP 7.4, the language has gradually replaced PHP 8.5 deprecates a number of these "close" or "free" functions as a result:
|
PHP 8.5 Upgrade and Migration Considerations
We generally recommend waiting for at least one bug fix release before adopting any new feature release, and PHP 8.5 is no exception. While the PHP release process includes multiple alpha, beta, and release candidates, history has shown that not enough people test them before the general availability release is issued. Waiting also allows ecosystem QA tooling such as static analysis tooling to adapt to the new release; these tools will often help you identify potential issues with an upgrade.
Once you are ready, make sure you read through the approved RFCs, and particularly the RFC with the general list of deprecations, to see what features you use may be affected. Use static analysis tooling to test your code for compatibility, and consider seeing if tools such as Rector can help your team.
Back to topFinal Thoughts
For teams who would rather their developers focus their time and energy elsewhere, or for those with limited in-house PHP expertise, another option for upgrading to PHP 8.5 is to partner with a trusted third party, such as Zend. We help you migrate, modernize, or upgrade with confidence, minimizing risk and saving time as you upgrade to the latest PHP version.
Comprehensive Support for PHP Version Upgrades
Zend makes mission-critical PHP possible through our full suite of PHP services. Take advantage of long term support for EOL PHP versions, unlock 24/7/365 support from a global team of experts, and stay up to date on the latest security and performance enhancements PHP has to offer.
Additional Resources
- On-Demand Webinar - Modernizing Legacy Web Apps: Strategies for a Seamless Journey
- White Paper - The Hidden Costs of PHP Upgrades
- White Paper - Planning Your Next PHP Migration
- Blog - Creating a PHP 8.1 Upgrade Plan Before EOL
- Blog - PHP Installer for Extensions (PIE): What to Know Before General Release
- Blog - PHP Migration and Version Adoption Trends
- Blog - PHP Upgrades: How to Plan and Execute Your Next Upgrade