ntavelis / mercure-php
向 mercure 站点发布消息
Requires
- php: >=7.4
- ext-json: *
- lcobucci/jwt: ^4.1.5
- nyholm/psr7: ^1.4.1
- psr/http-client: ^1.0
Requires (Dev)
- phpstan/phpstan: ^0.12.23
- phpunit/phpunit: ^8.0
- squizlabs/php_codesniffer: ^3.0
- symfony/http-client: ^5.3
README
此包从您的 PHP 应用程序向 mercure 站点发布通知。这些消息可以从客户端(Web 浏览器或移动应用程序)进一步消费,以提供应用程序的实时更新。这一切都归功于 Mercure 协议,您可以在这里了解更多关于该协议的信息。
向 dunglas 致敬,他在 mercure 项目 中的工作。
安装
通过 Composer 安装此包
$ composer require ntavelis/mercure-php
Mercure 站点安装
Mercure 站点是使用 GO 语言编写的二进制文件,应该启动并运行,以便从 PHP 应用程序接收消息。
请参阅官方文档了解如何安装站点
发送公开通知
我们需要从我们的 PHP 服务器向 mercure 站点发布消息,然后在我们的客户端(例如通过 JavaScript 浏览器)消费它们。
PHP 代码
以下示例是一个控制器,在 symfony 框架中
<?php namespace App\Controller; use Ntavelis\Mercure\Messages\Notification; use Ntavelis\Mercure\Providers\PublisherTokenProvider; use Ntavelis\Mercure\Publisher; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpClient\Psr18Client; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; class PublishController extends AbstractController { /** * @Route("/publish", name="publish") */ public function index() { $notification = new Notification(['https:///books/2'], ['data' => 'new public event']); $publisher = new Publisher( 'https://:3000/.well-known/mercure', new PublisherTokenProvider('aVerySecretKey'), new Psr18Client() ); $publisher->send($notification); return new JsonResponse(['success']); } }
注意:当我们初始化发布者时,我们需要传递一个 PSR-18 兼容的客户端,在我们的例子中我们使用 symfony/http-client。此包不提供客户端,您需要自己初始化并传递一个给发布者。例如,要提供 symfony http-client,您需要先通过 composer 安装它
composer require symfony/http-client
提示:您可以通过使用流畅 API 来手动构建类,以达到相同的结果。
通知类
Ntavelis\Mercure\Messages\Notification 的第一个参数是要发布通知的主题数组。主题可以是任何对您有意义的字符串,例如 'orders'、'clients'、'notes'、'https:///books/2' 等。第二个参数是要传递给客户端的数据数组,该数组将被 json 编码,并从客户端接收,客户端可以据此采取行动。
发布者类
这是一个实际上将通知发送到 mercure 站点的类,它期望在实例化时接收三个参数。Mercure 站点 URL、实现 Ntavelis\Mercure\Contracts\TokenProviderInterface 的类(您可以使用包中的一个,或提供自己的)以及上述提到的 PSR-18 兼容客户端的实例。
客户端 JavaScript 代码
为了消费上述公开消息,我们的客户端代码将如下所示
// The subscriber subscribes to updates for any topic matching https:///books/{id} const url = new window.URL('https://:3000/.well-known/mercure'); url.searchParams.append('topic', 'https:///books/{id}'); const eventSource = new EventSource(url.toString()); // The callback will be called every time an update is published eventSource.onmessage = e => { console.log(JSON.parse(e.data));// do something with the payload };
注意:我们使用了一个通配符 id,所以我们将收到任何给定 {id} 的书籍的通知。
以上示例使用了原生 js 代码,没有使用任何库。请参阅EventSource 文档以获取更多信息。
可选地,我们可以为我们的主题指定一个特定的类型,并在前端仅监听该类型,更多信息 在这里
私有消息
与公共消息不同,私有消息并非面向所有人消费。私有消息是指仅面向已验证消费者消费的消息。
要发布和消费私有消息,我们需要三样东西
- 从我们的PHP服务器代码发布私有通知。
- 提供一个端点生成客户端的JWT令牌。
- 客户端向后端发送请求以获取JWT令牌,该令牌证明我们能够接收私有消息并使用收到的令牌订阅事件。
PHP代码(步骤1)
从我们的PHP服务器代码中,我们现在必须使用Ntavelis\Mercure\Messages\PrivateNotification
类,该类接收与Notification类相同的参数,但将通知标记为私有。
<?php namespace App\Controller; use Ntavelis\Mercure\Messages\PrivateNotification; use Ntavelis\Mercure\Providers\PublisherTokenProvider; use Ntavelis\Mercure\Publisher; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpClient\Psr18Client; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Annotation\Route; class PublishController extends AbstractController { /** * @Route("/publish", name="publish") */ public function index() { $notification = new PrivateNotification( ['https:///author/ntavelis/books/155'], ['data' => 'new private event'] ); $publisher = new Publisher( 'https://:3000/.well-known/mercure', new PublisherTokenProvider('aVerySecretKey'), new Psr18Client() ); $publisher->send($notification); return new JsonResponse(['success']); } }
这就完成了,我们发布了一条仅针对用户ntavelis
的私有消息,主题指定为https:///author/ntavelis/books/155
。也许他是我们应用中书籍的作者,我们希望向客户端发送通知以更新他的私有仪表板。
提示:您可以通过使用流畅 API 来手动构建类,以达到相同的结果。
提供生成客户端令牌的端点(步骤2)
为了在我们的JavaScript中消费消息,我们需要在订阅中心时提供一个有效的令牌,以证明我们有权限接收私有通知。为此,我们可以向PHP端点发起Ajax请求以接收令牌。此包将为我们生成令牌,我们只需提供一个客户端可以调用的端点以接收令牌。
这是生成客户端(订阅者)令牌的PHP代码
<?php namespace App\Controller; use Ntavelis\Mercure\Providers\SubscriberTokenProvider; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; class SubscribeController extends AbstractController { /** * @Route("/subscribe", name="subscribe") */ public function index(Request $request) { $content = $request->getContent(); $contentArray = json_decode($content, true); $topic = $contentArray['topic']; // TODO authorize the request $provider = new SubscriberTokenProvider('aVerySecretKey'); $token = $provider->getToken([$topic]); return new JsonResponse(['token' => $token]); } }
在上面的示例中,我们使用了Ntavelis\Mercure\Providers\SubscriberTokenProvider
来获取特定主题的有效令牌。
注意:授权请求取决于您,您应该检查请求是否有效,并且它可以接收此主题的私有通知。
在客户端获取令牌并使用该令牌订阅事件(步骤3)
最后一步,将所有这些放在一起,从我们的客户端代码中获取令牌,并使用此令牌从中心订阅事件。
注意:我们将在本例中使用polyfill库将授权头传递给中心,因为EventSource不支持原生支持。
// use a polyfill library import { EventSourcePolyfill } from 'event-source-polyfill'; // Make a post request to the server to obtain the token for the topic we want to receive notifications for const token = fetch('/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({topic: 'https:///author/ntavelis/books/155'}), // send the topic we need to authenticate on }).then(response => response.json()) .then((json) => json.token); // When we have the token subscribe to the EventSource by passing the token token.then((token) => { const url = new window.URL('https://:3000/.well-known/mercure'); url.searchParams.append('topic', 'https:///author/ntavelis/books/155'); // Authorization header const eventSourceInitDict = { headers: { 'Authorization': 'Bearer ' + token } }; const es = new EventSourcePolyfill(url.toString(), eventSourceInitDict); es.onmessage = e => { console.log(JSON.parse(e.data)); }; });
请记住,您还可以使用基于cookie的认证连接到中心,您可以在此处了解更多信息 在这里。
额外
如果您想为特定类型配置通知,请参阅文档 在这里。
变更日志
请参阅 CHANGELOG 以获取有关最近更改的更多信息。
贡献
请参阅 CONTRIBUTING 和 CODE_OF_CONDUCT 以获取详细信息。
安全
如果您发现任何与安全相关的问题,请通过电子邮件 davelis89@gmail.com 发送,而不是使用问题跟踪器。
致谢
许可协议
MIT许可协议(MIT)。有关更多信息,请参阅 许可文件。