What's new in PHP 8.5
PHP 8.5 lands with a pack of features that smooth out daily development, remove long-standing friction points, and make functional patterns far easier to write. It feels like a release focused on practical wins: cleaner code, stronger introspection tools, safer configuration defaults, and more helpful debugging. Let's walk through the most interesting additions.
The Pipe Operator
One of the most talked about additions is the new pipe operator. The operator evaluates left to right, passing the previous expression's value into the next function or closure.
Before PHP 8.5, you might write something like this:
$input = ' Some kind of string. ';
$output = strtolower(
str_replace(['.', '/'], '',
str_replace(' ', '-',
trim($input)
)
)
);
With the pipe operator, the same logic becomes far clearer:
$input = ' Some kind of string. ';
$output = $input
|> trim(...)
|> (fn ($string) => str_replace(' ', '-', $string))
|> (fn ($string) => str_replace(['.', '/'], '', $string))
|> strtolower(...);
You can also use pipes to chain arbitrary callables, which helps when modeling functional flows without a heavyweight collection class:
$numberOfAdmins = $userRepository->fetchUsers()
|> (fn ($list) => array_filter($list, fn (User $u): bool => $u->isAdmin()))
|> count(...);
This cuts down on nested calls and makes linear transformations clearer.
New array_first and array_last functions
Two new functions, array_first() and array_last(), return the first and last values of an array. This avoids using array_key_first() combined with extra indexing.
Basic usage looks like this:
$single = ['single element'];
var_dump(array_first($single)); // string 'single element'
var_dump(array_last($single)); // string 'single element'
$empty = [];
var_dump(array_first($empty)); // null
var_dump(array_last($empty)); // null
$mixed = [1 => 'a', 0 => 'b', 3 => 'c', 2 => 'd'];
var_dump(array_first($mixed)); // 'a'
var_dump(array_last($mixed)); // 'd'
Note that these return the value and not the key. They also handle empty arrays by returning null so you can safely coalesce or test the result without extra checks.
Clone with properties
Readonly properties introduced a quirky side effect: cloning objects while adjusting just one property became painful. Many developers had to resort to reflection or reconstruct objects manually. PHP 8.5 fixes this by extending clone into a construct that accepts a second argument: an array of property names and updated values.
The signature effectively becomes:
clone(object $object, array $withProperties = []): object
An example will look like as following:
final class Book
{
public function __construct(
public string $title,
public string $description,
) {}
public function withTitle(string $title): self
{
return clone($this, [
'title' => $title,
]);
}
}
$copies = array_map(clone(...), $listOfBooks);
This is a big improvement for immutability patterns and object hydration.
New URI extension and parsing tools
PHP 8.5 ships a new always-available URI extension covering RFC 3986 and the WHATWG standard. It normalizes URLs by default but lets you retrieve the raw form when needed.
Example using the RFC 3986 Uri class:
use Uri\\Rfc3986\\Uri;
$url = new Uri('HTTPS://hibit.dev:443');
$uri->getHost();
$uri->getScheme();
$uri->getPort();
$url->toString(); // https://hibit.dev:443
$url->toRawString(); // HTTPS://hibit.dev:443
You get predictable normalization, editable components, and clearer handling of ports and encodings.
Max memory limit
The max_memory_limit acts as a hard cap. Once set, any attempt to raise memory_limit above this ceiling will trigger a warning and reset it to the maximum allowed. It cannot be changed at runtime since it’s an INI_SYSTEM directive, making it perfect for administrators who want tighter control over memory use across all scripts.
ini_get('max_memory_limit'); // "256M"
ini_get('memory_limit'); // "128M"
ini_set('memory_limit', '156M'); // Allowed, lower than max_memory_limit 256M
ini_set('memory_limit', '256M'); // Allowed, equal to max_memory_limit 256M
ini_set('memory_limit', '300M'); // Warning, set to max_memory_limit instead
ini_set('memory_limit', '-1'); // Warning, cannot remove restrictionThis new directive gives developers and system administrators a clear safety net, preventing runaway scripts from consuming excessive memory and stabilizing resource usage across environments.
Internationalization enhancements
PHP 8.5 includes several i18n improvements that help apps that must handle multiple locales and scripts.
One addition is a helper to test layout direction:
locale_is_right_to_left('ar_EG'); // true for Arabic EgyptThere is also a Locale class method Locale::isRightToLeft and a new IntlListFormatter class for locale aware list formatting. These additions make it easier to build UIs and text output that respect language conventions such as list separators and right to left behavior.
Improved debugging for fatal errors
Fatal errors now include backtraces by default, controlled by the fatal_error_backtraces INI directive. That change makes it far easier to track the origin of hard failures in production.
A fatal error message now prints a stack trace similar to regular exceptions. In practice this shortens the time it takes to find a cause when you see a runtime crash in logs and you do not have an interactive debugger attached.
0 Comments