跳转到主要内容
包在推送实时事件时,要把频道名称的定义和放行用户的判定分开设计。包在 boot() 中注册的频道,如果与使用方应用 routes/channels.php 中的名称相同,可能会被覆盖。理解这种注册顺序和评估顺序,可以减少维护时的缺陷。 本页参考了 Laravel 官方文档的广播,以及用于确认的最新版本 v13.42.0 的实现。事件的发送方法请参阅广播,提供者的加载请参阅包自动发现。

频道授权的机制

订阅私有频道时,客户端会向 /broadcasting/auth 发送请求。BroadcastController::authenticate() 将处理交给 Broadcast::auth(),由 Broadcaster 按顺序评估已注册的回调。 评估规则如下。
  • 回调返回 false 时,会立即抛出 AccessDeniedHttpException。
  • 返回 true 等真值时,授权在此时成立。
  • 返回 null 等假值时,会继续评估下一个注册的回调。如果没有任何回调放行,最后会抛出 AccessDeniedHttpException。
Pusher 和 Reverb 驱动会在授权前去掉 private- 和 presence- 前缀。因此,注册的频道名称中不要包含前缀。

在包中注册频道

以一个虚构的 Acme\Orders 包推送订单状态变化为例。频道名称为 acme.orders.{order},只允许订单的所有者订阅。 在包的 ServiceProvider 中,于 boot() 内调用 Broadcast::channel()。判定委托给包内的类。
join() 的第 1 个参数会传入授权时的用户。之后的参数是频道名称中 {...} 的值。当 {order} 的参数名与 Order $order 一致,且类型是 UrlRoutable 的子类时,路由模型绑定就会生效。
Broadcast::channel() 的 $callback 可以指定 callable,或带有 join() 方法的类名。指定类名时,Broadcaster::normalizeChannelHandlerToCallable() 会通过容器创建实例,然后调用 join()。

使用方应用的覆盖

使用方应用传给 withBroadcasting() 的文件(通常是 routes/channels.php)会在 withBroadcasting() 注册的 booted 回调中加载。booted 回调在所有服务提供者的 boot() 结束后执行,因此包的 boot() 中的注册会先进行。 Broadcaster::channel() 以频道名称为键,保存到内部数组中。因此,如果使用方应用注册了相同的名称,第 2 次注册会替换包的注册。Laravel 不会把同名注册当作重复错误。 要防止覆盖,请让频道名称唯一。例如像 acme.orders.{order} 这样加上包专属的前缀,使用方应用注册相同名称的可能性就会降低。 频道名称是与客户端之间公开的约定。一旦确定,请不要更改。更改后,旧的客户端将无法订阅。
对于同名频道,后注册的会静默地优先。php artisan channel:list 会显示频道名称和解析目标(类名或 Closure),可以确认留下的是哪一个注册。

频道选项与守卫

Broadcast::channel() 的第 3 个参数 $options 会作为每个频道的设置保存。指定 guards 后,会按顺序尝试用于认证的守卫。
省略 guards 时,使用请求的默认用户($request->user())进行授权。指定 guards 后,会从头开始依次调用 $request->user($guard),并使用最先得到的用户。如果包不写死守卫名称,而是作为配置值公开,使用方应用就可以自行选择。

在测试中确认频道注册

已注册的频道可以通过驱动的 getChannels() 以 Collection 形式获取。键就是频道名称本身。
授权结果通过对 /broadcasting/auth 的 HTTP 测试来确认。null 和 log 驱动的 auth() 不返回任何内容,因此不能用于此测试。频道会在应用启动时注册到默认驱动,因此请先在测试用的 phpunit.xml 等中把 BROADCAST_CONNECTION 设置为 reverb 或 pusher,再发送带认证的请求。
测试应用中需要注册 /broadcasting/auth。这个路由由 Broadcast::routes() 注册,BroadcastManager::channelRoutes() 只是把相同的处理交给 routes()。如果路由已被缓存,routes() 不会执行任何操作。

维护时的注意事项

  • 频道名称是公开的约定。更改名称后,现有客户端将无法订阅。
  • 基于类的频道通过 Container 解析。构造函数的依赖请使用容器能够解析的类型。

总结

包的频道授权只要在 boot() 中注册 Broadcast::channel() 即可工作。如果使用方应用的 routes/channels.php 之后注册了相同的名称,包的授权就会被替换。为频道名称加上前缀、把判定拆分到类中,并用 HTTP 测试确认 /broadcasting/auth 的结果,可以让包更易于维护。

参考的一手资料

最后修改于 2026年11月14日