
FrankenPHP 内置 Mercure Hub 实时推送实战指南从 Caddyfile 配置到 PHP 发布事件【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp导读FrankenPHP 内置了 Mercure 与 docs/mercure.md 为骨架结合仓库源码mercure.go、caddy/mercure.go、frankenphp.c与测试用例完整讲解如何启用内置 Hub、订阅更新、并通过mercure_publish()、file_get_contents()或 Symfony Mercure 组件三种方式发布事件帮助你构建基于 HTTP 的实时 Web 应用。为什么选择 Mercure 而非 WebSocketMercure 允许你把实时事件推送给所有已连接的设备这些设备会立即收到一个 JavaScript 事件。相比 WebSocket它是 Web 浏览器的原生能力使用简单、无需额外协议握手与消息格式约定且被所有现代浏览器原生支持基于EventSource服务器发送事件规范。对 PHP 生态而言这尤其方便发布端只是一个普通的 HTTP POST 请求任何 PHP 运行环境CLI、FPM、以及 FrankenPHP 的工作进程都能轻松发起服务端代码几乎不需要改动既有架构。启用 Mercure HubMercure 支持默认是关闭的源码中通过构建标签nomercure控制编译期裁切参见 mercure-skip.go。要开启它只需在Caddyfile中配置mercure指令块。最小化配置示例# 要响应的主机名 localhost mercure { # 用于签发发布者 JWT 令牌的密钥 publisher_jwt !ChangeThisMercureHubJWTSecretKey! # 设置了 publisher_jwt 时必须同时设置 subscriber_jwt subscriber_jwt !ChangeThisMercureHubJWTSecretKey! # 允许匿名订阅者无需 JWT anonymous } root public/ php_server关键点publisher_jwt与subscriber_jwt必须成对出现。FrankenPHP 在构建 Mercure 路由时要求这两个密钥都存在见 caddy/mercure.go 中createMercureRoute()对MERCURE_PUBLISHER_JWT_KEY与MERCURE_SUBSCRIBER_JWT_KEY环境变量的强制检查逻辑缺一即报错。anonymous允许不携带 JWT 的客户端直接订阅生产环境请谨慎评估是否开启。完整指令列表如subscriptions订阅 API、demo演示页、传输后端等参见 Mercure Hub 官方配置文档。使用 Docker 镜像的快捷路径仓库内置的示例 caddy/frankenphp/Caddyfile 已包含被注释掉的 Mercure 配置块并通过环境变量接入mercure { # 发布者 JWT 密钥 publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG} # 订阅者 JWT 密钥 subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG} # 允许匿名订阅者请再次确认是否符合预期 anonymous # 启用订阅 API请再次确认是否符合预期 subscriptions # 额外指令 {$MERCURE_EXTRA_DIRECTIVES} }使用 Docker 镜像参见 docs/docker.md时取消/etc/frankenphp/Caddyfile中 Mercure 区块的注释并通过MERCURE_PUBLISHER_JWT_KEY、MERCURE_SUBSCRIBER_JWT_KEY、MERCURE_PUBLISHER_JWT_ALG、MERCURE_SUBSCRIBER_JWT_ALG环境变量注入密钥与签名算法即可。配置示例还提示日志块中可用 Caddy 的查询参数过滤replace authorization REDACTED脱敏订阅 URL 中可能携带的authorization参数避免 JWT 落入访问日志。订阅更新原生 EventSource默认情况下Mercure Hub 暴露在 FrankenPHP 服务器的/.well-known/mercure路径上。订阅只需使用浏览器原生的EventSourceJavaScript 类!-- public/index.html -- !doctype html titleMercure Example/title script const eventSource new EventSource(/.well-known/mercure?topicmy-topic); eventSource.onmessage function (event) { console.log(New message:, event.data); }; /scripttopic参数指定订阅的主题可重复传参订阅多个主题。当服务端向该主题发布事件时浏览器会自动触发message事件回调event.data即服务端推送的数据。发布更新方式一FrankenPHP 内置mercure_publish()函数FrankenPHP 提供了一个便捷的mercure_publish()PHP 函数直接向内置 Hub 发布更新?php // public/publish.php $updateID mercure_publish(my-topic, json_encode([key value])); // 写入 FrankenPHP 日志 error_log(update $updateID published, 4);函数完整签名如下定义见 frankenphp.stub.php/** * param string|string[] $topics */ function mercure_publish(string|array $topics, string $data , bool $private false, ?string $id null, ?string $type null, ?int $retry null): string {}参数说明$topics发布目标主题可以是单个字符串也可以是字符串数组向多个主题同时广播。$data事件数据默认空字符串。$private是否为私有事件默认false。私有事件只推送给订阅时携带了授权 Cookie 的客户端通常配合 Symfony Mercure 的 Cookie 授权机制使用。$id事件 ID默认null由 Hub 自动生成。$type事件类型对应EventSource的event字段默认null客户端收到message事件。$retry客户端断线重连的等待毫秒数默认null使用 Hub 默认值。返回值发布成功后返回生成的更新 ID字符串。底层实现该函数在 frankenphp.c 中定义通过ZEND_PARSE_PARAMETERS解析最多 6 个参数并做类型校验$topics必须为array|string否则抛出参数类型错误随后调用 Go 侧go_mercure_publish()实现见 mercure.go若当前请求上下文未配置 Mercure Hubfc.mercureHub nil记录错误日志并抛出RuntimeException: No Mercure hub configured对应错误码 1主题按 zval 类型分派IS_STRING转成单元素主题列表IS_ARRAY通过GoPackedArray转为字符串切片构造mercure.Update携带Data、ID、Retry、Type、Private等字段后调用fc.mercureHub.Publish()失败时抛出RuntimeException: Publish failed错误码 2。仓库的集成测试 mercure_test.go 同时覆盖了普通请求模式TestMercurePublish_module与 worker 模式TestMercurePublish_worker测试脚本 testdata/mercure-publish.php 展示了函数各种参数的用法echo update 1: . mercure_publish(foo, bar, true, myid, mytype, 10) . \n; echo update 2: . mercure_publish([baz, bar]) . \n;方式二file_get_contents()发起 HTTP POST不依赖任何 FrankenPHP 专属函数直接向 Hub 发送带认证的 POST 请求即可发布更新参数为topic与data?php // public/publish.php const JWT eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.PXwpfIGng6KObfZlcOXvcnWCJOWTFLtswGI5DZuWSK4; $updateID file_get_contents(https://localhost/.well-known/mercure, context: stream_context_create([http [ method POST, header Content-type: application/x-www-form-urlencoded\r\nAuthorization: Bearer . JWT, content http_build_query([ topic my-topic, data json_encode([key value]), ]), ]])); // 写入 FrankenPHP 日志 error_log(update $updateID published, 4);关于 JWT 的关键事实与 Caddyfile 配置严格对应Authorization: Bearer头中的 JWT 必须使用Caddyfile中mercure.publisher_jwt配置的密钥进行签名JWT 的 payload 必须包含mercureclaim且带有对目标主题的publish权限例如{ mercure: { publish: [*] } }上述示例 JWT 即对全部主题拥有发布权限通配符*。完整授权规范参见 Mercure 官方发布者文档。可用 jwt.io 链接 在线生成/验证令牌但生产环境建议使用短期有效、动态生成的令牌并选用受信任的 JWT 库 在服务端签发避免静态密钥长期暴露。方式三Symfony Mercure 组件推荐用于复杂应用Symfony Mercure Component 是独立的 PHP 库不需要完整 Symfony 框架它封装了 JWT 生成、更新发布以及订阅者的 Cookie 授权。首先用 Composer 安装composer require symfony/mercure lcobucci/jwt然后这样使用?php // public/publish.php require __DIR__ . /../vendor/autoload.php; const JWT_SECRET !ChangeThisMercureHubJWTSecretKey!; // 必须与 Caddyfile 中的 mercure.publisher_jwt 一致 // 设置 JWT 令牌提供器 $jwFactory new \Symfony\Component\Mercure\Jwt\LcobucciFactory(JWT_SECRET); $provider new \Symfony\Component\Mercure\Jwt\FactoryTokenProvider($jwFactory, publish: [*]); $hub new \Symfony\Component\Mercure\Hub(https://localhost/.well-known/mercure, $provider); // 序列化更新并分发到 Hub由 Hub 广播给客户端 $updateID $hub-publish(new \Symfony\Component\Mercure\Update(my-topic, json_encode([key value]))); // 写入 FrankenPHP 日志 error_log(update $updateID published, 4);要点JWT_SECRET必须与 Caddyfile 中mercure.publisher_jwt的值完全一致FactoryTokenProvider的publish参数声明了该提供器可发布的主题[*]表示全部主题Hub构造函数的第一个参数即 Hub 端点 URLhttps://localhost/.well-known/mercureUpdate对象携带主题与数据publish()返回更新 ID。与主流 PHP 框架的集成Mercure 还被以下框架与平台原生支持LaravelSymfonyAPI Platform工作原理速览从源码结构可以梳理出 FrankenPHP 集成 Mercure 的完整链路配置期Caddy 模块启动时assignMercureHub()caddy/mercure.go通过mercureCaddy.FindHub()查找配置中的 Mercure Hub将其注入到 FrankenPHP 的请求选项frankenphp.WithMercureHub以及每一个 worker 的选项WithWorkerMercureHub中请求期worker 初始化时通过configureMercure()mercure.go把 Hub 绑定到 worker 上下文之后 PHP 代码调用mercure_publish()即可直接经 C 层转发到 Go 侧完成发布无 Hub 兜底若未配置 Hub 却调用了mercure_publish()Go 侧返回错误码 1PHP 侧抛出RuntimeException见 frankenphp.c避免静默失败编译期可选通过nomercure构建标签可完全剔除 Mercure 支持mercure-skip.go此时mercure_publish()恒返回错误码 3适合追求最小化二进制体积的场景。小结FrankenPHP 的内置 Mercure Hub 为 PHP 应用提供了一条从服务端到浏览器的实时事件通道Caddyfile 中一条mercure指令块即可启用浏览器端零依赖原生EventSource直接订阅/.well-known/mercure发布端既可用内置mercure_publish()函数也可走标准 HTTP POST配合 JWT 认证还可直接复用 Symfony Mercure 组件获得完整的令牌管理与授权能力。无论你是构建实时通知、活动流、协作应用还是后台任务进度推送这套方案都能在保持 PHP 生态简洁性的同时交付低延迟的实时体验。【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考