Tidak terasa sudah lebih dari 2 bulan tidak menulis akhirnya diberi kesempatan lagi untuk berkarya. Pada artikel kali ini codedoct akan membagikan cara membuat sebuah chat engine menggunakan websocket dengan teknologi ratchet yang akan dikombinasikan dengan laravel.
Sebelum memulai codedoct akan memberitahu teknologi apa saja yang akan digunakan secara detail,
- Laravel versi 5.4
- PHP versi 5.6
- Ratchet cboden versi 0.4
$ composer require cboden/ratchet
Selanjutnya, buat file command laravel untuk dijalankan di server dengan cara,
$ php artisan make:command WebSocketServer --command=websocket:init
Syntax di atas akan secara otomatis membuat file baru dengan nama WebSocketServer.php pada path app/Console/Commands/ edit file tersebut sehingga akan tampak seperti ini,
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use React\EventLoop\Factory;
use App\Http\Controllers\WebSocketController;
class WebSocketServer extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'websocket:init';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info("start server");
$server = IoServer::factory(
new HttpServer(
new WsServer(
new WebSocketController()
)
),
8080
);
$server->run();
}
}
Terlihat pada code diatas kita menambahkan file WebSocketController.php terserah kalian meletakkannya dipath mana saja, dalam hal ini codedoct meletakkan pada path app\Http\Controllers\, silahkan edit file terbut sehingga akan tampak seperti ini,
<?php
namespace App\Http\Controllers\Thirdparty;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
/**
* @author Mihawk | codedoct.com
*/
class WebSocketController implements MessageComponentInterface
{
protected $clients;
private $users;
private $userresources;
public function __construct()
{
$this->clients = new \SplObjectStorage;
$this->users = [];
$this->userresources = [];
}
/**
* [onOpen description]
* @method onOpen
* @param ConnectionInterface $conn [description]
* @return [JSON] [description]
* @example connection var conn = new WebSocket('ws://localhost:8090');
*/
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
$this->users[$conn->resourceId] = $conn;
}
/**
* [onMessage description]
* @method onMessage
* @param ConnectionInterface $conn [description]
* @param [JSON.stringify] $msg [description]
* @return [JSON] [description]
* @example sendAll conn.send(JSON.stringify({command: "sendAll", data: {message:"halo global"}, from: "3"}));
* @example sendConnection conn.send(JSON.stringify({command: "sendConnection", data: "halo kampret - kampret", to: [1,2]}));
* @example message conn.send(JSON.stringify({command: "message", to: "1", from: "9", message: "it needs xss protection"}));
* @example register conn.send(JSON.stringify({command: "register", userId: 9}));
*/
public function onMessage(ConnectionInterface $conn, $msg)
{
$data = json_decode($msg);
if (isset($data->command)) {
switch ($data->command) {
case "sendAll":
if ($data->from) {
foreach ($this->userresources as $key => $resources) {
if ($key != $data->from) {
foreach ($resources as $resourceId) { //setiap user ID bisa buka di banyak browser
if (isset($this->users[$resourceId])) {
$this->users[$resourceId]->send(json_encode($data->data));
}
}
}
}
}
break;
case "sendConnection":
if (count($data->to)>0) {
foreach ($this->userresources as $key => $resources) {
if (in_array($key, $data->to)) {
foreach ($resources as $resourceId) { //setiap user ID bisa buka di banyak browser
if (isset($this->users[$resourceId])) {
$this->users[$resourceId]->send(json_encode($data->data));
}
}
}
}
}
break;
case "message":
if ($data->from && $data->to) {
if ( isset($this->userresources[$data->to]) ) {
foreach ($this->userresources[$data->to] as $key => $resourceId) {
if ( isset($this->users[$resourceId]) ) {
$this->users[$resourceId]->send(json_encode($data->data));
}
}
}
if (isset($this->userresources[$data->from])) {
foreach ($this->userresources[$data->from] as $key => $resourceId) {
if ( isset($this->users[$resourceId]) && $conn->resourceId != $resourceId ) { //jika buka di browser yg berbeda
$this->users[$resourceId]->send(json_encode($data->data));
}
}
}
}
break;
case "register":
if (isset($data->userId)) {
if (isset($this->userresources[$data->userId])) {
if (!in_array($conn->resourceId, $this->userresources[$data->userId]))
{
$this->userresources[$data->userId][] = $conn->resourceId;
}
}else{
$this->userresources[$data->userId] = [];
$this->userresources[$data->userId][] = $conn->resourceId;
}
}
// $conn->send(json_encode($this->users));
$conn->send(json_encode($this->userresources));
break;
default:
$example = array(
'methods' => [
"send to all" => '{command: "sendAll", data: {message:"halo global"}, from: "3"}',
"send to group" => '{command: "sendConnection", data: "halo kampret - kampret", to: [1,2]}',
"message" => '{command: "message", to: "1", from: "2", data: {message:"halo gan"}',
"register" => '{command: "register", userId: 9}',
],
);
$conn->send(json_encode($example));
break;
}
}
}
public function onClose(ConnectionInterface $conn)
{
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
unset($this->users[$conn->resourceId]);
foreach ($this->userresources as &$userId) {
foreach ($userId as $key => $resourceId) {
if ($resourceId==$conn->resourceId) {
unset( $userId[ $key ] );
}
}
}
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
Code websocket server diatas akan kita panggil melalui command php artisan untuk itu kita harus menambahkannya pada kernel laravel, yang terletak pada path app/Console/ dengan nama file Kernel.php edit bagian command menjadi seperti ini,
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
Commands\WebSocketServer::class,
];
Oke jika semua sudah selesai saatnya kita coba run WebSocketServer nya dengan cara,
$ php artisan websocket:init
Sehingga akan tampak seperti ini tampilannya,
===DONE!===