PHP 社区已发布 8.5 版本的开源脚本语言,该语言为 Web 上的许多网站提供支持,包括使用 WordPress CMS 的网站。
PHP 8.5 于 11 月发布,标志着 PHP 社区承诺每年发布一次重大更新的第二年,每个版本之后还有两年的全面支持。
虽然 8.5 是全新版本,但我们已经在一系列流行的 CMS 平台和框架的年度 PHP 基准测试中涵盖了它。
如果您计划将 PHP 应用程序迁移到 8.5 版本,您需要了解此最新版本中有哪些变化。这包括您可能能够利用来改进代码的新功能,以及 PHP 开发人员准备移除的旧功能。
以下是我们认为此新版本的亮点:
PHP 8.5 中的新功能和改进
让我们从 PHP 代码库的新增内容开始。这些更改通常始于评论请求 (RFC),最终可能会被批准并分配给未来的 PHP 版本。
下面描述的新功能是 PHP 8.5 中最受关注的功能。
使用管道运算符链接函数调用
新的管道(|)运算符以类似于 JavaScript 程序员的方式链接函数调用。管道从左到右操作,在每一步传递一个单一值。
在之前的 PHP 版本中,程序员可能通过嵌套函数或对每一步返回的值进行一系列函数调用来完成类似的任务。
以下是使用新管道运算符的简单示例:
$text = ' New-in-php-8.4 ';
$result = $text
|> trim(...)
|> (fn($str) => str_replace('4', '5', $str))
|> (fn($str) => str_replace('-', ' ', $str))
|> strtoupper(...);
var_dump($result);
// string(14) "NEW IN PHP 8.5"
(请注意,我们使用的是 PHP 8.1 中引入的一流可调用语法 (...),将其与 trim() 和 strtoupper() 函数调用一起使用。)
上面的管道链可以写在一行上,但可读性应该是这个新运算符的好处之一。
上述内容相当于(按相反顺序)嵌套这些操作,如下所示:
$text = " New-in-php-8.4 ";
$result = strtoupper(
str_replace('-, ' ',
str_replace('4', '5',
trim($text)
)
)
);
或者,程序员可能在早期 PHP 版本中这样完成任务:
$text = " New-in-php-8.4 ";
$result = trim($text);
$result = str_replace('4', '5', $result);
$result = str_replace('-, ' ', $result);
$result = strtoupper($result);
使用新的 URI 扩展解析 URL
URL(对于更严格的人来说也称为 URI)对 Web 导航至关重要,但自 PHP 4 版本起内置的 parse_url() 函数在处理格式错误的输入时 известен(即众所周知)存在困难,这可能导致在尝试操作或验证网站地址时出现错误。
为了改进 URL 解析,PHP 8.5 整合了 uriparser 和 Lexbor 库,分别支持 RFC 3986 和 WHATWG URL 标准。
您可以通过如下方式使用新的 URI 扩展来调用 uniparser 库:
$uri = new Uri\Rfc3986\Uri("https://kinsta.com/blog/php-8-5/");
echo $uri->getScheme(); // https
echo $uri->getHost(); // kinsta.com
echo $uri->getPath(); // /blog/php-8-5
或者,您可以选择 Lexbor WHATWG URL 库,如下所示:
$uri = new Uri\WagWg\Url("https://kinsta.com/blog/php-8-5/");
echo $uri->getScheme(); // https
echo $uri->getUnicodeHost(); // kinsta.com
echo $uri->getAsciiHost(); // kinsta.com
echo $uri->getPath(); // /blog/php-8-5
上面的例子是最基础的。PHP 8.5 中 URI 扩展代表的两个库有一些共同的功能,但也有显著的差异。
一个重要的区别是,RFC 3986 库支持“原始”和“规范化解码”两种 URI 表示方式。这在处理百分比编码的输入和输出时非常有用。例如,在浏览器中,这两个 URI 是相同的:
在之前的 PHP 版本中,你可能需要使用 rawurldecode() 和 rawurlencode()(它们也符合 RFC 3986),但新扩展已经可以直接处理 URI 的所有组成部分,无论它们是否已编码。
以下示例来自新解析 API 的 RFC:
$uri = new Uri\Rfc3986\Uri("https://%61pple:p%61ss@ex%61mple.com/foob%61r?%61bc=%61bc");
echo $uri->getRawUserInfo(); // %61pple:p%61ss
echo $uri->getUserInfo(); // apple:pass
echo $uri->getRawUsername(); // %61pple
echo $uri->getUsername(); // apple
echo $uri->getRawPassword(); // p%61ss
echo $uri->getPassword(); // pass
echo $uri->getRawHost(); // ex%61mple.com
echo $uri->getHost(); // example.com
echo $uri->getRawPath(); // /foob%61r
echo $uri->getPath(); // /foobar
echo $uri->getRawQuery(); // %61bc=%61bc
echo $uri->getQuery(); // abc=abc
当使用 WHATWG URL 库与新扩展时,所有 URI 都被视为“原始”形式,因此没有单独的函数集来支持备用格式。但该库可以在 URI 中常见的 ASCII 和 Unicode 字符之间进行转换。
使用新的 max_memory_limit INI 指令严格控制内存
人们说,能力越大,责任越大。如果这种能力包括选择你的 PHP 应用程序可能尝试使用的服务器内存量,那么当进程消耗的内存超过可用内存时,你可能要为应用程序崩溃负责。
典型 PHP 安装的一部分是 php.ini 文件,其中包含配置信息,包括为任何 PHP 进程(或线程)指定内存消耗限制的指令。128 MB 内存限制的常见 INI 指令如下:
// php.ini
memory_limit 128M
在某些托管平台上,PHP 应用程序的开发者可以使用代码中的 ini_set() 函数动态覆盖 memory_limit:
ini_set('memory_limit', '256M');
// 开始需要最多 256 MB 内存的代码
你也可以将函数的值设为 -1,如 ini_set('memory_limit', '-1')——完全不限值。
对于不熟悉其应用程序运行所在服务器内存配置的开发者来说,覆盖内存限制的 INI 指令可能会有风险。如果一个或多个 PHP 线程尝试消耗超过总内存池的内存,结果可能是应用程序在运行时无警告地崩溃。
PHP 8.5 添加了 max_memory_limit INI 指令,即使在开发者可以访问 init_set() 来调整代码中内存使用的配置中,它也作为硬性上限。
以下是 PHP 8.5 安装的 php.ini 文件中的示例条目:
// php.ini
max_memory_limit 256M
memory_limit 128M
当 max_memory_limit 为 256 MB 时,我们的 PHP 代码中会发生以下情况:
ini_set('memory_limit', '256M'); // 这是允许的
ini_set('memory_limit', '512M'); // 失败并发出警告
ini_set('memory_limit', '-1'); // 失败并发出警告
尝试将新的内存限制设置为 512 MB(或无限制)不会成功。相反,PHP 会将内存限制设置为 php.ini 文件中分配给 max_memory_limit 的值并发出警告。(警告消息可能会根据 PHP 安装的错误报告设置显示在屏幕上并被记录。)
对于 PHP 8.5 开发人员来说,明智的方法是使用 ini_get() 函数来查看是否已定义新的最大限制,例如 ini_get('max_memory_limit')——然后根据返回的值进行调整。在 PHP 8.5 之前的版本中,该调用会安全地返回 false。
获取数组中的第一个或最后一个值
如果你认为 PHP 已经有函数来读取存储为数组第一个或最后一个成员的值,请举手。
原来,它没有。但自 PHP 7.3 以来,它有了揭示数组中第一个和最后一个键的函数。因此,要找到第一个和最后一个值,你可以使用 array_key_first() 或 array_key_last() 函数,然后使用返回的键来引用你要查找的值:
$array = ["One", "Two", "Three"];
echo $array[array_key_first($array)]; // "One"
PHP 8.5 为这项任务省去了一步,允许你通过新的 array_first() 和 array_last() 函数直接获取值。
这非常简单:
$array = ["One", "Two", "Three"];
echo array_first($array); // "One"
echo array_last($array); // "Three"
echo array_last([]); // null
如上所示,空数组将返回 null,但仅凭这一点并不能确认整个数组是空的,因为数组值也可以是 null:
echo array_last([1, 2, null]); // null
提醒使用函数的返回值
PHP 5.8 添加了一个新的 #[\NoDiscard] 属性,表示函数的返回值可能很重要。PHP 将确认返回值以某种方式被使用,如果没有,则会触发警告。
一个简单的例子:
#[\NoDiscard("此消息属性将附加到内置警告中。")]
function foo(): string {
return 'bar';
}
// 警告:
// 预期要使用函数 foo() 的返回值,
// 此消息属性将附加到内置警告中。
foo();
// 这不会触发警告:
$result = foo();
// (void) 强制转换也可以:
(void) foo();
在上面的例子中,定义函数的返回值在第一次调用时根本没有被使用,从而触发警告。但当赋值给变量 $result 或强制转换为 void 时,该值被认为是已被使用。
该 PHP 8.5 特性背后的 RFC 作者描述了比上面简单例子更引人注目的用途。一种场景是具有比简单通过/失败更复杂的错误报告的关键函数,并且最好通过函数的返回值来传达。
与属性相关的其他增强
除了新的 #[\NoDiscard] 属性外,新版本中属性元数据功能的其他增强包括:
- 属性现在可以指向常量。
#[\Override]属性现在可以应用于属性。#[\Deprecated]属性可以用于 trait 和常量。- 新的
#[\NoDiscard]属性可以用于禁止在无效目标上使用的核心和扩展属性触发编译时错误。
PHP 8.5 中的弃用和移除
每个 PHP 版本都会列出在将来版本中将被移除的功能列表。使用代码中已弃用的功能将触发警告。当最终从 PHP 中移除时,其使用可能导致致命错误。
以下是 PHP 8.5 中已弃用或移除的一些重要元素:
- 反引号操作符作为
shell_exec()的别名已被废弃。 - 非标准类型转换名称
(boolean)、(integer)、double和(binary)已被废弃。请改用(bool)、(int)、float和(string)。 - disable_classes INI 设置已被移除,因为它会导致各种引擎假设被破坏。
- 使用分号而非冒号终止
case语句已被废弃。 - 使用 null 作为数组偏移量或调用
array_key_exists()时使用 null 现已被废弃。请改用空字符串。 - 在
class_alias()中使用 "array" 和 "callable" 作为类别名已不再可行。 __sleep()和__wakeup()魔术方法已被软废弃。应改用__serialize()和__unserialize()魔术方法。- 将 NAN 转换为其他类型时现在会发出警告。
- 使用
[]或list()解构非数组值(null 除外)现在会发出警告。 - 将浮点数(或看起来像浮点数的字符串)转换为
int(如果无法表示)时现在会发出警告。
总结
以上就是 PHP 8.5 版本的主要亮点。我们相信新的管道操作符和改进的 URI 解析将会受到开发者的欢迎。也许还有新的 array_first() 和 array_last() 函数,我们本来打赌它们已经存在了。
但任何新的 PHP 版本都包含数百项更改。您可以在 PHP 小组的官方 GitHub 仓库中找到 完整的 PHP 8.5 更新列表。
同时,在 Kinsta,我们正在努力为我们的 WordPress 主机 客户推出 PHP 8.5。上线后,您将能够使用我们的 PHP 设置 工具切换到新版本。




