Server-Sent events with PHP and Symfony

Within the magic world of PHP, there’s a library for a lot of issues that you simply don’t know, each day new libraries are born and it’s inconceivable to know every of them.

Because of this, I strive all the time to observe on Twitter, LinkedIn, and GitHub good builders which are sharing new approaches and libraries.

For considered one of our clients, we wanted an utility the place the server can ship knowledge to all purchasers.

So we have to construct the software program with a Server-Despatched occasions method in a short while to launch the product as quickly as attainable to validate the shopper thought.

If you need extra about Server-Despatched occasions, what it means, benefits and limits, you may learn my earlier article right here.

To understand a Server-Despatched Occasions in PHP we discovered a Symfony library symfony/mercure, I noticed this library in some article on LinkedIn and it’s the proof that following the proper folks might be crucial.

You’ll be able to set up symfony/mercure by way of composer on this approach:

composer require symfony/mercure
Enter fullscreen mode

Exit fullscreen mode

After that, you may configure Mercure atmosphere variables into the file: config/packages/mercure.yaml

mercure:  
  hubs:  
    default:  
      url: [https://mercure-hub.example.com/.well-known/mercure](https://mercure-hub.instance.com/.well-known/mercure)  
      jwt:  
        secret: '!ChangeMe!'  
        publish: ['foo', '[https://example.com/foo](https://instance.com/foo')']  
        subscribe: ['bar', '[https://example.com/bar](https://instance.com/bar')']  
        algorithm: 'hmac.sha256'  
        supplier: 'MyProvider'  
        manufacturing facility: 'MyFactory'  
        worth: 'my.jwt'
Enter fullscreen mode

Exit fullscreen mode

Let me clarify what are the values to set into the configuration file:

secret: the important thing to make use of to signal the JWT (all different choices, apart from algorithm, subscribe, and publish can be ignored)

publish: an inventory of matters to permit publishing when producing the JWT (solely usable when secret or manufacturing facility are supplied)

subscribe: an inventory of matters to permit subscribing to when producing the JWT (solely usable when secret, or manufacturing facility are supplied)

algorithm: The algorithm to make use of to signal the JWT (solely usable when the key is supplied)

supplier: The ID of service to name to offer the JWT (all different choices can be ignored)

manufacturing facility: The ID of service to name to create the JWT (all different choices, apart from subscribe, and publish can be ignored)

worth: the uncooked JWT to make use of (all different choices can be ignored)

Robotically inside your .env can be up to date with atmosphere variables.

If you’re utilizing a Symfony model lower than 6, the YAML file doesn’t exist and also you want solely to configure your atmosphere variables into the .env file like this:

MERCURE_URL=[http://localhost:9090/.well-known/mercure](http://localhost:9090/.well-known/mercure)  
MERCURE_PUBLIC_URL=[http://localhost:9090/.well-known/mercure](http://localhost:9090/.well-known/mercure)  
MERCURE_JWT_SECRET=”JWT-AuctionEngine”
Enter fullscreen mode

Exit fullscreen mode

You should use an official docker on your native atmosphere, with a easy configuration like this:

model: "3.5"providers:  
  mercure:  
    container_name: mercure_sse_poc  
    picture: dunglas/mercure  
    atmosphere:  
      SERVER_NAME: ':80'  
      MERCURE_PUBLISHER_JWT_KEY: 'JWT-AuctionEngine'  
      MERCURE_SUBSCRIBER_JWT_KEY: 'JWT-AuctionEngine'  
      MERCURE_CORS_ALLOWED_ORIGINS: '*'  
      MERCURE_EXTRA_DIRECTIVES: |-  
      cors_origins "*"  
      nameless 1  
    ports:  
      — "9090:80"  
      — "4433:443"
Enter fullscreen mode

Exit fullscreen mode

JWT_KEY should be the identical as your atmosphere variables, on this little instance, I declare that it’s not essential to be authenticated and you should utilize Mercure from all domains.

In manufacturing, I like to recommend proscribing these parameters to keep away from undesired issues.

To ship knowledge to your consumer you could instantiate a Mercure object known as Replace with 2 arguments: the subject title the place you need to ship your knowledge, and the information encoded.

After that, you may publish your occasion utilizing an object that implements HubInterface (for this instance).

However symfony/mercure has already its implementation so you should utilize it with out writing your implementation.

Here’s a little instance:

public operate ship(Request $request, HubInterface $hub): void  
{  
    $replace = new Replace(  
       'channelname',  
       json_encode([  
           'foo' => 'bar'  
       ], JSON_THROW_ON_ERROR)  
    );  
    $hub->publish($replace);  
}
Enter fullscreen mode

Exit fullscreen mode

The thing Replace has totally different arguments (straight from https://mercure.rocks/spec):

subject: The identifiers of the up to date subject. It’s RECOMMENDED to make use of an IRI as an identifier. If this title is current a number of occasions, the primary incidence is taken into account to be the canonical IRI of the subject, and different ones are thought-about to be alternate IRIs. The hub MUST dispatch this replace to subscribers which are subscribed to each canonical or alternate IRIs.

knowledge (non-compulsory): the content material of the brand new model of this subject.

personal (non-compulsory): if this title is ready, the replace MUST NOT be dispatched to subscribers not approved to obtain it. See authorization. It is suggested to set the worth to on but it surely CAN comprise any worth together with an empty string.

id (non-compulsory): the subject’s revision identifier: will probably be used because the SSE’s id property. The supplied id MUST NOT begin with the # character. The supplied id SHOULD be a sound IRI. If omitted, the hub MUST generate a sound IRI (RFC3987). A UUID (RFC4122) or a DID MAY be used. Alternatively, the hub MAY generate a relative URI composed of a fraction (beginning with #). That is handy to return an offset or a sequence that’s distinctive for this hub. Even when supplied, the hub MAY ignore the id supplied by the consumer and generate its id.

sort (non-compulsory): the SSE’s occasion property (a selected occasion sort).

retry (non-compulsory): the SSE’s retry property (the reconnection time).

The frontend consumer must subscribe to the subject, the next instance is a bit of a React element:

const url="[http://127.0.0.1:9090/.well-known/mercure?topic=auctions-1](http://127.0.0.1:9090/.well-known/mercure?subject=auctions-1`)";  
const eventSource = new EventSource(url);eventSource.onmessage = occasion => {  
  const outcomes = JSON.parse(occasion.knowledge);  
  console.log(outcomes);  
}
Enter fullscreen mode

Exit fullscreen mode

You should use this code for instance in a UseEffect operate of React and you might be telling that you simply need to subscribe to the subject auctions-1 and each occasion despatched to that channel you’ll print it into the console to see what you may have acquired and you should utilize these knowledge for no matter you need.

I’ve written just a little instance right here to grasp nicely what occurs: https://github.com/AlessandroMinoccheri/sse-poc

Now in the event you clone the repository and observe the directions to put in the backend and frontend you may go to the web page: http://localhost:8080/

You’ll be able to see a easy kind to submit a bid provide.

Now, in the event you open the browser community, choose Fetch/XHR (for Chrome), and reload the web page you may see a subscription request to a selected subject.

Then, in the event you insert a quantity into the textual content area and click on the button to make a bid, you may see within the community tab a brand new POST request for the bid, however contained in the earlier request within the tab Occasions, you may see a brand new occasion despatched by the server with new info!

So in the identical subscription request, we are able to obtain occasions for the server, because of this, the Server-Despatched occasions method is taken into account a monodirectional communication as a result of is the server that sends knowledge to the consumer, and the consumer decides solely to subscribe to matters.

If you happen to make different bids you may see within the tab occasions all knowledge despatched from the server in the identical HTPP connection.

Is it attainable for the consumer to subscribe to a number of matters? Sure, completely, however how?

Nicely, you may simply do that factor into your frontend app (on this instance utilizing React):

const url="[http://127.0.0.1:9090/.well-known/mercure?topic=auctions-1](http://127.0.0.1:9090/.well-known/mercure?subject=auctions-1`)";  
const eventSource = new EventSource(url);eventSource.onmessage = occasion => {  
  const outcomes = JSON.parse(occasion.knowledge);  
  console.log(outcomes);  
}const anotherUrl="[http://127.0.0.1:9090/.well-known/mercure?topic=anotherTopic](http://127.0.0.1:9090/.well-known/mercure?subject=anotherTopic`)";  
const anotherEventSource = new EventSource(anotherUrl);anotherEventSource.onmessage = occasion => {  
  const outcomes = JSON.parse(occasion.knowledge);  
  console.log(outcomes);  
}
Enter fullscreen mode

Exit fullscreen mode

You’ll be able to subscribe to many matters on this approach as a result of the server will ship knowledge to a selected subject.

You should use this method to create a personal subject for a single person, or group of customers to ship occasions solely to them.

You’ll be able to write your implementation to dispatch asynchronous occasions, however this isn’t really useful as a result of Mercure already sends them asynchronously.

However if you would like, you should utilize Symfony Messenger to dispatch it by your self.

Let’s see how the earlier instance might be with your individual implementation:

public operate ship(Request $request, MessageBusInterface $customBus): void  
{  
   $replace = new Replace(  
      'channelname',  
      json_encode([  
          'foo' => 'bar'  
      ], JSON_THROW_ON_ERROR)  
   );  
   $customBus->publish($replace);  
}
Enter fullscreen mode

Exit fullscreen mode

As you may see the code doesn’t change, you should utilize a distinct implementation.

However keep in mind that Mercure already sends occasions asynchronously so it depends upon you if you wish to keep by your self the dispatching system.

There are numerous examples of utilizing the Server-Despatched occasions method.

Think about that you could construct a inventory choice consumer, the frontend utility can subscribe to matters to obtain knowledge from the server solely when a inventory quote modifications its worth.

Or if you could construct a Twitter feed replace, the server will ship knowledge when to each person (with a customized subject every) with new tweets.

Not too long ago I constructed a push notification system the place purchasers obtain info and updates from the server just like the frequent push notifications app in your telephone.

There are loads of helpful assets and examples, in my view utilizing symfony/mercure library has simplified my life as a result of in some minutes you may create a system able to ship occasions to the consumer with out loads of configurations and code to do.

Utilizing the proper device might be essential to the success of the mission, however to comprehend it you don’t have to start out from the implementation.

Begin understanding what’s the actual downside to resolve, I often do an EventStorming earlier than coding as a result of we have to know the area and enterprise logic.

After that, you may perceive higher if Server-Despatched Occasions is the proper method or not.

Add a Comment

Your email address will not be published. Required fields are marked *