websocket.hphp
A single-file RFC 6455 WebSocket server, written in HolyPHP itself. Real SHA-1 handshake, text/binary frames, fragmentation, ping/pong, close handshake, and non-blocking I/O so one slow client never blocks the others.
Minimal server
import "websocket.hphp";
$server = new WebSocketServer("tcp://0.0.0.0:9001");
$server->onMessage = function(int $client, string $msg): void {
$server->broadcast($msg); // chat-style echo
};
$server->run(); // blocking event loop
Connect from any browser:
const ws = new WebSocket("ws://localhost:9001");
ws.onmessage = (e) => console.log(e.data);
ws.send("hello");
Construction
| Property | Type | Meaning |
|---|---|---|
$addr | string | bind address, tcp://host:port |
$verbose | bool | log lifecycle + frames to stdout |
$onOpen | closure | fn(int $client) |
$onMessage | closure | fn(int $client, string $message) — text frames |
$onBinary | closure | fn(int $client, string $bytes) — binary frames |
$onClose | closure | fn(int $client, int $code) |
Client ids are small integers starting at 1, stable for the connection's lifetime.
Methods
Event-driven chat server (full example)
import "websocket.hphp";
$server = new WebSocketServer("tcp://127.0.0.1:9500");
$server->verbose = true;
if (!$server->listen()) { exit(1); }
$server->onOpen = function(int $client) use ($server): void {
$server->broadcast("user " . $client . " joined the chat");
};
$server->onMessage = function(int $client, string $msg) use ($server): void {
$server->broadcast("user " . $client . ": " . $msg);
};
$server->onClose = function(int $client, int $code) use ($server): void {
$server->broadcast("user " . $client . " left the chat");
};
while ($server->tick()) {
usleep(10000); // 10 ms loop; the library multiplexes I/O
}
Integrate into your own loop
Don't want the blocking run()? Call tick() from your own loop —
it accepts pending connections and drains readable sockets, then returns.
while ($server->tick(10)) { // your game/render loop here
do_frame();
}
Protocol details handled for you
| Feature | Status |
|---|---|
| HTTP upgrade handshake (Sec-WebSocket-Accept, real SHA-1 + base64) | ✓ |
| Masked client frames (unmasked in server direction) | ✓ |
| Fragmented messages (continuation frames, opcode 0) | ✓ |
| Text (1) / binary (2) opcodes | ✓ |
| Ping/pong keepalive (opcodes 9/10) | ✓ |
| Close handshake with status codes (opcode 8) | ✓ |
| 64-bit extended payload lengths | ✓ |
| One slow client never blocks others (poll-based) | ✓ |
Lower-level pieces (exported)
The library also exposes its frame codec if you want to build something exotic:
ws_decode returns
["fin" => bool, "opcode" => int, "payload" => string, "consumed" => int].
Browser-side starter
A complete HTML client ships at examples/ws_chat.html — dark themed chat UI,
auto-reconnect, Enter to send. Open it directly from disk; no web server needed.
hphp build examples/ws_chat.hphp -o wschat.exe
./wschat.exe
# then open examples/ws_chat.html in Chrome