闭包中修改集合变量需加&因php默认按值传递,而request/user对象天然按引用传递;协程task中不可传$this或context::get(),须提前提取基础类型参数。

Hyperf 3.1 面试中常被问到:为什么在协程内用闭包处理数据时,必须对集合变量加 & 才能修改外部值,而 Request、User 对象却不用?这背后不是框架设计选择,而是 PHP 变量作用域与内存模型的刚性规则。
闭包里 use($list) 和 use(&$list) 的区别
第一步:理解 PHP 默认传值机制。闭包通过 use 捕获外部变量时,默认创建副本,而非绑定原变量内存地址。
第二步:验证 $list 不加 & 的后果。执行 $list->push() 后,dd($list) 仍为空——因为操作的是副本,闭包退出后副本销毁,原始变量未受影响。
第三步:加上 & 后,闭包获得的是 $list 变量本身的引用。所有 push()、add()、transform() 操作直接作用于原始内存位置,外部可立即读取累积结果。
注意:这个 & 是 PHP 层级语法要求,与 Hyperf 版本无关,3.1 和 3.0 行为完全一致。
为什么 $request 和 $user 不用加 &
方法一:对象天然按引用传递。PHP 5+ 起,对象变量存储的是对象标识符(handle),use($request) 实际传递的是指向同一对象实例的指针,无需 & 修饰。
方法二:Request 和 User 都是类实例,不是值类型。即使你在闭包里调用 $request->all() 或 $user->id,底层访问的仍是原始对象属性,不会触发深拷贝。
方法三:反例验证。若传一个数组 $data = ['name' => 'foo'],不加 & 就无法在闭包里 $data['age'] = 25 并让外部生效——因为数组在 PHP 中默认是值类型(除非是对象包装的 ArrayObject)。
协程上下文里不能靠 use 传 $this 或 Context::get()
TaskWorker 进程与 Worker 进程完全隔离,协程上下文只存在于当前 worker 内。投递任务时,use($this) 或 use(Context::get()) 会直接报错:Serialization of 'Closure' is not allowed 或 unserialize(): Error at offset。
正确做法是提前“拍平”数据:把用户 ID、订单号、请求参数等提取成字符串/整型/数组,再通过 task()->execute() 传入。例如只传 ['user_id' => 123, 'order_sn' => 'SN20260806'],而非整个 Request 对象或 $this 实例。
切记:task_enable_coroutine => false 不能绕过序列化限制,它只关闭协程调度,不改变 IPC 参数传递机制。











