Laravel WebSockets chat
Branch: master
Clone or download
Alex
Latest commit baec682 Mar 9, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
config Added config Oct 20, 2017
src Added direct messaging via connector Oct 23, 2017
LICENSE Initial commit Oct 20, 2017
README.md Additional auth-example Mar 9, 2018
composer.json Upgrading to new version [2.0] of ollyxar/websockets Mar 9, 2018

README.md

Laravel WebSocket chat server

Version Downloads License

logo

Requirements

  • Unix (extension pcntl_fork)
  • PHP 7.1+
  • Laravel 5
  • composer

Installing WebSockets Chat

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,

Configuration

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$#%

Extended Handler class

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);
};

Starting WebSocket Server

php artisan websockets-chat:run

Sending direct message to the server

php artisan websockets-chat:send "Hello from system!"