- Unix (extension pcntl_fork)
- PHP 7.1+
- Laravel 5
- composer
The recommended way to install WebSockets is through Composer.
# Install Composer
curl -sS https://getcomposer.org/installer | php
Next, run the Composer command to install the latest stable version of WebSockets:
php composer.phar require ollyxar/websockets-chat
After updating composer, add the service provider to the providers
array in config/app.php
Ollyxar\WSChat\WSChatServiceProvider::class,
You can customize variables bellow by adding config-file: websockets-chat.php
in the config folder:
parameter | description | example |
---|---|---|
handler | Handler Class (extends of Worker) | \App\MyHandler |
host | Host (ip) | 0.0.0.0 |
port | Port | 2083 |
worker_count | Count of forked process | 4 |
use_ssl | Used protocol | false |
cert | PEM certificate | /etc/nginx/conf.d/wss.pem |
pass_phrase | PEM certificate pass phrase | secret$#% |
This is example how to use Handler with User authentication. If you have default configuration and file-session-storage you can use this example.
First you have to install auth-helper:
php composer.phar require ollyxar/laravel-auth
Then create your Handler.php
:
namespace App;
use Generator;
use Ollyxar\LaravelAuth\FileAuth;
// or you can use RedisAuth if you're storing sessions in the Redis-server:
// use Ollyxar\LaravelAuth\RedisAuth;
use Ollyxar\WebSockets\{
Frame,
Handler as Worker,
Dispatcher
};
/**
* Class Handler
* @package App
*/
class Handler extends Worker
{
/**
* Connected users
*
* @var array
*/
protected $users = [];
/**
* Append connected user
*
* @param array $headers
* @param $socket
* @return bool
*/
private function fillUser(array $headers, $socket): bool
{
if ($userId = FileAuth::getUserIdByHeaders($headers)) {
// allow only one connection for worker per user
if (!in_array($userId, $this->users)) {
$this->users[(int)$socket] = $userId;
return true;
}
}
return false;
}
/**
* @param $client
* @return Generator
*/
protected function onConnect($client): Generator
{
$userName = User::where('id', (int)$this->users[(int)$client])->first()->name;
yield Dispatcher::async($this->broadcast(Frame::encode(json_encode([
'type' => 'system',
'message' => $userName . ' connected.'
]))));
}
/**
* @param array $headers
* @param $socket
* @return bool
*/
protected function validateClient(array $headers, $socket): bool
{
return $this->fillUser($headers, $socket);
}
/**
* @param $clientNumber
* @return Generator
*/
protected function onClose($clientNumber): Generator
{
$user = User::where('id', (int)@$this->users[$clientNumber])->first();
$userName = data_get($user, 'name', '[GUEST]');
yield Dispatcher::async($this->broadcast(Frame::encode(json_encode([
'type' => 'system',
'message' => $userName . " disconnected."
]))));
unset($this->users[$clientNumber]);
yield;
}
/**
* @param string $message
* @param int $socketId
* @return Generator
*/
protected function onClientMessage(string $message, int $socketId): Generator
{
$message = json_decode($message);
$userName = User::where('id', (int)$this->users[$socketId])->first()->name;
$userMessage = $message->message;
$response = Frame::encode(json_encode([
'type' => 'usermsg',
'name' => $userName,
'message' => $userMessage
]));
yield Dispatcher::async($this->broadcast($response));
}
}
Then add markup to the front:
<div class="chat-wrapper">
<div class="message-box" id="message-box"></div>
<div class="panel">
<input type="text" name="message" id="message" placeholder="Message"/>
<button id="send-btn" class="button">Send</button>
</div>
</div>
And JS code:
var wsUri = "ws://laravel5.dev:2083",
ws = new WebSocket(wsUri);
ws.onopen = function () {
var el = document.createElement('div');
el.classList.add('system-msg');
el.innerText = 'Connection established';
document.getElementById('message-box').appendChild(el);
};
document.getElementById('message').addEventListener('keydown', function (e) {
if (e.keyCode === 13) {
document.getElementById('send-btn').click();
}
});
document.getElementById('send-btn').addEventListener('click', function () {
var mymessage = document.getElementById('message').value;
if (mymessage === '') {
alert("Enter Some message Please!");
return;
}
var objDiv = document.getElementById("message-box");
objDiv.scrollTop = objDiv.scrollHeight;
var msg = {
message: mymessage
};
ws.send(JSON.stringify(msg));
});
ws.onmessage = function (ev) {
var msg = JSON.parse(ev.data),
type = msg.type,
umsg = msg.message,
uname = msg.name;
var el = document.createElement('div');
if (type === 'usermsg') {
el.innerHTML = '<span class="user-name">' + uname + '</span> : <span class="user-message">' + umsg + '</span>';
document.getElementById('message-box').appendChild(el);
}
if (type === 'system') {
el.classList.add('system-msg');
el.innerText = umsg;
document.getElementById('message-box').appendChild(el);
}
document.getElementById('message').value = '';
var objDiv = document.getElementById('message-box');
objDiv.scrollTop = objDiv.scrollHeight;
};
ws.onerror = function (e) {
var el = document.createElement('div');
el.classList.add('system-error');
el.innerText = 'Error Occurred - ' + e.data;
document.getElementById('message-box').appendChild(el);
};
ws.onclose = function () {
var el = document.createElement('div');
el.classList.add('system-msg');
el.innerText = 'Connection Closed';
document.getElementById('message-box').appendChild(el);
};
php artisan websockets-chat:run
php artisan websockets-chat:send "Hello from system!"