PHP 8.6 批量弃用投票:31 项通过,is、let、readonly 要在 PHP 9 前改名
PHP 8.6 的批量弃用 RFC 已通过。投票于 8 月 10 日 13:00 UTC 关闭,35 张独立选票里 31 项通过、4 项被否。每项单独计票,各自需要达到三分之二多数。通过的项在 8.6 上抛弃用通知,移除目标定在 PHP 9。结果由 Gina P. Banyard 发到 internals 邮件列表。
五个新增的保留标识符
let用作标识符:24 赞成、11 反对、9 弃权,68.6%namespace用作类常量名:29 赞成、8 反对、7 弃权,78.4%is用作标识符:29 赞成、10 反对、6 弃权,74.4%- 函数命名为
readonly:39 赞成、1 反对、2 弃权,97.5% _用作常量与编译期别名:34 赞成、4 反对、5 弃权,89.5%
is 是投票期间争议最大的一项。Rowan Tommins 指出它会和 Hamcrest 冲突——Mockery 依赖的匹配器库,README 里通篇使用 is() 函数,Packagist 统计的安装量超过 5 亿。这项仍然过了三分之二门槛,所以任何定义了 is() 函数的包,都得在 PHP 9 之前改名。
函数与别名
- 类型检查别名
is_double()、is_integer()、is_long()、doubleval(),均约 95% strcoll()与SORT_LOCALE_STRING排序标志,均 95%metaphone():20 赞成、8 反对、16 弃权,71.4%spl_classes()100%,spl_object_hash():26 赞成、9 反对、11 弃权,74.3%SplFileObject的 CSV 方法:25 赞成、5 反对、15 弃权,83.3%mysqli::stmt_init()与mysqli_get_charset(),均 100%- 指定
$case_insensitive的define(),100% $allow_string为 false 时传字符串给is_a()与is_subclass_of(),均 100%
另有一组选票弃用「在期望数组的位置传对象」,覆盖 array_walk()、deflate_init() 与 inflate_init()、zlib 与 bzip2 流过滤器、mb_convert_variables()、http_build_query()。
metaphone() 的结果有个附带问题:RFC 里点名的替代品 Linguistics 包,内部调用的就是 php-src 自己的 metaphone(),所以它同样受这次弃用影响,并不是绕开的办法。
finally 里 return 的行为变化
从 finally 块中 return 以 90.7% 通过(39 赞成、3 反对、4 弃权)。和上面大多数只是命名清理的条目不同,这是一处行为变更,不只是改名。
被否的四项
- 弃用
list()结构:23 赞成、23 反对、1 弃权,50.0% - 将
in、out、inout保留为标识符:8 赞成、21 反对、15 弃权,27.6% - 弃用
_作为函数名,以及gettext()的_()别名:10 赞成、22 反对、11 弃权,31.3% - 弃用
dechunk过滤器:18 赞成、15 反对、12 弃权,54.5%
list() 最终停在 23:23 平票。投票期间曾一度 21:21 持平,之后一直没有拉开。
dechunk 过滤器引发的争论最多。Matteo Beccati 投了反对,理由是这项弃用会让 symfony/http-client 和 php-http/message 开始触发通知,而且没有提供替代方案。Jakub Zelenka 指出 php-src 本来就必须保留这个过滤器——HTTP 流封装要用它处理 chunked 编码,所以对用户态弃用它既删不掉代码,也减不了维护量。RFC 贡献者 Sjoerd Langkemper 表示,他的目标是阻止该过滤器被用于 filter chain 攻击。
现在该 grep 什么
眼下还没有东西会直接挂掉:通过的每一项都只是在 8.6 上抛弃用通知,计划在 PHP 9 移除。最值得先查的是关键字保留,因为名为 is、let、readonly、namespace 的函数或类常量,静态分析一遍就能扫出来。
# 函数/方法名占用保留标识符
grep -rnE 'function[[:space:]]+(is|let|readonly)[[:space:]]*\(' --include='*.php' .
# namespace 作类常量名
grep -rnE 'const[[:space:]]+namespace[[:space:]]*=' --include='*.php' .
# _ 作常量或编译期别名
grep -rnE "(define\\(['\"]_['\"]|const[[:space:]]+_[[:space:]]*=)" --include='*.php' .
# 即将弃用的函数与别名
grep -rnE '\b(is_double|is_integer|is_long|doubleval|strcoll|metaphone|spl_classes|spl_object_hash|stmt_init|get_charset)\(' --include='*.php' .
从 finally 里 return 的写法值得单独过一遍,这是唯一一处会改变运行时行为、而不只是让你改名的条目。