Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Mercure] Add the component#28877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
dunglas wants to merge13 commits intosymfony:masterfromdunglas:mercure
Closed

Conversation

@dunglas
Copy link
Member

@dunglasdunglas commentedOct 15, 2018
edited
Loading

QA
Branch?master
Bug fix?no
New feature?yes
BC breaks?no
Deprecations?no
Tests pass?yes
Fixed ticketsn/a
LicenseMIT
Doc PRtodo

Last week, I introducedthe Mercure protocol as well asa reference implementation of a mercure hub.

Mercure allows to push updates from servers to clients, through a hub. It usesserver-sent events, so it passes through all firewalls, works even with very old browsers, is super efficient when using HTTP/2 and is natively supported in all major browsers (no SDK required).Try the demo.

For instance, Mercure allows to automatically and instantly update all currently connected clients (web apps, mobile apps...) every time a resource is modified, created or deleted (e.g. when aPOST request occurs).

This PR introduce a new component, allowing to use Mercure in Symfony projects in a very convenient way.

Minimal example

<?php// dipatch an update from Symfony (for instance in a POST controller)namespaceApp\Controller;useSymfony\Component\HttpFoundation\Response;useSymfony\Component\Messenger\MessageBusInterface;useSymfony\Component\Mercure\Update;class Publish{publicfunction__invoke(MessageBusInterface$bus)    {// use doctrine to save an event$bus->dispatch(newUpdate('http://example.com/foo/bar','New content of the resource'        ));returnnewResponse('OK');    }}
// subscribe to updates client side, can be in a Twig template, in a Progressive Web App...consteventSource=newEventSource('https://demo.mercure.rocks/subscribe?topic=http://example.com/foo/bar');// The callback will be called every time an update is publishedeventSource.onmessage=e=>console.log(e);// do something with the payload, for instance update the view

Here we use the Messenger component to dispatch the update. It means that the request to the Mercure hub can be sent synchronously (by default), or asynchronously in a worker if you configure a transport such as RabbitMQ or Reddis.

The corresponding config:

# configframework:mercure:hubs:default:url:'https://demo.mercure.rocks/hub'jwt:'%env(MERCURE_JWT)%'# The publisher JWT, provided by the hub

Subscribing to several topics

Mercure allows to subscribe to several topics, and to topics matching a given pattern:

// client sideconsthub=newURL('https://demo.mercure.rocks/hub');hub.searchParams.append('topic','https://example.com/foo/{id}');// Templated IRI, any topic matching it will be receivedhub.searchParams.append('topic','https://example.com/another/resource');consteventSource=newEventSource(hub);eventSource.onmessage=e=>console.log(e);// do something with the payload

Making the Hub auto-discoverable

Mercure can leverage the web linking RFC to advertise available hubs to the clients:

<?phpnamespaceApp\Controller;useFig\Link\Link;useLcobucci\JWT\Builder;useLcobucci\JWT\Signer\Hmac\Sha256;useSymfony\Bundle\FrameworkBundle\Controller\AbstractController;useSymfony\Component\HttpFoundation\Request;class Discoverextends AbstractController{publicfunction__invoke(Request$request)    {$this->addLink($request,newLink('mercure','https://demo.mercure.rocks'));return$this->json(['@id' =>'http://example.com/foo/bar','availability' =>'https://schema.org/InStock']);    }}

In the previous example, the Symfony WebLink Component is used to generate the appropriateLink header. The hub can then be discovered usingsome lines of JS.

Requires#28875.

Authorization

Mercure also allows to securely dispatch updates only to users having a specific username, role or group... you name it. To do so, you just have to set a JSON Web Token containing the list of targets in a claim namedmercureTargets. This token must be stored in a cookie namedmercureAuthorization (the protocol also allows to transmit the JWT using OAuth, OpenID Connect, and any other transport).

<?phpnamespaceApp\Controller;useFig\Link\Link;useLcobucci\JWT\Builder;useLcobucci\JWT\Signer\Hmac\Sha256;useSymfony\Bundle\FrameworkBundle\Controller\AbstractController;useSymfony\Component\HttpFoundation\JsonResponse;useSymfony\Component\HttpFoundation\Request;class Discoverextends AbstractController{publicfunction__invoke(Request$request)    {// ...$username =$this->getUser()->getUsername();$token = (newBuilder())// set other JWT appropriate JWT claims, such as an expiration date            ->set('mercure', ['subscribe' => [$username,'another-target']])// could also include the security roles            ->sign(newSha256(),'!UnsecureChangeMe!')// store the key in a parameter instead            ->getToken();$response =newJsonResponse(['@id' =>'/demo/books/1.jsonld','availability' =>'https://schema.org/InStock']);$response->headers->set('set-cookie',sprintf('mercureAuthorization=%s; path=/subscribe; secure; httponly; SameSite=strict',$token));return$response;    }}
<?phpnamespaceApp\Controller;useSymfony\Component\HttpFoundation\Response;useSymfony\Component\Messenger\MessageBusInterface;useSymfony\Component\Mercure\Update;class Publish{publicfunction__invoke(MessageBusInterface$bus)    {// use doctrine to save an event$id =$bus->dispatch(newUpdate('http://example.com/foo/bar','New content of the resource',            ['a-username','a-group','anything-else']// the targets authorized to receive this update        ));returnnewResponse($id,200, ['Content-Type' =>'text/plain']);    }}

That's all! Only users having one of the specified targets will receive the update. In this example,lcobucci/jwt is used, but feel free to use any other library to generate the token.

To use the authorization feature, the hub and the Symfony app must be served from the same domain name (can be different subdomains).

kaznovac, karser, eko, TomasVotruba, and m4s0 reacted with thumbs up emojiToflar, kunicmarko20, Deuchnord, GwendolenLynch, mablae, dkarlovi, kayneth, alanpoulain, mmarchois, aitboudad, and 12 more reacted with hooray emojiDavidBadura reacted with confused emojiOskarStark, ogizanagi, vasilvestre, Guikingone, Korbeil, sstok, PabloKowalczyk, Aerendir, mykiwi, k911, and karser reacted with heart emoji
@javiereguiluz
Copy link
Member

If this component is accepted, I wonder if we should rename it to a more generic name describing its purpose instead of using the name of the provider used. Laravel for example calls this "Broadcasting" and Rails calls it "ActionCable". Thanks!

chiqui3d and m4s0 reacted with thumbs up emoji

@dunglas
Copy link
MemberAuthor

@javiereguiluz actually, this is not an abstraction layer but an implementation of the protocol (like HttpFoundation implements the HTTP protocol or WebLink implements Web Linking).
It should indeed be possible to create publishers for other transports, but in this case it would be nice to identify which ones, to be sure we can implement them.

</xsd:complexType>

<xsd:complexTypename="mercure_hub">
<xsd:attributename="name"type="xsd:string" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

that one should be marked as required, as it is the key in the map


<xsd:complexTypename="mercure_hub">
<xsd:attributename="name"type="xsd:string" />
<xsd:attributename="url"type="xsd:string"use="required" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

that one should not be required, as it is not required in all XML files being merged together (it is required in the merged config, not in each source config)

private$jwtProvider;
private$httpClient;

publicfunction__construct(string$publishEndpoint,callable$jwtProvider,callable$httpClient =null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I suggest adding some phpdoc describing the expected signature of these callables.

DavidBadura, Koc, nicodevenv, and Kocal reacted with thumbs up emoji
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Possibly in the format used by Psalm and PHPStan IIRC which have the most chance becoming the standard.

@s7anley
Copy link

I'm just curious why Mercure should be part of Framework bundle? Why not integrate it by own bundle?

DavidBadura and neemzy reacted with thumbs up emoji

@dunglas
Copy link
MemberAuthor

dunglas commentedOct 23, 2018
edited
Loading

@s7anley I've no strong opinion about that. I added it to FrameworkBundle because it's what we do most of the time, but not all times (SecurityBundle, WebServerBundle...). Extracting it in a custom bundle will also allow to extract the library out ofsymfony/symfony the time Mercure becomes mature, so I'm 👍

@dunglas
Copy link
MemberAuthor

The component, and the related bundle, are now available as standalone packages:

maryo, devrck, flug, and AntoineGriffon reacted with thumbs up emojiDavidBadura and flug reacted with hooray emoji

@nicolas-grekasnicolas-grekas modified the milestones:next,4.2Nov 1, 2018
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@stofstofstof left review comments

@OskarStarkOskarStarkOskarStark approved these changes

@fabpotfabpotAwaiting requested review from fabpot

+2 more reviewers

@ekoekoeko left review comments

@dkarlovidkarlovidkarlovi left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Projects

None yet

Milestone

4.2

Development

Successfully merging this pull request may close these issues.

9 participants

@dunglas@javiereguiluz@s7anley@eko@dkarlovi@stof@OskarStark@nicolas-grekas@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp