编程 PHP 8.6 升级:三个 session 默认值静默翻转,跨站 POST 会掉登录

2026-09-26 21:01:43

PHP 8.6 升级:三个 session 默认值静默翻转,跨站 POST 会掉登录

PHP 8.6 于 2026 年 11 月 19 日发布。生产环境里最可能打断应用的改动不是某个被移除的函数,而是三个 session 设置的默认值翻转:session.use_strict_mode、session.cookie_httponly、session.cookie_samesite。它们的失败方式是静默的——不抛异常,用户只是在跨站请求后不再保持登录。

来源:What Actually Breaks When You Upgrade to PHP 8.6

发布时间线

  • alpha 1:7 月 2 日
  • beta 1 + 软功能冻结:8 月 13 日
  • 硬功能冻结:9 月 22 日
  • RC1:9 月 24 日
  • RC4:11 月 5 日
  • GA:2026 年 11 月 19 日

目前还没有官方迁移页面,权威依据是 php-src master 分支上的 UPGRADING 文件。

session 安全默认值翻转

  • session.use_strict_mode:0 -> 1
  • session.cookie_httponly:0 -> 1
  • session.cookie_samesite:"" -> "Lax"

每一个单独看都是正确的默认值,合在一起就是最 disruptive 的改动,因为三者都不报错。应用照常运行,只是部分用户不再处于登录态。

cookie_samesite = "Lax" 咬得最狠。Lax cookie 不会随跨站 POST 请求发送。任何从其他 origin POST 到你的应用的东西——支付网关用 POST 把用户带回、身份提供方的回调、嵌入式表单、依赖 session 的 webhook——到达时都没有 session。用户无缘无故落到登录页。

cookie_httponly = 1 意味着 JS 不能再通过 document.cookie 读取 session cookie。直接读 session ID 的前端代码会坏。

use_strict_mode = 1 让 PHP 拒绝不是自己生成的 session ID(这才是 session fixation 的正解),会破坏从外部传入 session ID 的流程。

临时缓解:

session.cookie_samesite = "None"
session.cookie_secure = 1

SameSite=None 必须搭配 Secure,浏览器会直接拒绝不合规的组合,所以这不是一行配置就能回退的事。建议不要回退:今天就在 PHP 8.5 上显式设置这三项,在你能控制时间的时候先把问题暴露出来。

不声不响的行为变更

  • trim()、ltrim()、rtrim() 的默认字符集加入了 \f(form feed,0x0C)。trim("hello\f") 在 PHP 8.5 返回 "hello\f",在 PHP 8.6 返回 "hello"。输出静默改变。
  • NUL 字节从被容忍变成抛异常:getenv()、putenv()、parse_str()、setlocale()、dl()、openlog()、proc_open()($cwd),以及约 20 个文件系统函数 file_exists()、is_file()、filesize()、stat() 等,传入含 NUL 字节的字符串时抛 ValueError。PHP 8.5 返回 false,PHP 8.6 抛 ValueError。
  • unpack() 重新解释 ``:unpack("sname") 抛异常(C 没有字节序)。
  • SessionHandlerInterface 需要 create_sid() 和 validateId(),自定义 handler 会产生弃用提示。

弃用(E_DEPRECATED,PHP 9 中移除)

  • finally 里的 return(会丢弃 try 中的 return/异常)——已弃用。
  • __construct() / __destruct() 返回值,或把两者写成 generator——编译期弃用(39 项降到 0)。
  • array_filter() 传入非法 $mode 时抛 ValueError。
  • php://filter 限制链式 filter 数量(上限 16,可通过 filter.max_filter_count 配置)。
  • 所有 mbregex 弃用(Oniguruma 已无人维护)。迁移到带 /u 的 PCRE preg_match:mb_ereg('^[0-9]+$', $s) -> preg_match('/^[0-9]+$/u', $s)。正则并不总能一一对应地翻译。
  • 类型检查别名弃用:is_double()、is_long()、is_integer()、doubleval();另外还有 metaphone()、strcoll()、spl_object_hash()(改用 spl_object_id())、spl_classes()、SORT_LOCALE_STRING。
  • SPL CSV 与 ArrayIterator 方法:SplFileObject::fgetcsv()、fputcsv()、setCsvControl()、getCsvControl();ArrayIterator 的十个方法 getFlags()、setFlags()、asort()、ksort()、uasort()、uksort()、natsort()、natcasesort()、serialize()、unserialize()。
  • 本来就没有意义却接受对象的参数:array_walk()、mb_convert_variables()、zlib/bz2 流 filter。
  • mysqli_get_charset() 和 mysqli_stmt_init() 弃用。SOAP classmap 拒绝整数键。

按顺序要做的事

  1. 现在就在 PHP 8.5 上显式设置那三个 session 配置。
  2. 打开弃用报告,跑一遍测试套件。
  3. grep 这些符号:mb_ereg、is_double、is_long、is_integer、doubleval、spl_object_hash、metaphone、strcoll、unpack(。
  4. 检查自定义 session handler 是否实现了 create_sid() 和 validateId()。
  5. trim() 除非有测试失败,否则先别动。

不会坏的部分

PHP 8.6 不移除任何在 8.x 里被弃用的东西——移除都发生在 PHP 9。typed properties、enum、readonly class、fiber、attribute 都不受影响。

复制全文 生成海报 PHP 8.6 session 升级迁移 安全

推荐文章

程序员茄子在线接单