FrankenPHP 1.13 升级笔记:线程预算、reload 校验与 hot_reload 的 opcache 默认值
项目:php/frankenphp 文档:frankenphp.dev
以下整理针对 1.13.0 / 1.13.1 / 1.13.2 三个 release 中会直接影响自部署 PHP 应用的部分,重点是线程核算语义、putenv() 行为、配置 reload 校验和 hot_reload 的 opcache 默认值。
1.13.0:依赖升级与五个安全问题
1.13.0(2026-10-04)把内置 Caddy 升到 2.11.7、内置 Mercure 升到 1.0、兼容 PHP 8.6,Go 工具链升到 1.27。同时修了 5 个安全问题,其中 2 个 high:
- GHSA-868c-7h7m-mmj9(high):Windows 下
SCRIPT_FILENAME可逃逸文档根。 - GHSA-qcrp-8483-f2f2(high):Caddy 2.11.7 默认丢弃点号形式的 header。PHP 会把
Foo.Bar这样的名字映射成HTTP_FOO_BAR,两边组合起来可以伪造请求头。 - GHSA-xxjp-cjxr-2x6m:CGI 路径切分改为要求路径段边界。
/uploads/a.php.txt/b.php这类路径不再会执行到uploads/a.php。 - GHSA-996f-w38m-f574:
putenv()不再写入进程级 OS 环境,值不再跨请求、跨线程可见。 - GHSA-4prg-hv4r-g6mv:
frankenphp_log()收到构造数组不再导致整个服务崩溃。
putenv() 这条要单独看:以前它在进程环境里留下痕迹,现在只影响 PHP 侧。代码里靠 getenv() 读取其他请求写入的变量、或者依赖 putenv() 影响子进程环境的行为,升级后需自行验证。
线程核算:num_threads 不再包含 worker 线程
num_threads 现在只统计“没有 worker 服务的请求”可用的线程,worker 线程在其之上另算。原来同时写了 num_threads 和 workers 的配置,升级后会起更多线程。
{
frankenphp {
num_threads 8 # 只算非 worker 请求
max_threads 12
worker {
file ./public/frankenphp-worker.php
name app
num 2 # 额外 2 个线程
}
}
}
上面这份配置的线程预算按 num_threads + worker 线程数 ≤ max_threads 算,即 8 + 2 ≤ 12。当 max_threads 小于这两者之和时,启动直接失败,错误信息会同时打印三个值。默认值没有变化。调过线程数的实例升级前建议先手算一遍现有配置。
配置 reload:先校验再替换
reload 改为先校验新配置再替换:缺 worker 文件、worker 名重复、线程预算不一致都会被拒绝,旧配置继续服务。库用户可以调 frankenphp.Validate() 做同样的检查。
frankenphp {
worker {
file ./public/worker.php
name app
}
worker {
file ./public/worker2.php
name app # 重名,reload 会被拒绝
}
}
在 1.13.0 之前,配置写错会让服务直接 500。这条对滚动发布比较有用:坏配置不再把在跑的实例一起带下去。
1.13.1:1.13.0 的回归修复
- worker 模式下,在启动阶段读
$_SERVER['REQUEST_URI']的应用(如 Nextcloud)恢复可用。 - php-cli 恢复
$argv,Symfony Console 恢复。 - Caddy worker 启动失败按文档重试,不再第一次失败就放弃。
- 修了两个进程崩溃,以及优雅关闭时的 500。
- 静态构建带上 deepclone 扩展,用于加速 Symfony 缓存。
1.13.2:PHP2Shell 与四个自身修复
1.13.2(2026-10-21)是安全版。镜像内置 PHP 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12,静态二进制用 8.5.12,修 PHP2Shell(CVE-2026-109618、CVE-2026-109619,未认证 RCE)。除了升级,没有完整缓解手段。
自身修了 4 个:
- GHSA-6q3j-62j9-f9hh:embedded app 解压到共享临时目录里可预测的路径并复用已有文件,本地用户可预建目录让服务执行自己的 PHP 代码。现改为按用户目录原子解压。
- GHSA-fq24-r53r-xcx3:等待 PHP 线程的请求在客户端断开后被丢弃。此前未认证客户端可以排队再断开,把线程池占满。
- GHSA-ccgp-ch9h-533f:PHP 值转 Go 时拒绝循环数组。自引用数组传给
frankenphp_log()会栈溢出崩进程。 - GHSA-4gfw-9v29-gp9h:worker 模式下
error_get_last()和libxml_get_errors()在每个请求结束时清空。此前会把上一个客户端的输入片段和服务端路径泄漏给下一个请求。
hot_reload 下 opcache 的默认值变成 0
配置了 hot_reload 时,opcache.revalidate_freq 的默认值从 2 改成 0。原因是改完文件后立刻取的页面经常还是旧的。
; php.ini 里显式设置的值优先级更高
opcache.revalidate_freq = 2
php.ini、PHP_INI_SCAN_DIR、php_ini 指令里显式设置的值都优先于这个新默认值。生产环境如果只靠默认值吃到了 0,每次请求都会 stat 文件,需自行验证对吞吐的影响。
静态构建迁移到 static-php-cli v3
- macOS 只发 Apple Silicon,不再有
frankenphp-mac-x86_64;Intel Mac 用build-static.sh自己编。 -gnu二进制要求 glibc 2.28(原来 2.17)。老基础镜像需要先确认。- 静态构建镜像改成用
docker run传环境变量PHP_EXTENSIONS/PHP_EXTENSION_LIBS/XCADDY_ARGS,二进制输出到挂载的/output。
docker run --rm -v "$PWD/output:/output" \
-e PHP_EXTENSIONS="opcache" \
-e PHP_EXTENSION_LIBS="" \
-e XCADDY_ARGS="" \
镜像名与参数取值以 frankenphp.dev 的静态构建文档为准,需自行验证。
其它
- Caddy 侧 32 位平台上,
max_threads 4294967295过去会回绕成负数被当成auto,现在返回 range error。 frankenphp_log()与FRANKENPHP_LOG_LEVEL_*在 php-cli 下也可用,写 stderr;因为不加载 Caddyfile,log指令在这个场景不生效。frankenphp_thread_index()重新导出给 C 扩展。- opcache 共享内存重启会记日志,并计入实验性指标
frankenphp_opcache_restarts{reason}(PHP 8.4+)。ZTS 下这类重启不安全,计数应保持 0。