Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
alcalyn committed Sep 19, 2017
2 parents caff19b + 89c246e commit 2190019
Show file tree
Hide file tree
Showing 14 changed files with 535 additions and 9 deletions.
25 changes: 17 additions & 8 deletions .travis.yml
@@ -1,14 +1,23 @@
language: php
sudo: required

php:
- 5.5
- 5.6
- 7.0
services:
- docker

matrix:
include:
- env: MODE=php
php: 5.5
- env: MODE=php
php: 7.1
- env: MODE=docker

before_script:
- echo "extension=zmq.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
- composer update
- if [ "$MODE" = "php" ]; then yes '' | sudo apt-get install libzmq-dev; fi
- if [ "$MODE" = "php" ]; then yes '' | pecl install -f zmq; fi
- if [ "$MODE" = "php" ]; then composer update; fi

script:
- ./vendor/bin/phpcs --standard=phpcs.xml src
- ./vendor/bin/phpunit -c .
- if [ "$MODE" = "php" ]; then ./vendor/bin/phpcs --standard=phpcs.xml src; fi
- if [ "$MODE" = "php" ]; then ./vendor/bin/phpunit -c .; fi
- if [ "$MODE" = "docker" ]; then make; fi
8 changes: 8 additions & 0 deletions doc/_includes/nav-left.html
Expand Up @@ -24,6 +24,14 @@
<span>Websocket</span>
</a>
</li>
<li {% include menu-active.html url='/websocket-events.html' %}>
<a href="{{ site.baseurl }}/websocket-events.html">
<i class="fa fa-wifi" aria-hidden="true"></i>
Listen websocket events
<br>
<span>onOpen, onClose, onPublish...</span>
</a>
</li>
<li {% include menu-active.html url='/push-debug-profiler.html' %}>
<a href="{{ site.baseurl }}/push-debug-profiler.html">
<i class="fa fa-list-alt" aria-hidden="true"></i>
Expand Down
15 changes: 15 additions & 0 deletions doc/css/all.css
Expand Up @@ -66,3 +66,18 @@ footer {
footer i:before {
color: #3e3f3a;
}

table{
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #ccc;
}

th, td {
border: 1px solid #ccc;
padding: 0.33em;
}

th{
background-color: #f5f2f0;
}
Binary file added doc/img/events-dcm.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/img/events-dcm.xml
@@ -0,0 +1 @@
<mxfile userAgent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.0" version="7.4.0" editor="www.draw.io"><diagram id="8200411e-a872-013f-98f6-5e0b79646c1c" name="Page-1">7Vpbb5swFP41vE4xlyZ5bGi6Teqkat20Zwcc8GowMqZJ9ut3DAYKJlG60a2pqKIGHx8b+3zfuRhiOX6y/yhwFn/hIWGWPQv3lnNj2TZynRl8KcmhksxdtxJEgoZaqRU80F9EC/W4qKAhyTuKknMmadYVBjxNSSA7MiwE33XVtpx175rhiBiChwAzU/qDhjKupAtv1so/ERrF9Z3RTPdscPAYCV6k+n6W7WzLv6o7wfVcWj+Pcch3z0TO2nJ8wbmsrpK9T5iybW22atztkd5m3YKk8pwBdjXgCbNCb92vTEp5un5Sk1TLlIfaNPmOJgyn0FpteSofdM8M2kFMWXiHD7xQ984l2KJurWIu6C/Qxwy6EAigW0iNvL1Us1HGfM64AEHKyxu0gx7UZPo2guQw7L7eI+qJvuB9R/EO57JeIGcMZzndlEtWAxMsIpquuJQ80Ur1Bm+fraeF0VkxvCFs1eA8pKIMc4sTyhT9PxH2RCQNcG2xas9oAW1tfSIk2R9FEDW8AH8jPCFSHEBFD3BqUh5q51tW7V1LXLTQOvEz0nqe9hftK1EzdcsXuNCUGaaPY9BHeSRILOca/rdc+pxKIrY4IAafYOeypIPgj6SH/wAlMKNRCk1GtmqYMh2Yll1rseSZmizDAU2ju1Lnxm0lX/X2lYjD2C0rnS+mYUhSxRguscSbhtEZp6ks7eOt4ANW9GcfPMuDhfvQRm0bPkpdSNgz7AXTEj0C3NsRxb/zoLaHodbQ2mcia1/9PbL23ECKhBAjdRM2GvOIp5itW+mqdAgSauPFMqmd/SeR8qCJjwvJlfmbGe64Aq3Ue6HjqBWddhvYAC9EoLW0XSDyROS0xQVhWNKn7uxD9iyHXguBD88UNG3ame+VoPVZtPA6Pmt7vSjd02/y6bA+XFQraJFttnIW2FeGG/8gm5wHj0ReFzIGkwIKU0q4lJTg9tiyNOOGvXyljDA3qFTkRDQZ4Ts0plxwFsZXL80Fg5iOkQuQ8w5zAarPFSMmg7MNisyAi5Nsiq6XEF37BfdQeH21ghuZBzYIczRoAizEG4ht1tz/VomnCHsC5yNA/4dyG129xxDrvpF6e45O1s9mvd138ZHr7doww49dhOBiSgWXkAq8Ze8cN1Rpo9dKBZ5BIqKo06SC9T4gmWLUlAVOQuy+uNAegnSUhy72e8wCczMLHMu841fa5nn0vtgwmsdThL2ECNsvtl3HPq8oQ7P5COxZmCG2ok232la+7+xxs+Up1B7Bev52Cu6lCe0+YEVIGnBxVWVOgP4xoHWZ809eWMxMQMHIZTScEB0L0QX6h4i+xzNx/cp01GpolJdQzvJlL6F6+n99KB54mfz13p/KtEso03rnYNdxzwsTixFKfNt8mAJd0/PQP/j1gfNmyjPbfLgBK8VJPuXy0fB8xeoMmu1v2Kpc0P5Q0Fn/Bg==</diagram></mxfile>
160 changes: 160 additions & 0 deletions doc/websocket-events.md
@@ -0,0 +1,160 @@
---
layout: page
title: Listen to websocket events
---

<h1 class="no-margin-top">Listen to websocket events</h1>

At some point of your running websocket server,
clients will connect and disconnect, and subscribe to a wamp topic to publish messages.

You may need to do something when one of these event happens.

Sandstone allows you to listen an event through the Silex event dispatcher.


## Which event I can listen to

All these events are dispatched:

| Event | Event instance |
|-------|-----------------|
| `ConnectionEvent::ON_OPEN` | [`ConnectionEvent`](https://github.com/eole-io/sandstone/blob/master/src/Websocket/Event/ConnectionEvent.php) |
| `ConnectionEvent::ON_CLOSE` | [`ConnectionEvent`](https://github.com/eole-io/sandstone/blob/master/src/Websocket/Event/ConnectionEvent.php) |
| `ConnectionEvent::ON_AUTHENTICATION` | [`WebsocketAuthenticationEvent`](https://github.com/eole-io/sandstone/blob/master/src/Websocket/Event/WebsocketAuthenticationEvent.php) |
| `ConnectionEvent::ON_ERROR` | [`ConnectionErrorEvent`](https://github.com/eole-io/sandstone/blob/master/src/Websocket/Event/ConnectionErrorEvent.php) |
| `ConnectionEvent::ON_SUBSCRIBE` | [`WampEvent`](https://github.com/eole-io/sandstone/blob/master/src/Websocket/Event/WampEvent.php) |
| `ConnectionEvent::ON_UNSUBSCRIBE` | [`WampEvent`](https://github.com/eole-io/sandstone/blob/master/src/Websocket/Event/WampEvent.php) |
| `ConnectionEvent::ON_PUBLISH` | [`PublishEvent`](https://github.com/eole-io/sandstone/blob/master/src/Websocket/Event/PublishEvent.php) |
| `ConnectionEvent::ON_RPC` | [`RPCEvent`](https://github.com/eole-io/sandstone/blob/master/src/Websocket/Event/RPCEvent.php) |

### Details of events

| Event | When is it called | What can I do with it |
|-------|-------------------|-----------------------|
| `ON_OPEN` | Someone connects to websocket server | Retrieve some connection data with `$event->getConn()->Websocket` |
| `ON_CLOSE` | Someone closed websocket connection | *same as below* |
| `ON_AUTHENTICATION` | Someone connected to websocket server and has been authenticated | Retrieve authenticated user with `$event->getUser()` (returns Symfony `UserInterface`) |
| `ON_ERROR` | An error has been triggered | Retrieve the raised exception with `$event->getError()` (Returns an `\Exception`) |
| `ON_SUBSCRIBE` | Someone subscribed to a topic | Know which topic has been subscribed with `$event->getTopic()` |
| `ON_UNSUBSCRIBE` | Someone unsubscribed to a topic | *same with unsubscription* |
| `ON_PUBLISH` | Someone published a message to a topic | Listen to published messages on topics with `$event->getEvent()` to get message, `$event->getTopic()` to get topic |
| `ON_RPC` | Remote procedure call | Retrieve remote procedure call ID and parameters with `$event->getId()` and `$event->getParams()` |


## Listen to websocket events

I want to do something when someone connects to websocket server.

- First, create the listener

``` php
<?php

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Eole\Sandstone\Websocket\Event\ConnectionEvent;

class MyWebsocketListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
ConnectionEvent::ON_OPEN => 'onOpen',
];
}

public function onOpen(ConnectionEvent $event)
{
// do something when someone connects

// retrieve some connection data with $event->getConn()->Websocket;
// which returns a ConnectionInterface from RatchetPHP
}
}
```

- Then, register this listener as a Silex `EventListenerProviderInterface`

``` php
<?php

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Pimple\Container;
use Silex\Api\EventListenerProviderInterface;

class MyProvider implements EventListenerProviderInterface
{
public function subscribe(Container $app, EventDispatcherInterface $dispatcher)
{
$dispatcher->addSubscriber(new MyWebsocketListener());
}
}
```


## Another example: listen to published messages

I want to display (for debugging purpose) all published messages in any topic.

I have to listen to the `ConnectionEvent::ON_PUBLISH` event:

- First, create the listener

``` php
<?php

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Eole\Sandstone\Websocket\Event\ConnectionEvent;
use Eole\Sandstone\Websocket\Event\PublishEvent;

class MyMessagePublishListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
ConnectionEvent::ON_PUBLISH => 'onMessagePublish',
];
}

public function onMessagePublish(PublishEvent $event)
{
$message = $event->getEvent();
$topic = $event->getTopic();

echo "'$message' has been published in '$topic'", PHP_EOL;
}
}
```

- Then, register this listener

``` php
<?php

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Pimple\Container;
use Silex\Api\EventListenerProviderInterface;

class MyProvider implements EventListenerProviderInterface
{
public function subscribe(Container $app, EventDispatcherInterface $dispatcher)
{
$dispatcher->addSubscriber(new MyMessagePublishListener());
}
}
```

It should display in your websocket logs something like:

`'Hello world' has been published in 'general/chat'`


## Event class diagram

Here is the events diagram, all attributes are inherited:

<img
src="img/events-dcm.png"
alt="Sandstone websocket events DCM"
class="img-responsive img-thumbnail"
/>
2 changes: 1 addition & 1 deletion docker/php-fpm/Dockerfile
@@ -1,7 +1,7 @@
FROM phpdockerio/php71-fpm

RUN apt-get update \
&& apt-get -y --no-install-recommends install php7.1-mysql php-zmq \
&& apt-get -y --no-install-recommends install php7.1-mysql php-zmq git \
&& apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*

WORKDIR "/var/www/html"
32 changes: 32 additions & 0 deletions src/Websocket/Application.php
Expand Up @@ -10,6 +10,12 @@
use Ratchet\Wamp\WampServerInterface;
use Eole\Sandstone\Logger\EchoLogger;
use Eole\Sandstone\OAuth2\Security\Authentication\Token\OAuth2Token;
use Eole\Sandstone\Websocket\Event\ConnectionEvent;
use Eole\Sandstone\Websocket\Event\WebsocketAuthenticationEvent;
use Eole\Sandstone\Websocket\Event\ConnectionErrorEvent;
use Eole\Sandstone\Websocket\Event\WampEvent;
use Eole\Sandstone\Websocket\Event\PublishEvent;
use Eole\Sandstone\Websocket\Event\RPCEvent;
use Eole\Sandstone\Application as SandstoneApplication;

final class Application implements WampServerInterface
Expand Down Expand Up @@ -69,6 +75,8 @@ private function authenticateUser(ConnectionInterface $conn)
*/
public function onOpen(ConnectionInterface $conn)
{
$this->dispatch(ConnectionEvent::ON_OPEN, new ConnectionEvent($conn));

$this->logger->info('Connection event', ['event' => 'open']);
$this->logger->info('Authentication...');

Expand All @@ -77,6 +85,7 @@ public function onOpen(ConnectionInterface $conn)
if (null === $user) {
$this->logger->info('Anonymous connection');
} else {
$this->dispatch(ConnectionEvent::ON_AUTHENTICATION, new WebsocketAuthenticationEvent($conn, $user));
$this->logger->info('User logged.', ['username' => $user->getUsername()]);
}
} catch (\Exception $e) {
Expand Down Expand Up @@ -137,6 +146,8 @@ private function loadTopic($topicPath)
*/
public function onSubscribe(ConnectionInterface $conn, $topic)
{
$this->dispatch(ConnectionEvent::ON_SUBSCRIBE, new WampEvent($conn, $topic));

$this->logger->info('Topic event', ['event' => 'subscribe', 'topic' => $topic]);

$this->getTopic($topic)->onSubscribe($conn, $topic);
Expand All @@ -147,6 +158,8 @@ public function onSubscribe(ConnectionInterface $conn, $topic)
*/
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible)
{
$this->dispatch(ConnectionEvent::ON_PUBLISH, new PublishEvent($conn, $topic, $event, $exclude, $eligible));

$this->logger->info('Topic event', ['event' => 'publish', 'topic' => $topic]);

$this->getTopic($topic)->onPublish($conn, $topic, $event);
Expand All @@ -157,6 +170,8 @@ public function onPublish(ConnectionInterface $conn, $topic, $event, array $excl
*/
public function onUnSubscribe(ConnectionInterface $conn, $topic)
{
$this->dispatch(ConnectionEvent::ON_UNSUBSCRIBE, new WampEvent($conn, $topic));

$this->logger->info('Topic event', ['event' => 'unsubscribe', 'topic' => $topic]);

$this->getTopic($topic)->onUnSubscribe($conn, $topic);
Expand All @@ -167,6 +182,8 @@ public function onUnSubscribe(ConnectionInterface $conn, $topic)
*/
public function onClose(ConnectionInterface $conn)
{
$this->dispatch(ConnectionEvent::ON_CLOSE, new ConnectionEvent($conn));

$this->logger->info('Connection event', ['event' => 'close']);

foreach ($this->topics as $topic) {
Expand All @@ -181,6 +198,8 @@ public function onClose(ConnectionInterface $conn)
*/
public function onCall(ConnectionInterface $conn, $id, $topic, array $params)
{
$this->dispatch(ConnectionEvent::ON_RPC, new RPCEvent($conn, $topic, $id, $params));

$this->logger->info('Topic event', ['event' => 'call', 'topic' => $topic]);
}

Expand All @@ -189,6 +208,19 @@ public function onCall(ConnectionInterface $conn, $id, $topic, array $params)
*/
public function onError(ConnectionInterface $conn, \Exception $e)
{
$this->dispatch(ConnectionEvent::ON_ERROR, new ConnectionErrorEvent($conn, $e));

$this->logger->info('Connection event', ['event' => 'error', 'message' => $e->getMessage()]);
}

/**
* Dispatch a ConnectionEvent to SandstoneApplication dispatcher.
*
* @param string $eventName
* @param ConnectionEvent $event
*/
private function dispatch($eventName, ConnectionEvent $event)
{
$this->sandstoneApplication['dispatcher']->dispatch($eventName, $event);
}
}
32 changes: 32 additions & 0 deletions src/Websocket/Event/ConnectionErrorEvent.php
@@ -0,0 +1,32 @@
<?php

namespace Eole\Sandstone\Websocket\Event;

use Ratchet\ConnectionInterface;

class ConnectionErrorEvent extends ConnectionEvent
{
/**
* @var \Exception
*/
private $error;

/**
* @param ConnectionInterface $conn
* @param \Exception $error
*/
public function __construct(ConnectionInterface $conn, \Exception $error)
{
parent::__construct($conn);

$this->error = $error;
}

/**
* @return ConnectionInterface
*/
public function getError()
{
return $this->error;
}
}

0 comments on commit 2190019

Please sign in to comment.