Laravel 不能直接用 BROADCAST_DRIVER=mercure,因为官方未实现 Mercure 广播驱动,强行配置会报错“Driver [mercure] not supported”;第三方包多已停更且协议不兼容,导致事件发布失败或前端收不到消息。

FrankenPHP 内置 Mercure Hub,Laravel 项目无需额外部署服务、不改广播驱动、不用 Pusher 或 Soketi,就能开箱实现实时推送——前提是关闭 Laravel 的默认广播机制,直接走 HTTP POST + EventSource。
为什么不能直接用 BROADCAST_DRIVER=mercure
Laravel 官方并未提供 Mercure 广播驱动(截至 2026 年 10 月仍无 mercure driver 实现)。你如果在 .env 里写 BROADCAST_DRIVER=mercure,会直接报错:Driver [mercure] not supported。这不是配置遗漏,是 Laravel 核心没实现它。
强行用第三方包(如 laravel-mercure-broadcaster)风险高:多数已停止维护,且依赖旧版 Mercure 协议(v0.12 之前),与 FrankenPHP 内置的 Mercure Hub(基于 mercure v0.15+)不兼容,常见现象是事件发出去但浏览器收不到,curl -i http://localhost/.well-known/mercure 返回 200 但 EventSource 连接立即关闭。
正确做法是绕过 Laravel 广播系统,用原生方式发布和订阅:
- 后端用
mercure_publish()函数或直连http://localhost/.well-known/mercure发布 - 前端用标准
EventSource订阅,不引入laravel-echo或任何 SDK - 频道地址用绝对 URL(如
https://localhost/demo),不能用 Laravel 的逻辑频道名(如chat.123)
mercure_publish() 怎么在 Laravel Controller 里安全调用
FrankenPHP 在请求上下文中注入了 Mercure Hub 实例,但 mercure_publish() 是 PHP 扩展函数(由 FrankenPHP 编译时内置),不是 Laravel Facade,不能在队列任务或 Artisan 命令中直接使用——那些场景没有 FrankenPHP 请求上下文,调用会返回 false 且无错误提示。
只在 Web 请求生命周期内可用,例如:
// app/Http/Controllers/NotificationController.php
public function send()
{
$update = json_encode(['message' => 'Hello from Laravel!', 'time' => now()]);
<pre class="brush:php;toolbar:false;">// ✅ 正确:在控制器方法中调用,主题必须是完整 URL
$ok = mercure_publish('https://localhost/demo', $update);
// ❌ 错误:主题不能是相对路径、不能缺协议、不能含通配符
// mercure_publish('/demo', $update); // 400 Bad Request
// mercure_publish('demo', $update); // 400
// mercure_publish('https://localhost/{id}', $update); // 404 unless wildcard enabled in Caddyfile
return response()->json(['published' => $ok]);}
注意点:
- 主题 URL 必须与前端
EventSource订阅地址完全一致(协议、域名、路径) - 发布内容必须是合法 JSON 字符串,不能是 PHP 数组(
mercure_publish()不自动json_encode) - 返回值为布尔型:
true表示已入队(不保证送达),false表示 Hub 不可用或参数非法 - 若需从队列中发布(如发通知),改用 HTTP POST 到
/.well-known/mercure(见下一条)
如何让队列任务或 Artisan 命令也能推送 Mercure 事件
因为 mercure_publish() 依赖 FrankenPHP 请求上下文,队列进程(queue:work)和 Artisan 命令是独立 CLI 进程,无法调用它。此时唯一可靠方式是模拟 HTTP POST 请求到 Mercure Hub 的 publish 端点。
FrankenPHP 的 Mercure Hub 默认暴露在 http://localhost/.well-known/mercure(HTTP 模式)或 https://localhost/.well-known/mercure(HTTPS 模式)。Caddy 会自动代理该路径到内置 Hub。
示例(在 Job 中):
// app/Jobs/SendNotificationJob.php
public function handle()
{
$data = ['message' => 'Task completed', 'job_id' => $this->jobId];
<pre class="brush:php;toolbar:false;">$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'http://localhost/.well-known/mercure', [
'form_params' => [
'topic' => 'https://localhost/notifications',
'data' => json_encode($data),
],
'headers' => [
'Authorization' => 'Bearer ' . config('mercure.jwt_key'),
],
]);}
关键前提:
-
config('mercure.jwt_key')需提前在config/mercure.php中定义,值为 Caddyfile 中配置的 JWT 密钥(见下条) - Caddyfile 必须启用 Mercure 并配置
publish_origins,否则 POST 会被拒绝(403) - 本地开发可跳过 JWT(Caddy 默认允许 localhost 免鉴权发布),但生产环境必须带
Authorization: Bearer xxx - 不要用
file_get_contents()+stream_context_create():容易因超时或 SSL 验证失败静默失败
Caddyfile 中 Mercure 必须开启的三项配置
FrankenPHP 内置 Mercure Hub,但默认不启用。Caddyfile 中必须显式声明 mercure 指令块,否则所有 Mercure 请求都返回 404。
最小可用配置(放在 route 或全局 handle 内):
mercure {
# 1. 必须指定 JWT 密钥,用于验证发布请求
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
<pre class="brush:php;toolbar:false;"># 2. 允许哪些来源可以发布(生产环境务必限制,开发可设为 localhost)
publish_origins ["http://localhost", "https://localhost"]
# 3. 启用订阅者认证(可选,但私有频道必须开)
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}}
常见坑:
-
publish_origins缺失或值不匹配 → POST 返回 403 Forbidden -
publisher_jwt使用空字符串或未设置环境变量 → Hub 启动失败,FrankenPHP 日志报failed to parse JWT key - 忘记在
.env中设置MERCURE_PUBLISHER_JWT_KEY→ 用openssl rand -hex 32生成并填入 - 前端用
http://订阅但 Caddy 只监听 HTTPS → 浏览器因混合内容拦截连接
复杂点在于:Mercure 订阅是跨域的,而 JWT 鉴权密钥、发布源白名单、协议一致性三者必须严丝合缝,漏一个,整个链路就静默中断——没有错误日志,只有浏览器控制台里一闪而过的 EventSource failed to connect。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











