obray / web-socket-server
PHP based WebSocket server.
Requires
- php: >=8.1
- obray/socket-server: ^1.1
README
Small PHP WebSocket server library built on obray/socket-server.
This package handles the WebSocket upgrade, frame parsing, protocol close responses, and dispatches messages to an application handler. Application behavior such as chat rooms, alert routing, authentication, presence, and connection metadata should live in the handler that implements the library interface.
Install
composer require obray/web-socket-server
For local development with this checkout next to SocketServer, the example in
examples/broadcast.php includes a small fallback autoloader.
Start An Echo Server
<?php require __DIR__ . '/vendor/autoload.php'; $server = (new \obray\WebSocketServer())->create('127.0.0.1', 8080); $server->start();
The default handler echoes text and binary messages, replies to ping frames with pong frames, and closes when the client sends a close frame. It is quiet by default. Pass a logger to see lifecycle messages:
$handler = new \obray\base\WebSocketBaseHandler(function ($message) { error_log($message); }); $server = (new \obray\WebSocketServer())->create('127.0.0.1', 8080, null, $handler); $server->start();
Custom Handler
Implement obray\interfaces\WebSocketServerHandlerInterface or extend
obray\base\WebSocketBaseHandler and override only the callbacks you need.
class AlertsHandler extends \obray\base\WebSocketBaseHandler { private $connections = []; public function onUpgraded($data, \obray\interfaces\SocketConnectionInterface $connection): void { $this->connections[spl_object_hash($connection)] = $connection; } public function onText(string $data, \obray\interfaces\SocketConnectionInterface $connection): void { foreach($this->connections as $client){ if($client->isConnected()){ \obray\WebSocket::sendText($client, $data); } } } public function onDisconnect(\obray\interfaces\SocketConnectionInterface $connection): void { unset($this->connections[spl_object_hash($connection)]); } }
Sending Messages
Handlers can use the helper methods on obray\WebSocket instead of encoding
frames directly:
\obray\WebSocket::sendText($connection, 'hello'); \obray\WebSocket::sendBinary($connection, $bytes); \obray\WebSocket::sendPing($connection); \obray\WebSocket::sendPong($connection); \obray\WebSocket::sendClose($connection); \obray\WebSocket::sendCloseWithCode($connection, 1000, 'done');
If your handler defines onStart(\obray\SocketServer $server), the WebSocket
adapter will call it when the lower socket server starts. This is intentionally
optional so application-specific concerns can stay outside the library.
public function onStart(\obray\SocketServer $server): void { $server->watchTimer(5, 30, function () { // Application-owned recurring work, such as publishing alerts. }); }
Protocol Behavior
- Client frames must be masked.
- Reserved bits are rejected unless future extensions add support.
- Invalid opcodes close only the offending connection.
- Control frames cannot be fragmented and cannot exceed 125 bytes.
- Frame and message payloads are limited to 1 MiB by default.
- Protocol errors close with WebSocket close code
1002. - Oversized payloads close with WebSocket close code
1009.
Configure payload limits before creating or starting the server:
$webSocketServer = (new \obray\WebSocketServer()) ->setMaximumFramePayloadLength(262144) ->setMaximumMessageLength(524288); $server = $webSocketServer->create('127.0.0.1', 8080, null, $handler); $server->start();
The smoke test exercises handshake, echo, ping/pong, partial reads, fragmented messages, clean close, unmasked frames, invalid opcodes, and oversized frames:
php tools/websocket_smoke.php
Production Notes
- Run the process under a supervisor such as systemd, Supervisor, or another process manager.
- Terminate TLS at a proxy or configure a secure
StreamContext. - Keep authentication, authorization, rooms, routing, and persistence in your application handler.
- Track application connection state in the handler, not in the library.
- Use
SocketServer::setLogger()for lower-level server logs if you need them.