Everything that is coming in PHP 8.5
As every year, we will have the new version of PHP this year too, which is PHP 8.5. It’s the minor version in the PHP 8 line, and the version will be released later this year. Let’s discuss everything that has been added in PHP 8.5 so far.
- The pipe operator
- The new array methods
- The
#[\NoDiscard]
attribute - Final Property Promotion
- Attributes on Constants
- Improved Directory class
- Fatal Error Backtraces
- Improvements to persistent cURL share handles
- First Class Callables in constant expressions
- Using Closures in Constant Expressions
- The error and exception handler functions
- New INI diff option
The pipe operator
PHP 8.5 will introduce the pipe operator (|>
), and it will allow you to write the code in a point-free style that limits the use of unnecessary intermediary variables.
So, the following code…
$value = "hello world";
$result1 = function3($value);
$result2 = function2($result1);
$result = function1($result2);
…can be rewritten using the pipe operator like this:
$value = "hello world";
$result = $value
|> function3(...)
|> function2(...)
|> function1(...);
The |>
operator, or “pipe”, accepts a single-parameter callable on the right and passes the left-side value to it, evaluating to the callable’s result. Pipe (|>
) evaluates left to right by passing the value (or expression result) on the left as the first and only parameter to the callable on the right.
Here’s a more real-world example:
$fullName = 'Fred Flintstone';
$result = $fullName
|> fn($x) => explode(' ', $x) // Produces an array of discrete words
|> fn($x) => implode('_', $x) // Join those words with _
|> strtolower(...) // Lowercase everything
;
// $result === 'fred_flintstone'
Read more about the pipe operator in this dedicated article.
The new array methods
PHP 8.5 will introduce two new functions, array_first()
and array_last()
that make it easier to get the first and last elements of an array without affecting the internal pointer and without using the 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
instead of throwing an error.
$first = array_first([]); // null
$last = array_last([]); // null
Read more about these functions in this article.
The #[\NoDiscard]
attribute
PHP 8.5 introduces the new #[\NoDiscard]
attribute, which allows developers to indicate that the return value of a function or method should not be ignored. If a developer calls a function marked with #[\NoDiscard]
and does nothing with the return value, PHP will emit a warning.
#[\NoDiscard("as the operation result is important")]
function performOperation(): int {
// Perform some operation
return 1; // 1 for success, 0 for failure
}
// Calling the function without using the return value
// Warning: The return value of function performOperation() is expected to be consumed, as the operation result is important in test.php on line 10
performOperation();
// Calling the function and using the return value
// This will not trigger a warning
$status = performOperation();
It’s a great way to ensure that developers don’t accidentally ignore important return values, especially in cases where the return value is crucial for the operation’s success or failure.
Read more about the #[\NoDiscard]
attribute in this article.
Final Property Promotion
PHP 8.5 will let you declare a property as final in the constructor property promotion syntax. This wasn’t possible before. The final keyword means that a property cannot be overridden in child classes.
class Example {
public function __construct(
final string $id
) {}
}
-
The property
$id
is now both promoted (created and initialized via the constructor) and final (cannot be overridden). -
If you use
final
, you don’t have to specify visibility (likepublic
orprivate
); it defaults topublic
, but you can still combinefinal
with visibility if you want.
This makes the constructor property promotion syntax consistent, and there will be less boilerplate code to write.
Attributes on Constants
As you may know, Attributes are a way to add metadata to code elements (like classes, methods, properties, etc.) using a special syntax. For example:
#[MyAttribute]
class MyClass {}
Now, with PHP 8.5, on top of things like classes, methods, and class constants, you can also add attributes to global (non-class) constants.
You can now add attributes to compile-time non-class constants, like:
#[MyAttribute]
const MY_CONST = 42;
One thing to keep in mind is that this does not work for constants defined with define()
, only those declared with const
.
You can now use ReflectionConstant::getAttributes()
to read attributes on constants at runtime. Additionally, the #[\Deprecated]
attribute is updated to allow targeting constants; when applied, the constant is marked with CONST_DEPRECATED
.
#[Deprecated(reason: 'Use NEW_API_URL instead. This 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 analyzers can now warn about using OLD_API_URL
$data = file_get_contents(OLD_API_URL);
return $data;
}
Improved Directory class
PHP 8.5 changes PHP’s Directory class to behave like a strict, non-modifiable resource object. Essentially, the Directory
class in PHP is used to represent directory handles, usually created by the dir()
function. Historically, it behaved like a regular class, but this could lead to bugs and misuse (e.g., creating invalid Directory objects with new Directory()
).
So, to fix this, PHP 8.5 makes the Directory
class behave like a true “resource object” (sometimes called an “opaque object”).
This means:
- Final Class: You cannot extend (inherit from) the Directory class.
- No Direct Instantiation: You cannot create a Directory object with
new Directory()
. Only thedir()
function can create valid instances. - No Cloning: You cannot clone a Directory object.
- No Serialization: You cannot serialize or unserialize Directory objects.
- No Dynamic Properties: You cannot add new properties to Directory objects at runtime.
Now, when you try to create a Directory object using new Directory()
, it will throw an error:
$dir = new Directory(); // Throws an Error
$dir = dir('/tmp'); // Correct way to get a Directory object
Fatal Error Backtraces
PHP 8.5 will add automatic stack traces to PHP fatal error messages, making debugging much easier.
Today, in PHP, when a fatal error (like running out of execution time or memory) happens, the error message does not include a backtrace (a list of function calls that led to the error). Without a backtrace, it’s hard to figure out what caused the error, especially in large or complex codebases.
To mitigate this, PHP 8.5 introduces a new INI setting:
fatal_error_backtraces
.
When enabled (and it may be enabled by default), PHP will print a stack trace for fatal errors. The stack trace shows the sequence of function calls that led to the error, similar to what you see with exceptions.
Here’s what the backtraces looked like previously:
Fatal error: Maximum execution time of 1 second
exceeded in example.php on line 7
With the new INI setting enabled, it will look like this:
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 things about the new error backtraces:
- Only fatal errors (not warnings or notices) get backtraces, to avoid performance and memory issues.
- The backtrace respects privacy settings (like
SensitiveParameter
attributes). - The backtrace is also available programmatically via
error_get_last()
in shutdown functions.
Improvements to persistent cURL share handles
PHP 8.5 improves how PHP manages persistent cURL share handles, making them safer and easier to use.
Essentially, cURL share handles let multiple cURL requests share data (like DNS cache) for efficiency. PHP recently added support for persistent cURL share handles, which can be reused across multiple PHP requests. The original design had some risks and usability issues, especially around sharing cookies and managing persistent IDs.
PHP 8.5 introduces a new curl_share_init_persistent()
function that creates a persistent cURL share handle. This function is safer and more consistent than the previous curl_share_init()
. You no longer need to provide a custom persistent ID; PHP manages this automatically based on the options you pass. If you call the function again with the same options, you get the same handle (it’s reused).
$options = [CURL_LOCK_DATA_DNS];
$shareHandle = curl_share_init_persistent($options);
// Use $shareHandle with CURLOPT_SHARE in your cURL requests
curl_setopt($ch, CURLOPT_SHARE, $shareHandle);
This improvement is important since it prevents accidental sharing of sensitive cookies. On top of that, the developers don’t have to manage persistent IDs or worry about handle mismatches.
First Class Callables in constant expressions
PHP 8.5 will allow first-class callables (FCCs) to be used in PHP constant expressions.
As you may know, first-class callables are a concise way to reference functions or static methods as callable objects using the ...
syntax. For instance:
// $callable is now a callable to strlen()
$callable = strlen(...);
// callable to MyClass::myMethod()
$callable = MyClass::myMethod(...);
Previously, you could not use FCCs inside constant expressions (like default property values, attribute arguments, or constant definitions). PHP 8.5 changes this, allowing you to use FCCs in constant expressions.
const MY_CALLABLE = strlen(...);
#[Attr(self::myMethod(...))]
class C {}
This makes using FCCs more consistent across PHP and makes it convenient to define reusable callables as constants, use them in attributes, or as default values, making code more expressive and DRY.
Using Closures in Constant Expressions
PHP 8.5 will allow static closures (anonymous functions) to be used in PHP constant expressions. To understand what’s changed, let’s first understand what a constant expression is.
A constant expression in PHP is a value that must be fully determined at compile time. Examples include:
- Default values for function parameters
- Attribute arguments
- Class constants
- Property default values
Before PHP 8.5, you could not use closures (anonymous functions) in constant expressions. This has changed now in PHP 8.5. So, you can do something like this.
function my_array_filter(
array $array,
Closure $callback = static function ($item) { return !empty($item); }
) {
// ...
}
Or use closures as attribute arguments, property defaults, or class constants.
There are a few constraints to this:
-
Must be static: The closure cannot use
$this
or capture variables from the surrounding scope (nouse($foo)
and no arrow functions). -
No variable capturing: Only pure, static closures are allowed, because constant expressions can’t depend on runtime values.
-
Checked at compile time: PHP will give an error if you try to use a non-static closure or one that captures variables.
The ability to use Closures in constant expressions allows you to write cleaner code since you can provide default callbacks or validators directly in function signatures, attributes, or constants.
Apart from this, libraries can use closures in attributes for things like validation, formatting, or test case generation, like so.
final class Locale
{
#[Validator\Custom(static function (string $languageCode): bool {
return preg_match('/^[a-z][a-z]$/', $languageCode);
})]
public string $languageCode;
}
Overall, it’s a good improvement to make the code more expressive and concise.
The error and exception handler functions
PHP 8.5 will add get_error_handler()
and get_exception_handler()
functions to PHP, letting you 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 handler is.
Developers had to use a workaround: set a new handler, save the old one, then restore it, which is an awkward 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, or null if none is set.
Here’s how you can 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 may guess, this makes the process of setting the handlers reliable since you get the exact handler you set, making debugging and handler management easier.
Also, frameworks and libraries can now safely check or restore handlers without side effects.
New INI diff option
PHP 8.5 introduces the INI diff CLI option to show all INI settings that differ from PHP’s built-in defaults.
php --ini=diff
This command lists all INI configuration directives that have been changed from their built-in default values. It’s a quick way to see which settings have been customized in your environment, which is especially useful for debugging, troubleshooting, or sharing configuration differences.
Here’s what an example output looks like.
Non-default INI settings:
memory_limit: "128M" -> "512M"
display_errors: "1" -> ""
date.timezone: "UTC" -> "Europe/Amsterdam"
Here, the left value is the default, and the right value is the current setting. Apart from this, only settings that differ from the default are shown.
This quickly lets you spot configuration changes that might affect application behavior and easily compare different environments (dev, staging, production).
This also lets you see what’s changed in containerized or automated setups.
👋 Hi there! This is Amit, again. I write articles about all things web development. If you enjoy my work (the articles, the open-source projects, my general demeanour... anything really), consider leaving a tip & supporting the site. Your support is incredibly appreciated!