PHP 8.5于2025年11月20日正式发布:十大核心改进
PHP 8.5于2025年11月20日正式发布,这标志着 PHP 语言在提高开发者效率和代码一致性方面迈出了重要一步。这个版本带来了一系列改进,包括 10 个重要的新特性 和 4 项废弃通知,旨在为开发者提供更流畅、更具表达力的编码体验。
以下是 PHP 8.5 版本的新特性总结:
1、管道运算符
对于希望将多个可调用对象链接在一起,并以原生方式从左到右传递值的 PHP 开发人员来说,管道运算符是一个令人兴奋的工具:
// Using the pipe operator in PHP 8.5
$result = "Hello World"
|> htmlentities(...)
|> str_split(...)
|> fn($x) => array_map(strtoupper(...), $x)
|> fn($x) => array_filter($x, fn($v) => $v != 'O');
2、原生 array_first() 和 array_last() 函数
PHP 8.5 将引入 array_first() 和 array_last() 函数。虽然表面上看,这些函数似乎无关紧要(而且 PHP 社区已经有用户实现和 polyfill),但作为 PHP 语言的原生函数,它们早就应该出现了。
这些函数是对 PHP 7.3 中已合并的数组键方法的补充:
在 PHP 7.3 中,我们有了 array_key_first() 和 array_key_last() 来获取数组的第一个和最后一个键。但我们目前还没有获取数组的第一个和最后一个值的方法。这比你想象的要难,因为:
数组键不一定是整数,也不一定从 0 开始,等等……
像 reset() 和 end() 这样的现有“技巧”在语义上是不正确的,因为它们会修改数组的“内部迭代器”。此外,它并非适用于所有类型的表达式(例如,函数返回的数组/普通数组可能会因为引用传递参数而触发警告)。
使用 $array[array_key_first($array)] 比较繁琐。
array_first() 和 array_last()工作原理的示例:
// Function signatures
function array_first(array $array): mixed {}
function array_last(array $array): mixed {}
// Examples
array_first(["single element"]); // "single element"
array_last(["single element"]); // "single element"
array_first([]); // NULL
array_last([]); // NULL
array_first([1 => 'a', 0 => 'b', 3 => 'c', 2 => 'd']); // 'a'
array_last([1 => 'a', 0 => 'b', 3 => 'c', 2 => 'd']); // 'd'
$str = "hello";
array_first([&$str, false]); // "hello" (no ref)
array_last([false, &$str]); // "hello" (no ref)
3、新的 URI 扩展
PHP 8.5 引入了一个新的 URI 扩展,它是一个符合标准的解析器,“同时支持 RFC 3986 和 WHATWG URL 标准,并作为其标准库中始终可用的一部分,包含在一个新的 ‘URI’ 扩展中。” RFC 中提供了大量示例,以下是 PHP 基金会公告文章中的一个示例,展示了 RFC 3986 的 URI 类:
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、获取当前正在执行的闭包
PHP 8.5 将支持闭包中的递归,方法是获取当前正在执行的闭包(感谢 Alexandre Daubois 的贡献)。正如 rfc:closure_self_reference RFC 中指出的,目前的解决方法是将变量引用绑定到闭包中。PHP 8.5 提供了静态方法 Closure::getCurrent() 来获取当前正在运行的闭包:
$fibonacci = function (int $n) {
if (0 === $n || 1 === $n) {
return $n;
}
$fn = Closure::getCurrent();
return $fn($n - 1) + $fn($n - 2);
};
echo $fibonacci(10) . "\n";
5、常量表达式中的闭包支持
PHP 8.5 引入了对常量表达式中闭包的支持,使得将默认属性值定义为闭包成为可能,以及其他一些用例:
function my_array_filter(
array $array,
Closure $callback = static function ($item) { return !empty($item); },
) {
$result = [];
foreach ($array as $item) {
if ($callback($item)) {
$result[] = $item;
}
}
return $result;
}
my_array_filter([
0, 1, 2,
'', 'foo', 'bar',
]); // [1, 2, "foo", "bar"]
6、PHP 致命错误回溯
新增的 fatal_error_backtraces 设置可以控制是否显示致命错误的回溯信息。在 PHP 8.5 中,fatal_error_backtraces 设置的默认值为 1——你无需进行任何配置即可获取这些回溯信息(当然,你也可以根据需要禁用它们)。在当前稳定的 PHP 版本(例如 PHP 8.4)中,一些致命错误可能没有回溯信息,例如解析错误(语法错误)、重复的函数或类、无限循环且执行时间超过上限等。
Fatal error: Cannot redeclare class B (previously declared in /srv/app/index.php:11) in /srv/app/b.php on line 3
Stack trace:
#0 /srv/app/index.php(6): require()
#1 /srv/app/index.php(21): A->loadClassB()
#2 {main}
7、INI 差异选项
PHP 8.5 为 php —ini 标志引入了 INI 差异选项,使识别配置中已更改的 INI 值变得容易。--ini 标志有助于显示已加载的 php.ini 配置文件以及解析的其他 .ini 文件:
$ php --ini=diff
Non-default INI settings:
allow_url_include: "0" -> ""
auto_append_file: (none) -> ""
...
8、PHP 8.5 废弃通知:4 项语言清理
PHP 8.5 继续朝着语言一致性迈进,引入了四项废弃通知,为 PHP 9.0 的最终清理做准备。非规范化标量类型转换废弃:废弃了四种非规范化的类型转换写法,要求开发者统一使用短格式或规范化名称:(integer) → 推荐使用 (int)(double) → 推荐使用 (float)(boolean) → 推荐使用 (bool)(binary) → 推荐使用 (string)
所有 MHASH* 常量废弃:由于 mhash 函数已在 PHP 8.1 中废弃,PHP 8.5 接着废弃了所有相关的 MHASH* 常量。应迁移到 hash() 函数,并使用哈希算法的字符串名称。
自定义输出缓冲处理器中返回非字符串值废弃:自定义的输出缓冲处理器(在 ob_start() 中设置的回调)现在必须返回一个字符串。返回非字符串值(如数组、true 等)将触发废弃通知。
自定义输出缓冲处理器中直接输出废弃:自定义的输出缓冲处理器不应该在自身内部产生任何输出(如使用 echo 或 print)。这种行为现在会触发废弃通知,因为处理器应该只通过返回字符串来修改缓冲区内容。

