workerman 3 升级到 4 的核心问题不是语法破坏,而是对象生命周期管理更严格:[$obj, 'method'] 回调在连接断开后易因 $obj 被销毁而触发 fatal error: call to a member function on null;官方推荐改用静态方法+显式传参(如 messagehandler::handle($connection, $data)),并将状态存于 $connection 或 worker 实例上,确保生命周期对齐。

Workerman 3 升级到 4 并不涉及语法层面的强制破坏性变更,但实际迁移中常因对象生命周期管理、回调绑定方式和初始化时机差异引发运行时错误——尤其是 [$obj, 'method'] 类型回调在连接断开后触发时抛出 Fatal error: Call to a member function xxx() on null。这不是版本号升级本身改了语法,而是 Workerman 4 对事件循环稳定性要求更高,暴露了旧写法中长期被忽略的隐患。
核心问题:对象方法回调失效不是 Bug,是设计收敛
Workerman 3 允许你在 onConnect 里 new 一个对象,再把它的方法传给定时器或 onMessage,看似能跑;Workerman 4 在多进程 reload、连接频繁上下线等真实场景下,会更快暴露对象已被销毁的问题。根本原因没变:PHP 不自动保活对象引用,[$obj, 'method'] 只是快照,不延长生命周期。
- 不要指望升级后“原样跑通”,重点不是改写法,而是改逻辑归属
- 所有依赖
$this的业务逻辑,必须确认该对象实例存活时间 ≥ 所有潜在回调触发时间 - 若对象只在
onConnect创建,它大概率在连接关闭后就被回收,后续定时器调用必崩
推荐改造路径:静态入口 + 显式参数(最稳)
放弃“让对象自己处理自己”,改为由回调函数明确传入所需上下文。这是 Workerman 官方文档明确推荐、且与 PHP-FPM/CLI/测试环境完全兼容的方式。
- 把原来
MyClass::onMessage()中所有对$this->xxx的访问,改为接收$connection和$data参数后查状态 - 连接相关状态(如用户 ID、登录时间)直接存到
$connection->uid、$connection->login_time等属性上 - 全局共享状态(如在线用户列表、房间成员)用
static属性或Worker::$globalClients管理,避免闭包捕获
Workerman 3 常见写法(升级后高危):
$worker->onConnect = function($connection) {
$handler = new MessageHandler();
$connection->handler = $handler;
};
$worker->onMessage = [$connection->handler, 'handle']; // ❌ 连接断开后 $connection->handler 不存在
Workerman 4 推荐写法(安全):
class MessageHandler {
public static function handle($connection, $data) {
// 从 connection 上取状态
$uid = $connection->uid ?? 'guest';
$connection->send("Hello {$uid}");
}
}
$worker->onMessage = function($connection, $data) {
MessageHandler::handle($connection, $data); // ✅ 显式传参,无生命周期依赖
};
若必须保留对象实例:手动强引用 + 绑定 Worker 生命周期
仅适用于有复杂状态机、难以拆解为静态方法的场景。关键不是“能不能用”,而是“谁来负责对象不死”。
- 对象必须创建在
onWorkerStart或作为$worker的属性,不能在onConnect或临时回调里 new - 用
static $instance存单例时,注意多进程间不共享,每个子进程需独立初始化 - 热更新(reload)会重建 Worker 实例,原对象引用立即失效,
[$oldObj, 'method']变成 null —— 必须在onWorkerStop清理资源,并在onWorkerStart重建
class RoomManager {
private static $instance;
public static function getInstance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() { /* 初始化 Redis 连接等 */ }
}
// 在 onWorkerStart 中确保可用
$worker->onWorkerStart = function($worker) {
$worker->roomManager = RoomManager::getInstance();
};
$worker->onMessage = [$worker->roomManager, 'onMessage']; // ✅ 挂在 worker 上,生命周期对齐
其他配套调整点
升级不是只改回调,还需同步检查几处易漏细节:
-
onWorkerStart中初始化数据库/Redis 连接 —— Workerman 4 更强调“每个进程独享资源”,禁止在主进程构造函数初始化后 fork 共享 - WebSocket 心跳配置从
$worker->ping_interval改为$ws_worker->pingInterval(属性名驼峰化,4.x 开始统一) - 若用了 GatewayWorker,确认 BusinessWorker 与 Gateway 版本匹配,3.x 的 gateway 协议与 4.x 不完全兼容
- 日志输出建议改用
Worker::log()而非echo或var_dump,避免阻塞事件循环











