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->1session.cookie_httponly:0->1session.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的 PCREpreg_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 拒绝整数键。
按顺序要做的事
- 现在就在 PHP 8.5 上显式设置那三个 session 配置。
- 打开弃用报告,跑一遍测试套件。
- grep 这些符号:
mb_ereg、is_double、is_long、is_integer、doubleval、spl_object_hash、metaphone、strcoll、unpack(。 - 检查自定义 session handler 是否实现了
create_sid()和validateId()。 trim()除非有测试失败,否则先别动。
不会坏的部分
PHP 8.6 不移除任何在 8.x 里被弃用的东西——移除都发生在 PHP 9。typed properties、enum、readonly class、fiber、attribute 都不受影响。