webman 请求生命周期是长生命周期 worker 进程内完成一次请求,需避免内存泄漏、静态变量污染等问题;request 不可变,须用 withattribute() 返回新实例并传递;控制器默认复用,禁止在 $this 中存请求级状态;app()->setlocale() 必须在最早期中间件执行;数据库与 redis 客户端需支持连接健康检测与恢复。

Webman 的请求生命周期不是“一次请求一个进程”,而是“一次请求在一个长生命周期 Worker 进程内完成”。理解这点,才能避开内存泄漏、静态变量污染、连接复用异常等高频问题。
Request 对象是不可变的(immutable),不能在中间件里挂载 user 属性
常见错误现象:$request->user 在中间件赋值后,控制器里读不到;或出现 Call to undefined method support\Request::user()。
原因在于 Webman 的 Request 类是只读对象,底层基于 PSR-7 规范实现,所有 setter 方法(如 withAttribute())返回的是新实例,原对象不变。中间件中写 $request->withAttribute('user', $user) 后若不显式传递下去,后续环节拿到的仍是原始 $request。
- 正确做法:在鉴权中间件中 return 时,把新
$request传给 next ——return $next($request->withAttribute('user', $user)); - 控制器里必须用
$request->getAttribute('user')获取,而不是$request->user - 别试图通过全局变量或静态属性“绕过”这个限制,会引发多请求间数据污染
控制器实例默认复用,$this->xxx 可能被上一个请求残留
常见错误现象:用户 A 登录后访问 /api/order,返回了用户 B 的订单;或日志里发现 App\Controller\OrderController::$cache 是前一次请求留下的旧数据。
这是因为 Webman 默认开启控制器复用(每个进程内单例),OrderController 实例在首次请求创建后,会被后续同进程的请求反复调用其方法,但 $this 上的属性不会自动清空。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 避免在控制器属性里存请求级状态,比如
public $user;、protected $cache = []; - 如需缓存,改用函数局部变量,或用
static+ 请求唯一标识(如$request->getHeaderLine('X-Request-ID'))做键隔离 - 彻底关闭复用?可在
config/app.php中设'controller_reuse' => false,但会增加对象创建开销
app()->setLocale() 必须在每次请求最早期执行
常见错误现象:前端切换语言后,接口返回仍是中文;trans('hello') 始终返回 locale=zh_CN 的翻译,即使 session 或 cookie 已更新为 en_US。
根本原因是 trans() 内部依赖 app()->getLocale(),而该值在请求开始时就被缓存,后续调用不再检查 session / cookie / URL 参数变更。
- 必须在全局中间件(如
app/middleware/LocaleMiddleware.php)的handle()开头调用app()->setLocale($locale) - 获取
$locale的逻辑要可靠:优先从$request->header('Accept-Language')解析,其次查$request->cookie('locale'),最后 fallback 到配置默认值 - 别在控制器
initialize()里做这事——那时路由已匹配、中间件已执行完毕,locale 设置已失效
数据库连接和 Redis 客户端在进程生命周期内复用
常见错误现象:长时间运行后出现 MySQL server has gone away;或 Redis 连接超时后,后续请求持续失败,直到重启进程。
Webman 的 Worker 进程常驻内存,PDO/Redis 实例一旦创建就一直存在,不会随请求结束而释放。它们依赖底层连接池或自动重连机制,但并非全部支持。
- MySQL:用
pdo_mysql时,务必在 DSN 中加;charset=utf8mb4;options=case:PDO::ATTR_EMULATE_PREPARES=0;options=case:PDO::ATTR_PERSISTENT=false,禁用持久连接,避免连接老化 - Redis:推荐使用
phpredis扩展而非predis,前者支持连接自动重试;若用predis,需自行包装连接逻辑,在connect()失败时重建客户端 - 关键原则:所有 I/O 客户端不应依赖“请求结束即销毁”的假定,必须具备连接健康检测与恢复能力
真正难的不是看懂源码里哪一行创建了 Request,而是意识到:你写的每一行业务代码,都运行在可能被复用、被并发共享、被长期持有的上下文中。










