编程 PHP 8.6 把 array_intersect() 的字符串转换提前到扫描阶段

2026-11-08 21:04:15

PHP 8.6 把 array_intersect() 的字符串转换提前到扫描阶段

array_intersect() 一直按字符串比较值:把元素转成 string 再比较。8.6 改的是转换的时机——从“排序比较时”提前到“扫描输入时”。这条变更记录在 php/doc-en issue #5901 的 Backward Incompatible Changes 节。

变化点

旧实现把值转换推迟到排序比较阶段;PHP 8.6 在扫描输入数组的过程中就完成转换。受影响的不只是性能,而是转换发生的次数、顺序和产生副作用的位置。

结果会变的几种情况

有状态的 __toString()

array_intersect() 只接受可转字符串的标量,或实现了 __toString() 的对象。如果 __toString() 带内部状态(计数器、懒加载、缓存填充),每次调用返回的字符串可能不同:

final class Token
{
private int $n = 0;

public function __construct(private string $v) {}

public function __toString(): string
{
return $this->v . (++$this->n > 1 ? '#' : '');
}
}

array_intersect([new Token('a'), new Token('b')], ['a', 'b']);

转换提前后,调用 __toString() 的时机与调用序列都可能与旧版不同,因此这类实现的返回值也就可能不同。文档把这一点明确列为结果可能改变的情形。

__toString() 抛异常时,触达的是哪一个

如果某个元素的 __toString() 抛出异常,实际被触达的那个异常取决于转换按什么顺序发生。扫描输入时按输入顺序转换,谁先被扫描到,谁的异常先抛出;旧实现由排序比较路径决定。同一个输入,抛出的异常可能来自不同元素。

转换告警的数量和顺序

元素本身是数组时会触发 Array to string conversion 警告。转换从排序阶段前移到扫描阶段,告警的条数和先后顺序都会变化。任何依赖告警数量或顺序的测试与日志断言,都需要重新确认。

空数组与类型校验的顺序

两条顺序约束:

  • 参数类型校验发生在检查空数组、转换值之前。传入非 array 参数时先抛 TypeError,不会因为另一个参数是空数组而短路跳过校验。
  • 任一输入数组为空时,不做值转换。此时既不会调用 __toString(),也不会产生转换告警——这会让依赖 __toString() 副作用的代码(计数、写日志、触发懒加载)静默地少执行一次。

自查清单

  1. grep 所有 array_intersect( 调用,确认第一个之后的参数里是否含实现了 __toString() 的对象,尤其是带状态或会抛异常的对象。
  2. 检查元素是否可能是数组(嵌套数据结构直接进入比较),核对现有测试对 Array to string conversion 告警数量、顺序的断言。
  3. 检查是否有 try/catch 依赖异常来自哪个元素,或依赖异常消息中的具体值。
  4. 检查空数组输入路径:8.6 下不转换值,__toString() 的副作用不再发生。

同批 Backward Incompatible Changes

同一节还列出(不展开):

  • by-reference foreach 在数组压缩后可能访问之前被跳过的元素
  • ?? / empty() 对 magic property:__isset() 已写入属性表后不再调 __get()
  • trim() 默认剥离字符集加入 form feed
  • fclose() / file_put_contents() / copy() 在 flush/close 失败时返回 false

时间线与链接

PHP 8.6 RC5 计划于 2026-11-05,GA 2026-11-19。

  • 变更条目:
  • 函数文档:
复制全文 生成海报 PHP 数组函数 升级踩坑

推荐文章

程序员茄子在线接单