tiantian
Newbie

PHP 8.5: A Complete Breakdown of 14 New Features

Edited on 3months ago

As in previous years, we shall welcome a new version of PHP this year: PHP 8.5, scheduled for official release on 20 December 2025. This constitutes a minor release within the PHP 8 series, slated for later this year. Let us now discuss all the new features currently incorporated into PHP 8.5. Some features have been detailed in prior articles. This piece shall directly outline all functionality slated for implementation in 8.5.


Overview of New Features

• Pipe operator

• New array methods

• #[\NoDiscard] attribute

• Final attribute promotion

• Attributes on constants

• Improved Directory class

• Fatal error backtrace

• Persistent cURL shared handle improvements

• First-class callable objects in constant expressions

• Using closures in constant expressions

• Error and exception handling functions

• Asymmetric visibility for static properties

• New Levenshtein distance function for glyph clusters

• New INI diff options

Pipeline Operator

PHP 8.5 will introduce the pipeline operator (|>), enabling dotless code style and reducing unnecessary intermediate variables.


Thus, the following code...


$value = "hello world";
$result1 = function3($value);
$result2 = function2($result1);
$result = function1($result2);


...can be rewritten using the pipe operator as:


$value = "hello world";

$result = $value
|> function3(...)
|> function2(...)
|> function1(...);


The |> operator, also known as the ‘pipe’, accepts a single callable object on its right-hand side and passes the value on its left-hand side to it, returning the result of that callable object. The pipe (|>) evaluates from left to right, passing the value on the left (or the result of an expression) as the first and only argument to the callable object on the right.


Here is a more realistic example:


$fullName = “Fred Flintstone”;

$result = $fullName
|> fn($x) => explode(“ ”, $x) // Produces an array of discrete words
|> fn($x) => implode(“_”, $x) // Join these words with underscores
|> strtolower(...) // Convert all to lowercase;

// $result === 'fred_flintstone'


New Array Methods

PHP 8.5 will introduce two new functions, array_first() and array_last() , which simplify retrieving the first and last elements of an array without affecting the internal pointer or utilising keys.


$array = [1, 2, 3, 4, 5];
$first = array_first($array); // 1
$last = array_last($array); // 5

$array = ['a' => 1, 'b' => 2, 'c' => 3];
$first = array_first($array); // 1
$last = array_last($array); // 3


For empty arrays, both functions return null rather than throwing an error.


$first = array_first([]); // null
$last = array_last([]); // null


#[\NoDiscard] attribute

PHP 8.5 introduced the new #[\NoDiscard] attribute, which enables developers to indicate that a function or method's return value should not be ignored. Should a developer call a function marked with #[\NoDiscard] without processing its return value, PHP will issue a warning.


#[\NoDiscard(‘As the operation result is critical’)]
function performOperation(): int {
// Execute certain operations
return 1; // 1 indicates success, 0 indicates failure
}

// Calling the function without using the return value
// Warning: On line 10 of test.php, the return value of function performOperation() is expected to be used, as the operation result is critical
performOperation();

// Calling the function and using the return value
// This does not trigger a warning
$status = performOperation();


This is an effective method to ensure developers do not inadvertently overlook crucial return values, particularly when such values are pivotal to determining the success or failure of an operation.


Final Property Promotion


PHP 8.5 will permit you to declare properties as final within constructor property promotion syntax. This was previously impossible. The final keyword signifies that the property cannot be overridden in subclasses.


class Example {
public function __construct(
final string $id
) {}
}


The property $id is now both hoisted (created and initialised via the constructor) and final (cannot be overridden).


When using final, you need not specify visibility (such as public or private); it defaults to public. However, you may still combine final with visibility modifiers if required.


This maintains consistency in constructor property hoisting syntax and reduces the amount of boilerplate code required.


Properties on Constants

As you know, properties are a method of adding metadata to code elements (such as classes, methods, properties, etc.) using special syntax. For example:



#[MyAttribute]
class MyClass {}


Now, in PHP 8.5, you can add properties to global (non-class) constants, in addition to classes, methods, and class constants.


You can now add properties to non-class constants at compile time, such as:


#[MyAttribute]
const MY_CONST = 42;


One point to bear in mind is that this does not apply to constants defined using define(), but only to those declared with const .


You can now use ReflectionConstant::getAttributes() to read attributes on constants at runtime. Furthermore, the #[\Deprecated] attribute has been updated to permit application against constants; when applied, the constant is marked as CONST_DEPRECATED .


#[Deprecated (Reason: “Please use NEW_API_URL instead. This interface will be removed in v2.0.”)]
const OLD_API_URL = “https://old-api.example.com”;

const NEW_API_URL = 『https://api.example.com』;

// Usage example
function fetchData() {
// IDEs and static analysers may now issue warnings regarding OLD_API_URL
$data = file_get_contents(OLD_API_URL);
return $data;
}


Enhanced Directory Class

PHP 8.5 has altered PHP's Directory class to behave as a strict, immutable resource object. Essentially, PHP's Directory class represents directory handles, typically created by the dir() function. Historically, it behaved like a conventional class, but this could lead to errors and misuse (such as creating invalid Directory objects with new Directory() ).


Therefore, to address this issue, PHP 8.5 makes the Directory class behave like a true ‘resource object’ (sometimes termed an ‘opaque object’).


This means:


• Final class: You cannot extend (inherit from) the Directory class.

• Cannot be directly instantiated: You cannot create Directory objects using new Directory(). Only the dir() function can create valid instances.

• Cannot be cloned: You cannot clone Directory objects.

• Cannot be serialised: You cannot serialise or deserialise Directory objects.

• No dynamic properties: You cannot add new properties to Directory objects at runtime.

Now, attempting to create a Directory object using new Directory() will result in an error:



$dir = new Directory(); // Throws an error
$dir = dir(“/tmp”); // The correct method for obtaining a Directory object


Fatal Error Backtraces

PHP 8.5 will introduce automatic stack traces for PHP fatal error messages, facilitating easier debugging.


Currently in PHP, when a fatal error occurs—such as when execution time or memory is exhausted—the error message does not include a backtrace (the list of function calls leading to the error). Without a backtrace, pinpointing the cause of the error proves challenging, particularly within large or complex codebases.


To alleviate this issue, PHP 8.5 introduces a new INI setting: fatal_error_backtraces .


When enabled (likely by default), PHP will print stack traces for fatal errors. These backtraces display the sequence of function calls leading to the error, similar to what is seen with exceptions.


Below is how backtraces appeared previously:



Fatal error: Maximum execution time of 1 second
exceeded in example.php on line 7


After enabling the new INI settings, it will appear as follows:


Fatal error: Maximum execution time of 1 second exceeded
in example.php on line 6
Stack trace:
#0 example.php(6): usleep(100000)
#1 example.php(7): recurse()
#2 example.php(7): recurse()
...
#11 {main}


Key points regarding new error backtracking:


• Only fatal errors (not warnings or notices) are backtracked to avoid performance and memory issues.

• Backtracking respects privacy settings (such as the SensitiveParameter attribute).

• Backtracking can also be obtained programmatically by disabling error_get_last() within functions.



Improvements to Persistent cURL Shared Handles

PHP 8.5 enhances PHP's management of persistent cURL shared handles, making them more secure and easier to use.


Essentially, cURL shared handles enable multiple cURL requests to share data (such as DNS caches) for efficiency. PHP recently added support for persistent cURL shared handles, which can be reused across multiple PHP requests. The original design presented certain risks and usability issues, particularly concerning shared cookies and managing persistent IDs.


PHP 8.5 introduces a new curl_share_init_persistent() function, which creates a persistent cURL shared handle. This function is more secure and consistent than the previous curl_share_init() . You no longer need to provide a custom persistent ID; PHP automatically manages this based on the options you pass. If you call the function again with the same options, you will receive the same handle (it is reused).


$options = [CURL_LOCK_DATA_DNS];
$shareHandle = curl_share_init_persistent($options);

// In your cURL request, use CURLOPT_SHARE to set the $shareHandle.
curl_setopt($ch, CURLOPT_SHARE, $shareHandle);


This improvement is significant as it prevents the accidental sharing of sensitive cookies. Furthermore, developers need not manage persistent IDs or worry about handle mismatches.


First-Class Callable Objects in Constant Expressions

PHP 8.5 will permit the use of first-class callable objects (FCCs) within PHP constant expressions.


As you know, first-class callable objects provide a concise way to reference functions or static methods as callable objects using the ... syntax. For example:


// $callable is now the callable object for strlen()
$callable = strlen(...);

// The callable object for MyClass::myMethod()
$callable = MyClass::myMethod(...);


Previously, you could not use function constancy clauses (FCCs) within constant expressions, such as default property values, property parameters, or constant definitions. PHP 8.5 has changed this, permitting the use of FCCs within constant expressions.


const MY_CALLABLE = strlen(...);

#[Attr(self::myMethod(...))]
class C {}


This makes the use of FCCs in PHP more consistent and facilitates defining reusable callable objects as constants, utilising them in properties or as default values, thereby enhancing code expressiveness and adhering to the DRY principle.


Using Closures in Constant Expressions

PHP 8.5 will permit the use of static closures (anonymous functions) within PHP constant expressions. To comprehend the changes, let us first understand what a constant expression is.


Constant expressions in PHP are values that must be fully determined at compile time. Examples include:


• Default values for function parameters

• Property parameters

• Class constants

• Property default values

Prior to PHP 8.5, you could not use closures (anonymous functions) within constant expressions. This has changed in PHP 8.5. Consequently, you can now do things like this.



function my_array_filter(
array $array,
Closure $callback = static function ($item) { return !empty($item); }
) {
// ...
}


Alternatively, closures may be employed as property parameters, property default values, or class constants.


Several constraints apply:


They must be static: Closures cannot utilise $this or capture variables from the surrounding scope (no use($foo) and no arrow functions).


No variable capture: Only pure static closures are permitted, as constant expressions cannot depend on runtime values.


Compile-time checks: Attempting to use non-static closures or closures capturing variables will result in PHP errors.


The ability to use closures within constant expressions enables cleaner code, as default callbacks or validators can be provided directly within function signatures, properties, or constants.


Beyond this, libraries may utilise closures within properties for validation, formatting, or test case generation, as demonstrated below.



final class Locale
{
#[Validator\Custom(static function (string $languageCode): bool {
return preg_match('/^[a-z][a-z]$/', $languageCode);
})]
public string $languageCode;
}


Overall, this is a commendable improvement that renders the code more expressive and concise.


Error and Exception Handling Functions

PHP 8.5 will introduce the get_error_handler() and get_exception_handler() functions, enabling you to directly retrieve the current error and exception handlers.


In PHP, you can set custom error and exception handlers using set_error_handler() and set_exception_handler(). However, there was no direct way to check what the current handlers were.


Developers had to resort to a workaround: setting a new handler, saving the old one, then restoring it – a cumbersome and error-prone process.


PHP 8.5 introduces two new functions:


• get_error_handler(): ?callable

• get_exception_handler(): ?callable

These functions return the currently registered error or exception handler, returning null if none is set.


Here is how to use them.



set_error_handler(null);
get_error_handler(); // null

$handler = [$this, 'error_handler'];
set_error_handler($handler);
get_error_handler() === $handler; // true

$new_handler = $this->error_handler(...);
$old_handler = set_error_handler($new_handler);
get_error_handler() === $new_handler; // true
restore_error_handler();
get_error_handler() === $old_handler; // true


As you might expect, this makes the process of setting handlers reliable, as you obtain precisely the handler you set, thereby simplifying debugging and handler management.


Moreover, frameworks and libraries can now safely inspect or restore handlers without side effects.


Asymmetric Visibility for Static Properties

PHP 8.5 will permit static properties in PHP to possess asymmetric visibility, meaning their read and write accesses can have differing visibility levels.


To give you a brief introduction, asymmetric visibility introduced in PHP 8.4 enables you to set different access levels for reading and writing properties. For instance, a property can be public for reading but private or protected for writing.


Previously, asymmetric visibility applied only to object (instance) properties. In PHP 8.5, you can now also utilise it for static properties.



class Example
{
publicprivate(set) staticstring$classTitle = “Example class”;
// Anyone may read Example::$classTitle, but only the class itself may alter it.

protected(set) staticint$counter = 0;
// Anyone may read Example::$counter, but only the class or its subclasses may alter it.

public static function changeName(string $name): void
{
self::$classTitle = $name; // Permitted (within class)
}
}

echo Example::$classTitle; // Permitted (public read)
Example::$classTitle = “Nope”; // Not permitted (private write)


This renders the asymmetric visibility characteristic more consistent across languages.


New grapheme cluster Levenshtein function

PHP 8.5 introduces a new PHP function, grapheme_levenshtein(), which precisely compares strings using grapheme clusters (user-perceived characters) rather than bytes or code points.


Essentially, the Levenshtein distance measures how many single-character edits (insertions, deletions, substitutions) are required to transform one string into another. PHP's existing levenshtein() and mb_levenshtein() functions operate on bytes or Unicode code points, which may yield incorrect results for complex Unicode characters such as emojis or accented letters.


Grapheme clusters are the single characters visible to users, even if they comprise multiple Unicode code points (e.g., ‘é’ may be a single code point or ‘e’ plus a combining accent).


To accommodate this, PHP 8.5 introduced the grapheme_levenshtein() function, which calculates Levenshtein distance based on grapheme clusters. This means it treats visually identical characters as equivalent, even if their underlying encoding differs.



var_dump(
grapheme_levenshtein(
‘\u{0065}\u{0301}’,
‘\u{00e9}’
)
); // Result: 0


Here, both strings display as ‘é’ to the user, yet they possess different encodings. The new function correctly treats them as equal.


New INI diff option

PHP 8.5 introduces the INI diff CLI option, which displays all INI settings differing from PHP's built-in defaults.


php --ini=diff


This command lists all INI configuration directives that have been altered from their built-in default values. It provides a swift method for identifying which settings within your environment have been customised, proving particularly useful for debugging, troubleshooting, or sharing configuration discrepancies.


The following illustrates what the output might look like.



Non-default INI settings:
memory_limit: "128M" -> "512M"
display_errors: "1" -> ""
date.timezone: "UTC" -> "Europe/Amsterdam"


Here, the value on the left is the default setting, while the value on the right is the current configuration. Beyond this, only settings differing from the default are displayed.


This enables you to swiftly identify configuration changes that may impact application behaviour and effortlessly compare settings across different environments (development, testing, production).


It also allows you to observe what alterations have occurred within containerised or automated setups.

Login
{{error.username}}
{{error.password}}
or
Register
{{error.username}}
{{error.nickname}}
{{error.email}}
{{error.password}}
{{error.repassword}}
Forget the password
{{error.email}}
{{error.code}}
Reply:{{reply.touser}}
Edit
Allow cookies on this browser?

All cookies currently used by FreeTalkHub are strictly necessary. Our cookies are employed for login authentication purposes and utilise Google's one-click login functionality, serving no other purpose.