coroutine::getcontext() 返回当前协程的 swoole\coroutine\context 实例,是协程隔离的键值容器,仅在协程内有效,不继承父子关系,支持数组语法存取但不支持嵌套自动创建。

Coroutine::getContext 返回什么
Coroutine::getContext() 返回当前协程的上下文对象(Swoole\Coroutine\Context 实例),它本质上是一个协程隔离的、类似 ArrayObject 的容器,用于在同个协程生命周期内跨函数、跨 await 点存储和读取数据。不是全局变量,也不是进程/线程级共享——这点容易误用。
常见错误现象:在协程外调用返回 null;或误以为它能穿透协程边界(比如从子协程改父协程的 context);或当成 static 变量滥用导致数据污染。
- 必须在协程内调用,否则返回
null - 每个协程有独立实例,父子协程之间不继承 context 内容(除非手动复制)
- 底层基于
zend_hash实现,读写性能接近原生数组,无明显开销
怎么安全地存取 key-value
直接把 Coroutine::getContext() 当作可读写数组用即可,支持 [] 语法和 offsetGet/offsetSet。但要注意 PHP 引用和对象赋值行为:
// 正确:直接赋值 $ctx = Coroutine::getContext(); $ctx['user_id'] = 123; $ctx['request'] = $request; // $request 是对象,存的是引用 // 错误:先取再赋值(可能触发 __get 导致意外行为) $ctx['config']['timeout'] = 5000; // ❌ 不安全!context 不支持嵌套自动创建 // 正确写法 $config = $ctx['config'] ?? []; $config['timeout'] = 5000; $ctx['config'] = $config;
- 避免深层嵌套赋值,context 不是递归代理,
$ctx['a']['b'] = 1会报错或静默失败 - 存对象时注意生命周期:若对象含资源(如
Swoole\Coroutine\MySQL连接),退出协程后连接会被自动关闭,不要跨协程复用 - key 名建议用字符串,避免数字 key 被当成数组索引误判
和 Fiber::getCurrent()->getArguments() 或 $_SERVER 混用的坑
有人想用 getContext() 替代传统请求上下文传递,结果和 Fiber 原生机制或 Swoole 的 $_SERVER 变量混淆。它们完全无关:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
-
$_SERVER是协程间共享的(PHP 层面模拟的超全局),不是协程安全的,绝不能用来存用户态数据 -
Fiber::getCurrent()->getArguments()是 Fiber 构造时传入的参数,只读、不可变,且与 Swoole 协程不互通 -
Coroutine::getContext()是 Swoole 协程专属,仅在Swoole\Coroutine\run或go启动的协程中有效
典型误用场景:在 onRequest 回调里往 $_SERVER['ctx'] 写数据,然后期望在后续 go 协程里读到——实际读到的可能是其他请求的残留值。
替代方案:什么时候不该用 getContext
如果只是临时传参,优先用函数参数或闭包捕获;如果需要结构化上下文(如 trace_id、user、tenant),建议封装成类并注入,而不是散落在 context 数组里。
- 调试困难:context 内容无法被 xdebug 或 IDE 自动提示,键名易拼错
- 类型不可控:PHP 不检查 key 是否存在,
$ctx['user_id'] ?? null必须每次都写 - 框架集成风险:Hyperf、Swoft 等已内置 Context 组件(如
Hyperf\Context\Context),它们做了更严格的生命周期管理和类型约束,直接混用Coroutine::getContext()可能绕过框架拦截逻辑
真正需要它的场景其实很窄:写底层扩展、调试协程调度器、或在没有框架的裸 Swoole 脚本里做轻量状态透传。多数业务代码里,它是个“知道就行,慎用”的工具。










