Belajar PHP Mencoba RatchetPHP untuk menangani Websocket

RatchetPHP library dapat membantu kita untuk membuat websocket, realtime aplikasi. Websocket sendiri kadang diperlukan di beberapa kasus aplikasi yang menginginkan data dan flow proses dilakukan secara realtime. Contoh sederhana adalah kita sedang chatting, ada proses yang dilakukan secara realtime.

Kasus ini sebenarnya sudah dikenalkan di salah satu fitur keren dari NodeJS. Dengan NodeJS kita dapat mudah melakukan hal tersebut dengan bahasa Javascript. Namun, mungkin ada beberapa kasus dimana kita tidak bisa menggunakan JS untuk menghandle proses websocket ini dan harus menggunakan PHP karena mungkin semua aplikasi dibagun di atas PHP.

Saya mencoba mengikuti dokumentasi dari RatchetPHP dan mengikuti beberapa tutorial yang ada di internet. Berikut saya coba tulis di blog agar nanti bisa mudah di implementasikan kembali tanpa harus browsing-browsing lagi.

Tahapan-nya:
1. PHP versi 7
Versi PHP yang bisa menjalankan Ratchetphp ini >=5.4.2. Untuk temen-temen yang masih dibawah itu belum bisa menggunakan RatchetPHP ini. Saya sarankan menggunakan PHP Versi 7+
2. Install ZeroMQ. Distributed system message.
Ini seperti RabbitMQ yang gunanya menghantarkan data/message. Karena RatchetPHP ini menggunakan ZeroMQ, saya mengikuti untuk memasang ZeroMQ di ubuntu saya. Panduan Installasi bisa mengikut di link ini: https://gist.github.com/katopz/8b766a5cb0ca96c816658e9407e83d00

#!/usr/bin/bash

# Download zeromq
# Ref http://zeromq.org/intro:get-the-software
wget https://github.com/zeromq/libzmq/releases/download/v4.2.2/zeromq-4.2.2.tar.gz

# Unpack tarball package
tar xvzf zeromq-4.2.2.tar.gz

# Install dependency
sudo apt-get update && \
sudo apt-get install -y libtool pkg-config build-essential autoconf automake uuid-dev

# Create make file
cd zeromq-4.2.2
./configure

# Build and install(root permission only)
sudo make install

# Install zeromq driver on linux
sudo ldconfig

# Check installed
ldconfig -p | grep zmq

# Expected
############################################################
# libzmq.so.5 (libc6,x86-64) => /usr/local/lib/libzmq.so.5
# libzmq.so (libc6,x86-64) => /usr/local/lib/libzmq.so
############################################################


3. Install ext-zmq. extension untuk ZeroMQ.
Extension ini untuk menghubungkan antara PHP dengan ZeroMQ. Temen-temen bisa lihat di link ini https://eole-io.github.io/sandstone-doc/install-zmq-php-linux

Setelah semua di install buat folder berisi composer json seperti berikut ini:

{
    "name": "adiputra/rachetphp-test",
    "description": "rachetphp testing",
    "require": {
        "cboden/ratchet": "^0.4.1",
        "react/zmq": "0.2.*|0.3.*"
    },
    "autoload": {
        "psr-4": {
            "Adiputra\\Rachetphp\\": "src"
        }
    },
    "authors": [
        {
            "name": "Adiputra",
            "email": "tuts.adiputra@gmail.com"
        }
    ]
}

Setelah itu jalankan composer install untuk menginstall beberapa library dari Ratchetphp ini.

Setelah itu kita buat folder src lalu di dalam folder src kita buat folder bin dan file server.php. Isi file server.php seperti berikut ini:

<?php
require dirname(__DIR__) . '../../vendor/autoload.php';

use React\EventLoop\Factory;
use React\ZMQ\Context;

$loop = React\EventLoop\Factory::create();
$broadcast = new Adiputra\Rachetphp\Broadcast;

// Listen for the web server to make a ZeroMQ push after an ajax request
$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555'); // Binding to 127.0.0.1 means the only client that can connect is itself
$pull->on('message', array($broadcast, 'onNewObject'));

// Set up our WebSocket server for clients wanting real-time updates
$webSock = new React\Socket\Server('0.0.0.0:8080', $loop); // Binding to 0.0.0.0 means remotes can connect
$webServer = new Ratchet\Server\IoServer(
    new Ratchet\Http\HttpServer(
        new Ratchet\WebSocket\WsServer(
            $broadcast
        )
    ),
    $webSock
);

$loop->run();

File server.php gunanya untuk membuat server serta melisten atau menerima apakah ada data yang dikirimkan oleh client atau tidak.

Setelah itu kita buat file client yaitu index.html seperti kode dibawah ini:

<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<div id="messages"></div>

<div>
    <form>
        <input type="text" id="msgText">
        <input type="button" id="msgSend" value=">">
    </form>
</div>
<script>
    var conn = new WebSocket('ws://localhost:8080');
    var userId = 51;
    
    conn.onopen = function (e) {
        console.log("Connection established!");
        console.log(e.data);
    };

    conn.onmessage = function (e) {
        console.log(e.data);

        var response = JSON.parse(e.data);
        if(response['user_id'] == userId) {
            var chat = document.createElement("div");
            var cwind = document.getElementById("messages");
            cwind.appendChild(chat);
            cwind.lastElementChild.innerHTML = response["message"];
        }
    };

    var button = document.getElementById("msgSend");

    button.onclick = function () {
        var val = document.getElementById("msgText").value;
        conn.send(JSON.stringify({'user_id': userId, 'message': val}));
    };

</script>
</body>

File index.html ini adalah file client yang nanti akan menerima data dari server ke client atau sebaliknya dari client ke server.

Setelah itu saya buat file Broadcast.php seperti kode dibawah ini:

<?php
namespace Adiputra\Rachetphp;

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Broadcast implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);

        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        $numRecv = count($this->clients) - 1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
            , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');

        foreach ($this->clients as $client) {
            if ($from !== $client) {
                // The sender is not the receiver, send to each client connected
                $client->send($msg);
            }
        }
    }

    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we can no longer send it messages
        $this->clients->detach($conn);

        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";

        $conn->close();
    }

    public function onNewObject($msg)
    {
        echo "on new object fire \n";
        foreach ($this->clients as $client) {
            $client->send($msg);
        }
    }
}

Isi file tersebut adalah membaca beberapa event yang terjadi. Temen-temen yang pernah menggunakan Socket IO pasti memahami beberapa event disini ya. Secara garis besar terdapat event ketika telah terbuka / masuk (on open). Ketika sedang tertutup (on close) dan ketika terjadi pengiriman data (on message). Beberapa method seperti on error untuk menghandle ketika terjadi error pada class Ratchetphp ini. Ke-empat method itu adalah bawaan utama atau default yang saya baca pada documentasi RatchetPHP

Terdapat method tambahan yaitu method onNewObject. Kegunaan method itu untuk mengirimkan kembali ke client data setelah menerima data.

Sampai disini kawan-kawan bisa menjalankan dengan cara:
1. Jalankan server.php
Menjalankan server.php dengan cara mengetikan php src/bin/server.php. Pastikan ketika menjalankan ini tidak memunculkan error.
2. Jalankan index.html di beberapa browser
Untuk menerima dan mengirimkan data dari client ke client lain. Kawan-kawan bisa membuka file index.html di beberapa browser seperti browser firefox dan chromium.

Sampai disini seharusnya sudah jalan dengan baik ya 🙂

Saya coba membuat file baru dengan nama SendMessage.php:

<?php
$userId = 51;

$entryData = array(
    'user_id' => $userId,
    'message' => "Send message for user id: " . $userId
);

$context = new ZMQContext();
$socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'adiputra');
$socket->connect("tcp://localhost:5555");
$socket->send(json_encode($entryData));

File ini gunanya untuk mengakses langsung file PHP lalu mengirimkan data ke socket agar client bisa membaca data yang dikirimkan dengan PHP.

Temen temen bisa mencoba dengan mengetikkan perintah php src/SendMessage.php untuk mencobanya.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: