PHP 8.6 软冻结:用 ? 占位符预填参数的 partial function application
PHP 8.6 Beta 1 将于 2026 年 8 月 13 日(周四)发布,同时标记 soft feature freeze(phpnews.net/php-8-6-beta-1)。这个版本里对日常写代码影响最直接的一项,是 partial function application(部分函数应用):用一个 ? 占位符把函数的部分参数预先填好,返回一个闭包。
语法:? 占位符 + 剩余参数
调用时把要固定的实参写在原位置,不固定的位置放 ?,引擎返回一个 Closure,它只接收没被占位符占掉的那些参数。
function slugify(string $text, string $separator = '-'): string
{
return strtolower(str_replace(' ', $separator, $text));
}
// 固定第二个参数,得到只接收 $text 的闭包
$slug = slugify(?, '_');
echo $slug('Hello PHP'); // hello_php
生成的闭包可以直接当回调传给 array_map、usort、事件监听器等需要 callable 的地方,不需要再包一层 fn()。
和闭包、first-class callable 的区别
三种写法最终都得到 Closure,差别在参数是怎么来的。
// 1. 手写闭包:参数列表自己维护
$fn = fn(string $text) => slugify($text, '_');
// 2. first-class callable(PHP 8.1+):整体转发,参数一个都不能少
$fn = slugify(...);
$fn('Hello PHP', '_');
// 3. partial application(PHP 8.6):部分参数已固定
$fn = slugify(?, '_');
$fn('Hello PHP');
- 手写闭包:最灵活,但被调函数的签名一变,闭包的参数列表要跟着改。
- first-class callable
slugify(...):(...)表示"把整个可调用对象转成闭包",调用时仍要传全部参数,语义上是转发而非预填。 - partial application:固定哪几个参数、剩下哪几个由原函数签名推导,调用方只需要关心剩下的参数。
8.6 一起锁定的其他特性
Io\Poll命名空间下的轮询 API,可以用 epoll 或 WSAPoll 替代stream_select()Time\Duration类,用于表示时长并做运算- readonly 属性默认值
- 闭包优化:推导
static并为引擎能证明相同的闭包做缓存(8.6 只落地了缓存部分,static 推导因为边界情况被放弃) clamp(),把值限制在区间内- 更安全的会话默认值:
session.use_strict_mode与session.cookie_httponly默认变为 1,session.cookie_samesite默认变为Lax - 类常量和枚举 case 上的
#[\Override]、枚举的__debugInfo()、内置SortDirection枚举、函数参数的文档注释
时间线
| 阶段 | 日期 |
|---|---|
| Beta 1(soft freeze) | 2026-08-13 |
| Beta 2 | 2026-08-27 |
| Beta 3 | 2026-09-10 |
| Hard feature freeze | 2026-09-22 |
| RC1 | 2026-09-24 |
| RC2 | 2026-10-08 |
| RC3 | 2026-10-22 |
| RC4 | 2026-11-05 |
| GA | 2026-11-19 |
php.watch 上的语法与新函数变化
php.watch/versions/8.6 汇总的改动:
json_decode的错误信息会指出出错位置- 新增
clamp函数、SortDirection枚举、grapheme_strrev array_filter的$mode传入非法值时抛ValueErrortrim/ltrim/rtrim/chop默认开始清理 Form-Feed(\f)
Zend 博客补充的弃用项
zend.com/blog/php-8-6 提到 PHP 8.6 引入 Time\Duration(纳秒精度),同时确认了几项弃用:
- 限制 filter chain 的最大数量(
php://filter,新上限 16,可通过filter.max_filter_count配置) - 弃用
__construct()/__destruct()的返回值 - oniguruma 结束维护,所有 mbstring 正则函数(
mb_ereg*、mb_split)被弃用,将在 PHP 9.0 移除;对应 PECL 包mb_onig已创建,官方建议改用 PCRE 函数并加上/u修饰符
使用时注意的点
占位符只是位置标记,剩余参数按原签名顺序传入,所以用 ? 跳着填参数会让调用点难以阅读,这种场合手写闭包反而更清楚。partial application 生成的是真正的 Closure,可以直接进回调位置;但如果被调函数签名改了,预填的位置和剩余参数都会跟着变,编译期不会提醒你调用点是否还符合预期。
适用场景集中在"同一个函数反复用同一批前置参数":模板渲染里固定的转义/格式化函数、日志器上固定的 level、排序比较器里固定的字段名。只为单个调用点省一行 fn() 就没必要引入占位符语法了。