会,但仅限Worker模式;普通per-request模式下static不跨请求。Worker模式因进程常驻,顶层/函数内/类静态变量均跨请求共享并持续累积,属设计特性而非bug,需谨慎用于计数等场景,避免误作用户状态缓存。

会,但只在 Worker 模式下;普通模式(即默认的 per-request 模式)下不会。
Worker 模式下 static 变量跨请求保留是设计使然,不是 bug
FrankenPHP 的 Worker 模式让 PHP 脚本常驻内存,顶层作用域(非函数内、非类中)的 static $counter 会被分配在进程级持久内存区,每次请求都复用同一块地址。这不是意外泄漏,而是明确支持的特性——比如你真想统计总请求数,static $total = 0; $total++; 就能工作。
但要注意:这个行为仅限于「脚本顶层」的 static 声明。函数内的 static $x 和类中的 public static $y 在 Worker 模式下同样跨请求存活,且生命周期完全脱离单次 HTTP 请求边界。
- 函数内
static $i = 0;:首次调用初始化,之后所有请求调用该函数都共享并累加这个值 - 类静态属性
MyClass::$cache:所有请求 new MyClass() 都读写同一份内存 - 不推荐在 preload 文件或入口循环外定义
static $user_data——它会变成全局污染源
普通模式(per-request)下 static 不跨请求,和 PHP-FPM 行为一致
如果你没显式启用 Worker 模式(比如没在 Caddyfile 里写 php { worker ./index.php },也没用 --worker 启动),FrankenPHP 就走标准 per-request 流程:每个请求启动独立 PHP 生命周期,static 变量随请求结束自动销毁。
立即学习“PHP免费学习笔记(深入)”;
此时 static $count 的行为和你在 PHP-FPM 或 CLI 下运行一样——本次请求内保持,下次请求从头开始。无需任何清理动作。
- 验证方式:在入口加
var_dump(static $x = 0; $x++);,刷新页面看输出是否每次都为int(0) - 常见误判:你以为开了 Worker,其实 Caddyfile 写的是
php而非php { worker ... },导致实际仍是 per-request
容易踩的坑:把“需要隔离”的状态塞进 static
最典型的错误是把用户相关数据(如 static $current_user_id)、临时配置(static $debug_mode)或缓存键前缀(static $prefix)直接声明为 static。这些在 Worker 模式下会残留,导致下一个请求拿到上一个用户的 ID 或错误的调试开关。
- 正确做法:改用函数参数传入、依赖注入、或
php.Context(FrankenPHP 提供的协程本地存储) - 若必须跨请求缓存,应使用外部存储(Redis、APCu)并主动控制 TTL,而非依赖
static的“隐式持久化” - 检查所有
static $xxx声明,问自己一句:“这个值是否真的需要被第 1000 个请求看到第 1 个请求写入的内容?”
真正难处理的不是 static 本身,而是团队里没人意识到它已不再“每请求重置”——尤其当老项目从 FPM 迁移过来时,那些藏在中间件、工具类、甚至 vendor 包里的 static 状态,最容易在 Worker 模式下悄悄引发数据错乱。



















