Chat server with PHP and websockets
This example mount a multichannel chat server using Sandstone topic creation feature.
Server script
You will first need to create the topic class.
This class must extends Eole\Sandstone\Websocket\Topic
,
and then implements these methods:
onSubscribe
: called when someone join this topiconPublish
: called when someone publish a message to this topiconUnSubscribe
: called when someone unsubscribes
The logic behing broadcasting messages to all subscribing client of a topic is a logic that differs depending on what you want to implement.
In our case, the logic would be:
onSubscribe
: I want to receive message from this topiconPublish
: I send a message on this topiconUnSubscribe
: I don’t want to receive messages from this topic anymore
In other hand, the Eole\Sandstone\Websocket\Topic
class provides a method
to broadcast a message to every subscribing client, example:
$this->broadcast([
'type' => 'message',
'message' => $event,
]);
Then let’s create the ChatTopic
class:
ChatTopic.php
class ChatTopic extends Eole\Sandstone\Websocket\Topic
{
/**
* Broadcast message to each subscribing client.
*
* {@InheritDoc}
*/
public function onPublish(Ratchet\Wamp\WampConnection $conn, $topic, $event)
{
$this->broadcast([
'type' => 'message',
'message' => $event,
]);
}
/**
* Notify all subscribing clients that a new client has subscribed to this channel.
*
* {@InheritDoc}
*/
public function onSubscribe(Ratchet\Wamp\WampConnection $conn, $topic)
{
parent::onSubscribe($conn, $topic);
$this->broadcast([
'type' => 'join',
'message' => 'Someone has joined this channel.',
]);
}
/**
* Notify all subscribing clients that a new client has unsubscribed from this channel.
*
* {@InheritDoc}
*/
public function onUnSubscribe(Ratchet\Wamp\WampConnection $conn, $topic)
{
parent::onUnSubscribe($conn, $topic);
$this->broadcast([
'type' => 'leave',
'message' => 'Someone has left this channel.',
]);
}
}
This class will be used for a chat topic, i.e general
or technical
.
To create one topic, this could do the job:
$app->topic('chat/general', function ($topicPattern) {
return new ChatTopic($topicPattern);
});
What will happens here:
When a client subscribes to chat/general
topic,
Sandstone will call your callback to get a new instance of your ChatTopic
,
and provides the string "chat/general"
to your instance.
This same instance will be used for each future client subscribing to this topic.
In the same way, a message Someone has joined this channel.
will be broadcasted
to all subscribing clients on new subscriptions.
When someone send a message, it is just broadcasted to every subscribing clients.
But we don’t want to only one channel, we want to allow to create dynamic channels:
$app->topic('chat/{channel}', function ($topicPattern) {
return new ChatTopic($topicPattern);
});
This works the same way as Silex routes.
Note: See more at Topic route parameters to know how to retrieve topic route arguments.
So let’s create the server script:
chat-server.php
require_once 'vendor/autoload.php';
require_once 'ChatTopic.php';
// Instanciate Sandstone
$app = new Eole\Sandstone\Application();
// Sandstone requires JMS serializer
$app->register(new Eole\Sandstone\Serializer\ServiceProvider());
// Register and configure your websocket server
$app->register(new Eole\Sandstone\Websocket\ServiceProvider(), [
'sandstone.websocket.server' => [
'bind' => '0.0.0.0',
'port' => '25569',
],
]);
// Add a route `chat/{channel}` and its Topic factory (works same as mounting API endpoints)
$app->topic('chat/{channel}', function ($topicPattern) {
return new ChatTopic($topicPattern);
});
// Encapsulate your application and run websocket server
$websocketServer = new Eole\Sandstone\Websocket\Server($app);
$websocketServer->run();
Now the chat server works.
How to test it with javascript
There is a Javascript library, Autobahn|JS, which provides an implementation of the WAMP protocol.
Note: Be careful to use the 0.8.x version of the library in order to work with WAMP v1.
Following the documentation, we can use our chat with:
front-end.html
<html>
<body>
<script src="http://autobahn.s3.amazonaws.com/js/autobahn.min.js"></script>
<script>
/**
* Called when connected to chat server.
*
* @param {wampSession} session Object provided by autobahn to subscribe/publish/unsubscribe.
*/
function onSessionOpen(session) {
// Subscribe to 'chat/general' topic
session.subscribe('chat/general', function (topic, event) {
console.log('message received', topic, event);
});
// Publish a message to 'chat/general' topic
session.publish('chat/general', 'Hello friend !');
}
/**
* Called on error.
*
* @param {Integer} code
* @param {String} reason
* @param {String} detail
*/
function onError(code, reason, detail) {
console.warn('error', code, reason, detail);
}
// Connect to chat server
ab.connect('ws://localhost:25569', onSessionOpen, onError);
</script>
</body>
</html>
Then, run your chat server:
php chat-server.php
And go to this page. You should receive chat messages in your Javascript console.