Pure PHP RFC6455 WebSocket Protocol Implementation Using Ratchet and Workerman

This article presents a pure‑PHP implementation of the RFC6455 WebSocket protocol, detailing its handshake, framing, state management, and security features, and shows how to integrate the Ratchet RFC6455 library with the asynchronous Workerman framework, including installation, full source code, and step‑by‑step testing.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Pure PHP RFC6455 WebSocket Protocol Implementation Using Ratchet and Workerman

Overview

The library implements the RFC 6455 WebSocket protocol as a framework‑independent component that handles server‑side and client‑side handshakes, message framing, state management, and security extensions such as masking, per‑message deflate, and sub‑protocol negotiation. Ambiguities defined in the RFC remain unresolved in the library, leaving their handling to the concrete implementation.

Installation

composer require workerman/workerman ratchet/rfc6455 guzzlehttp/psr7

Full Implementation (start.php)

<?php
/**
 * @desc WebSocket service – based on Ratchet RFC6455 + Workerman
 */
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Ratchet\RFC6455\Handshake\ServerNegotiator;
use Ratchet\RFC6455\Handshake\RequestVerifier;
use Ratchet\RFC6455\Messaging\MessageBuffer;
use Ratchet\RFC6455\Messaging\Message;
use Ratchet\RFC6455\Messaging\Frame;
use Ratchet\RFC6455\Messaging\CloseFrameChecker;
use GuzzleHttp\Psr7\HttpFactory;

class WebSocketRFC6455 {
    public static function input($buffer, $connection): int {
        if (!isset($connection->handshaked)) {
            return str_contains($buffer, "

") ? strlen($buffer) : 0;
        }
        // After handshake, consume everything; MessageBuffer handles framing.
        return strlen($buffer);
    }

    public static function decode($buffer, $connection): string {
        if (!isset($connection->handshaked)) {
            return self::handleHandshake($buffer, $connection);
        }
        if (!isset($connection->messageQueue)) {
            $connection->messageQueue = [];
        }
        $connection->messageBuffer->onData($buffer);
        return array_shift($connection->messageQueue) ?? '';
    }

    public static function encode($data, $connection): string {
        if (!isset($connection->handshaked)) {
            return $data; // Pass‑through 101 response during handshake.
        }
        $frame = new Frame($data, true, Frame::OP_TEXT);
        return $frame->getContents();
    }

    private static function handleHandshake($buffer, $connection): string {
        $request = \GuzzleHttp\Psr7\Message::parseRequest($buffer);
        $factory = new HttpFactory();
        $verifier = new RequestVerifier();
        try {
            $negotiator = new ServerNegotiator($verifier, $factory);
            $response = $negotiator->handshake($request);
            $connection->messageQueue = [];
            $connection->messageBuffer = new MessageBuffer(
                new CloseFrameChecker(),
                function (Message $msg, MessageBuffer $buffer) use ($connection) {
                    $connection->messageQueue[] = $msg->getPayload();
                },
                null, false, null, null, null, null
            );
            $connection->send(\GuzzleHttp\Psr7\Message::toString($response));
            $connection->handshaked = true;
            echo "WebSocket handshake succeeded: {$connection->getRemoteIp()}
";
            return '';
        } catch (\Exception $e) {
            echo "Handshake failed: " . $e->getMessage() . "
";
            $connection->close();
            return '';
        }
    }
}

$worker = new Worker('tcp://0.0.0.0:8486');
$worker->count = 4;
$worker->protocol = WebSocketRFC6455::class;

$worker->onMessage = function (TcpConnection $connection, $data) {
    echo "Received message: [$data]
";
    $connection->send("Server reply: Received your message - $data");
};

$worker->onClose = function (TcpConnection $connection) {
    echo "Connection closed: {$connection->getRemoteIp()}
";
};

Worker::runAll();

Running and Testing

Step 1 – Start the server php start.php start Step 2 – Browser test

var ws = new WebSocket('ws://127.0.0.1:8486');
ws.onmessage = function(event) { console.log('Received: ' + event.data); };
ws.send('Hello from Open Source Tech Stack!');

Step 3 – Debug output

Connection succeeded: WebSocket handshake succeeded: 172.18.0.1 Message received: Received message: [Open Source Tech Stack] Server reply:

Server reply: Received your message - Open Source Tech Stack

Core Design Analysis

Handshake Phase decode() returns an empty string; Workerman directly sends the 101 response.

After a successful handshake, a MessageBuffer (the core Ratchet component) is created to handle subsequent frames.

Message Processing input() lets Workerman consume complete packets. decode() forwards the raw buffer to $connection->messageBuffer->onData($buffer), which internally manages fragmentation, masking, and Ping/Pong frames.

The onMessage callback automatically extracts payloads via getPayload() from the message queue.

Encoding encode() uses Ratchet's Frame class to wrap data into a WebSocket text frame.

During the handshake phase, the HTTP response is passed through unchanged.

Conclusion

Ratchet RFC6455 combined with Workerman provides an elegant, I/O‑agnostic WebSocket protocol implementation for PHP. It cleanly separates protocol logic from the transport layer, allowing developers to focus on business logic. For simple use‑cases such as chat rooms or real‑time dashboards, the built‑in Workerman websocket:// protocol (≈30 lines) may be sufficient and offers a more straightforward alternative.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AsynchronousWebSocketprotocolworkermanratchetrfc6455
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.