Skip to main content

What's New in PHP 8.5: Pipe Operators, URI Extension, and More

·1127 words·6 mins·

The new stable release of PHP 8.5 is coming next month on November 20, 2025. This release is packed with list of new improvements and feature deprecations that will streamline the language and imrprove security. It’s important for developers to review these changes to ensure their applications remain compatible with these new updates. These are only the major changes; for a complete list on the PHP RFC wiki.

New Features in PHP 8.5
#

1. Pipe Operators
#

PHP 8.5 introduces pipe operators (|>) that allow for more readable and functional-style code. The pipe operator (|>) takes the result of an expression and passes it as the first argument to a function.

The pipe operator has the following definition:

mixed |> callable;

The pipe operator evaluates left to right, accepting a single parameter callable on the right. It then passes the left side value to the callable, which evaluates to its result.

Here’s an example of how to achieve the same result using traditional function calls versus using the pipe operator:

// Traditional function calls
$result = trim(strtolower($input));

// Using the pipe operator
$result = $input
    |> strtolower(...)
    |> trim(...);

As you can see, the pipe operator allows for a more linear and readable flow of data transformations.

2. array_first() and array_last() functions.
#

PHP 8.5 will have new array functions: array_first() and array_last(). These functions may sound like a not a big deal, since PHP already got a sea of array functions. These functions complement the already provided array key methods introduced in PHP 7.3.

A seemingly easy problem like getting the first or last element from an array has many different solutions that can confuse PHP beginners.

// Function signatures
function array_first(array $values): mixed {}
function array_last(array $values): mixed {}

// Examples
array_first(["only item"]); // "only item"
array_last(["only item"]); // "only item"

array_first([]); // NULL
array_last([]); // NULL

array_first([1 => 'alpha', 0 => 'beta', 3 => 'gamma', 2 => 'delta']); // 'alpha'
array_last([1 => 'alpha', 0 => 'beta', 3 => 'gamma', 2 => 'delta']); // 'delta'

$greeting = "hello";
array_first([&$greeting, false]); // "hello" (no ref)
array_last([false, &$greeting]); // "hello" (no ref)

3. URI Extension
#

PHP 8.5 introduces a new URI extension that provides a standardized way to parse, manipulate, and validate Uniform Resource Identifiers (URIs). This is a standards-compliant parser “for both RFC 3986 and the WHATWG URL standard as an always-available part of its standard library within a new ‘URI’ extension.” Below is an example of how to use the URI extension from the PHP Foundation announcement:

use Uri\Rfc3986\Uri;
 
$url = new Uri('HTTPS://thephp.foundation:443/sp%6Fnsor/');
 
$defaultPortForScheme = match ($url->getScheme()) {
    'http' => 80,
    'https' => 443,
    'ssh' => 22,
    default => null,
};
 
// Remove default ports from URLs.
if ($url->getPort() === $defaultPortForScheme) {
    $url = $url->withPort(null);
}
 
// Getters normalize the URL by default. The `Raw`
// variants return the input unchanged.
 
echo $url->toString(), PHP_EOL;
// Prints: https://thephp.foundation/sponsor/
echo $url->toRawString(), PHP_EOL;
// Prints: HTTPS://thephp.foundation/sp%6Fnsor/

4. Retrieve the Currently Executing Closure
#

Recursion in closures has always been a bit tricky in PHP. In PHP 8.5, a new static function Closure::getCurrent() has been added to retrieve the currently executing closure. Previously, the workaround was binding a variable reference into a closure.

$seriesResolver = function (int $index) {
    if ($index === 0 || $index === 1) {
        return $index;
    }

    $selfInvoker = Closure::getCurrent();

    return $selfInvoker($index - 1) + $selfInvoker($index - 2);
};

echo $seriesResolver(10) . "\n";

5. Closures in Constant Expressions Support
#

Support for closures in constant expressions has been added in PHP 8.5. This allows developers to define closures as constant values. The RFC explains why this feature is introduced and useful:

“Several PHP constructs are limited to accept “constant expressions” only. These expressions may only contain a limited number of operations that can roughly be summarized as “immutable values”. Notably attribute parameters are a construct that only accepts constant expressions and Closures are not currently part of the set of allowed operations in constant expressions.

As Closures are effectively just PHP source code (or rather: PHP Opcodes) they are an immutable value (when limiting some of the features) and as such there is no fundamental reason why they should not be allowed within constant expressions. And indeed there are some use cases that would be enabled by allowing Closures to appear in constant expressions.”

function my_array_filter(
    array $values,
    Closure $predicate = static function ($element) { return !empty($element); },
) {
    $filtered = [];
 
    foreach ($values as $entry) {
        if ($predicate($entry)) {
            $filtered[] = $entry;
        }
    }
 
    return $filtered;
}
 
my_array_filter([
    0, 1, 2,
    '', 'foo', 'bar',
]); // [1, 2, "foo", "bar"]

6. PHP Fatal Error Backtraces
#

The new fatal_error_backtraces INI directive has been added in PHP 8.5 to control whether backtraces are included in fatal error messages. Fatal errors without backtraces in stable PHP versions might include parse errors (syntax errors), duplicate functions or classes, infinite loops with a max execution time, etc.

The default value of INI file in PHP 8.5 is set to fatal_error_backtraces=1, which includes backtraces in fatal error messages. If you want to disable backtraces in fatal error messages, you can set it to fatal_error_backtraces=0.

INI Diff Options
#

The php --ini command shows loaded php.ini configuration file, with additional .ini files parsed:

output of command php –ini

The new php --ini-diff command shows the difference between the default PHP configuration and the loaded configuration files. This is useful for debugging configuration issues.

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

Deprecations
#

Every new feature release also includes deprecations, serving as a warning of upcoming changes in the next major version. This allows developers to adapt their code early. Depending on the functionality, some deprecations may never require any action, such as if a function is never called. Others may necessitate a significant rewrite or refactor of the application.

Language/Syntax Deprecations
#

  1. Terminating a case statement with a semicolon. Always terminate with a colon.
  2. Non-standard cast names. Use int (not integer), float (not double), bool (not boolean), string (not binary).
  3. Usage of null as an array offset when calling array_key_exists().
  4. __sleep() and __wake() for serialization. Instead use __serialize() and __unserialize().

Standard Library Deprecations
#

  1. readdir(), rewinddir(), and closedir() deprecate usage of a null value for the $dir_handle argument.

Upgrading to PHP 8.5
#

It is generally recommended waiting for at least one bug fix release before adopting any new feature release. This applies to PHP 8.5 as well. The PHP release process involves multiple alpha, beta, and release candidates, but the 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 tools, to adapt to the new release. These tools can help identify potential issues with an upgrade.