简介
PHP 8.5 引入了许多令人兴奋的新特性,包括管道运算符(Pipe Operator)、URI 扩展、Clone With 语法、#[\NoDiscard] 属性等。本文将介绍主要变化和如何从 PHP 8.4 迁移到 PHP 8.5。
参考资源
- 在线测试环境:https://3v4l.org/
- 官方发布说明:https://www.php.net/releases/8.5/en.php
- 迁移指南:https://www.php.net/manual/en/migration85.php
- 更新详情:https://php.watch/versions/8.5
PHP 8.5 新特性
管道运算符(Pipe Operator)
PHP 8.5 引入了管道运算符 |>,使函数链式调用更加清晰易读:
// PHP 8.5 之前 - 嵌套函数调用,难以阅读
$slug = strtolower(str_replace('.', '', str_replace(' ', '-', trim($title))));
// PHP 8.5 - 使用管道运算符,从左到右清晰阅读
$slug = $title
|> trim(...)
|> (fn($str) => str_replace(' ', '-', $str))
|> (fn($str) => str_replace('.', '', $str))
|> strtolower(...);
管道运算符将左侧的值作为右侧函数的第一个参数传递,实现函数组合的链式调用。
URI 扩展
PHP 8.5 新增了内置的 URI 扩展,遵循 RFC 3986 和 WHATWG URL 标准来解析和处理 URL。
URI 与 URL 的关系:URI(统一资源标识符)用于标识资源,URL(统一资源定位符)用于定位资源。所有 URL 都是 URI,但不是所有 URI 都是 URL。该扩展命名为 URI 是因为它能处理更广泛的标识符格式。
示例:
// PHP 8.5 之前
$components = parse_url('https://php.net/releases/8.5/en.php');
var_dump($components['host']); // "php.net"
// PHP 8.5 - 使用新的 URI 扩展
use Uri\Rfc3986\Uri;
$uri = new Uri('https://php.net/releases/8.5/en.php');
var_dump($uri->getHost()); // "php.net"
var_dump($uri->getPath()); // "/releases/8.5/en.php"
var_dump($uri->getScheme()); // "https"
新的 URI 扩展由 uriparser 和 Lexbor 库驱动,提供更安全、更符合标准的 URI 解析 API。
Clone With 语法
PHP 8.5 简化了对象克隆时更新属性的操作,特别适用于 readonly 类的 “with-er” 模式。
什么是 “with-er” 模式:一种用于不可变对象的设计模式,方法名以
with开头(如withId()、withName()),不修改原对象,而是返回一个修改了某个属性的新对象。常见于 PSR-7 HTTP 消息接口和 DateTimeImmutable 等。
示例:
class Color {
public function __construct(
public readonly int $red,
public readonly int $green,
public readonly int $blue,
public readonly int $alpha = 255,
) {}
// PHP 8.5 之前
public function withAlphaOld(int $alpha): self {
$values = get_object_vars($this);
$values['alpha'] = $alpha;
return new self(...$values);
}
// PHP 8.5 - 使用 clone with
public function withAlpha(int $alpha): self {
return clone($this, ['alpha' => $alpha]);
}
}
$color = new Color(255, 128, 0);
$transparent = $color->withAlpha(128);
#[\NoDiscard] 属性
新的 #[\NoDiscard] 属性用于标记函数返回值不应被忽略:
#[\NoDiscard("检查操作是否成功")]
function saveData(array $data): bool {
// 保存数据...
return true;
}
// 忽略返回值会触发警告
saveData(['key' => 'value']);
// Warning: return value of saveData() should be used or cast to (void)
// 正确使用
$success = saveData(['key' => 'value']);
if (!$success) {
// 处理错误
}
// 显式忽略(不会警告)
(void) saveData(['key' => 'value']);
常量表达式中的闭包
PHP 8.5 允许在属性、常量和默认值中使用静态闭包和一等可调用对象:
class Validator {
public const RULES = [
'email' => filter_var(...),
'trim' => trim(...),
];
}
function process(
callable $callback = strtoupper(...)
): string {
return $callback('hello');
}
持久化 cURL Share Handles
新的 curl_share_init_persistent() 函数可以跨请求维护 cURL 共享句柄:
// 创建持久化的共享句柄
$share = curl_share_init_persistent('my-share-handle');
$ch = curl_init('https://example.com');
curl_setopt($ch, CURLOPT_SHARE, $share);
curl_exec($ch);
PHP 8.5 新增函数
array_first() 和 array_last()
获取数组的第一个或最后一个元素,数组为空时返回 null:
$events = ['login', 'view_page', 'checkout', 'logout'];
$first = array_first($events); // 'login'
$last = array_last($events); // 'logout'
$empty = [];
var_dump(array_first($empty)); // null
var_dump(array_last($empty)); // null
get_error_handler() 和 get_exception_handler()
获取当前注册的错误处理器和异常处理器:
// 设置自定义错误处理器
set_error_handler(function($errno, $errstr) {
echo "Error: $errstr";
});
// 获取当前错误处理器
$handler = get_error_handler();
var_dump($handler); // Closure
// 获取当前异常处理器
$exceptionHandler = get_exception_handler();
IntlListFormatter 类
新的国际化列表格式化类:
$formatter = new IntlListFormatter('zh-CN', IntlListFormatter::TYPE_AND);
echo $formatter->format(['苹果', '香蕉', '橙子']);
// 输出: 苹果、香蕉和橙子
$formatter = new IntlListFormatter('en-US', IntlListFormatter::TYPE_OR);
echo $formatter->format(['apple', 'banana', 'orange']);
// 输出: apple, banana, or orange
curl_multi_get_handles()
从 multi-cURL 会话中获取所有句柄:
$mh = curl_multi_init();
$ch1 = curl_init('https://example.com/1');
$ch2 = curl_init('https://example.com/2');
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);
$handles = curl_multi_get_handles($mh);
var_dump(count($handles)); // 2
locale_is_right_to_left() / Locale::isRightToLeft()
判断语言环境是否使用从右到左的文字方向:
var_dump(locale_is_right_to_left('ar')); // true (阿拉伯语)
var_dump(locale_is_right_to_left('he')); // true (希伯来语)
var_dump(locale_is_right_to_left('zh-CN')); // false (中文)
var_dump(Locale::isRightToLeft('fa')); // true (波斯语)
grapheme_levenshtein()
计算两个字符串之间的 Levenshtein 编辑距离(基于字形簇):
$distance = grapheme_levenshtein('café', 'cafe');
var_dump($distance); // 1
Closure::getCurrent()
在闭包内部获取当前闭包实例,便于实现递归:
$factorial = function(int $n): int {
if ($n <= 1) return 1;
return $n * Closure::getCurrent()($n - 1);
};
echo $factorial(5); // 120
其他改进
致命错误包含堆栈跟踪
PHP 8.5 中,致命错误现在会包含完整的堆栈跟踪信息,大大提升了调试体验。
新的 DOM 方法
$doc = new DOMDocument();
$doc->loadHTML('<div class="box">A</div><div class="box">B</div>');
// getElementsByClassName()
$elements = $doc->getElementsByClassName('box');
echo $elements->length; // 2
// insertAdjacentHTML()
$div = $doc->querySelector('div');
$div->insertAdjacentHTML('beforeend', '<span>New</span>');
属性可以应用于常量
class MyClass {
#[Deprecated("Use NEW_CONST instead")]
public const OLD_CONST = 'old';
public const NEW_CONST = 'new';
}
静态属性支持非对称可见性
class Counter {
public private(set) static int $count = 0;
public static function increment(): void {
self::$count++;
}
}
echo Counter::$count; // 可读
Counter::$count = 10; // 错误:不可写
构造函数提升支持 final 属性
class User {
public function __construct(
public final string $id,
public string $name,
) {}
}
setcookie() 支持 “partitioned” 选项
setcookie('name', 'value', [
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'partitioned' => true, // 新选项
]);
新的全局常量
// 构建提供者标识
echo PHP_BUILD_PROVIDER; // 例如: "debian" 或 "remi"
// 构建日期
echo PHP_BUILD_DATE; // 例如: "Jan 1 2026"
CLI 新选项
# 显示与默认值不同的 INI 配置
php --ini=diff
max_memory_limit INI 指令
管理员可以设置内存限制的上限:
; php.ini
max_memory_limit = 256M
应用程序无法将 memory_limit 设置为超过 max_memory_limit 的值。
弃用特性
反引号运算符(shell_exec 别名)
// ❌ PHP 8.5 已弃用
$output = `ls -la`;
// ✅ 使用 shell_exec()
$output = shell_exec('ls -la');
非标准类型转换
// ❌ PHP 8.5 已弃用
$bool = (boolean) $value;
$int = (integer) $value;
$float = (double) $value;
$bin = (binary) $value;
// ✅ 使用标准类型转换
$bool = (bool) $value;
$int = (int) $value;
$float = (float) $value;
$bin = (string) $value;
mysqli_execute()
// ❌ 已弃用
mysqli_execute($stmt);
// ✅ 使用 mysqli_stmt_execute()
mysqli_stmt_execute($stmt);
curl_close() 和 curl_share_close()
这些函数自 PHP 8.0 起就是空操作,现在正式弃用:
// ❌ 已弃用(实际上不需要调用)
curl_close($ch);
curl_share_close($sh);
// cURL 句柄现在会自动清理
xml_parser_free()
// ❌ 已弃用(自 PHP 8.0 起是空操作)
xml_parser_free($parser);
// XML 解析器现在会自动清理
socket_set_timeout()
// ❌ 已弃用
socket_set_timeout($stream, 30);
// ✅ 使用 stream_set_timeout()
stream_set_timeout($stream, 30);
MHASH_* 常量
所有 MHASH_* 相关常量已弃用,应使用 hash() 函数替代。
__sleep() 和 __wakeup()
这两个魔术方法被软弃用(soft-deprecated),建议使用 __serialize() 和 __unserialize() 替代:
// ❌ 软弃用
class OldStyle {
public function __sleep(): array {
return ['data'];
}
public function __wakeup(): void {}
}
// ✅ 推荐方式
class NewStyle {
public function __serialize(): array {
return ['data' => $this->data];
}
public function __unserialize(array $data): void {
$this->data = $data['data'];
}
}
case 语句后使用分号
// ❌ PHP 8.5 已弃用
switch ($value) {
case 1; // 分号
break;
}
// ✅ 使用冒号
switch ($value) {
case 1: // 冒号
break;
}
null 作为数组偏移量
// ❌ PHP 8.5 已弃用
$array[null] = 'value';
// ✅ 使用空字符串或其他明确的键
$array[''] = 'value';
移除的特性
disable_classes INI 设置
disable_classes INI 设置已被完全移除。
CLI -z / –zend-extension 选项
从命令行加载 Zend 扩展的选项已被移除。
兼容性变化
类型转换警告
- NAN 转换为其他类型时会产生警告
- 浮点数转整数时的精度损失会产生警告
- 对非数组使用解构语法会产生警告
class_alias() 限制
“array” 和 “callable” 不再是 class_alias() 的有效类名。
总结
PHP 8.5 带来了许多实用的新特性和改进:
主要新特性:
- 管道运算符
|> - URI 扩展
- Clone With 语法
#[\NoDiscard]属性array_first()和array_last()函数- 常量表达式中的闭包
- 致命错误堆栈跟踪
- IntlListFormatter 类
需要注意的变化:
- 反引号运算符已弃用
- 非标准类型转换已弃用
__sleep()和__wakeup()软弃用disable_classesINI 设置已移除
通过了解这些变化并遵循迁移建议,可以顺利地从 PHP 8.4 迁移到 PHP 8.5。