phrity/websocket
PHP WebSocket client and multi-connection server with ws/wss support. Includes listener callbacks, standard Close and Ping/Pong handling, optional deflate compression, fragmentation and masking support, plus middleware system for extending behavior.
## Getting Started
### Minimal Setup for Laravel
1. **Install the package**:
```bash
composer require phrity/websocket
Basic Client Usage (Request/Response):
use WebSocket\Client;
use WebSocket\Middleware\CloseHandler;
use WebSocket\Middleware\PingResponder;
$client = new Client("wss://echo.websocket.org/");
$client
->addMiddleware(new CloseHandler())
->addMiddleware(new PingResponder());
$client->text("Hello");
$response = $client->receive();
echo $response->getContent(); // Output: "Hello"
$client->close();
Basic Server Setup (Laravel Route):
use WebSocket\Server;
use WebSocket\Middleware\CloseHandler;
use WebSocket\Middleware\PingResponder;
Route::get('/ws', function () {
$server = new Server(8080);
$server
->addMiddleware(new CloseHandler())
->addMiddleware(new PingResponder())
->onText(function ($server, $connection, $message) {
$connection->text("Echo: " . $message->getContent());
});
$server->start();
});
Key Files to Review:
docs/Client.md (for client patterns)docs/Server.md (for server patterns)docs/Middleware/ (for middleware customization)Use the client for background tasks (e.g., real-time data sync):
use Illuminate\Bus\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use WebSocket\Client;
class SyncDataJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
$client = new Client("wss://api.example.com/ws");
$client->addMiddleware(new CloseHandler());
$client->text(json_encode(['action' => 'sync']));
$response = $client->receive();
$data = json_decode($response->getContent(), true);
// Process $data...
$client->close();
}
}
Trigger Laravel events on WebSocket messages:
$server->onText(function ($server, $connection, $message) {
event(new WebSocketMessageReceived(
$message->getContent(),
$connection->getRemoteAddress()
));
});
Create a base middleware stack in a service provider:
// app/Providers/WebSocketServiceProvider.php
public function register()
{
$this->app->singleton('websocket.middleware.stack', function () {
return [
new CloseHandler(),
new PingResponder(),
new CompressionExtension(new DeflateCompressor()),
];
});
}
Then reuse it:
$client = new Client("wss://example.com");
foreach ($this->app['websocket.middleware.stack'] as $middleware) {
$client->addMiddleware($middleware);
}
Store WebSocket connections in Laravel sessions:
$server->onOpen(function ($server, $connection) {
session([$connection->getId() => $connection]);
});
$server->onClose(function ($server, $connection) {
session()->forget($connection->getId());
});
Replace Laravel Echo/Pusher with custom WebSocket broadcasting:
// app/Services/WebSocketBroadcaster.php
class WebSocketBroadcaster
{
protected $server;
public function __construct(Server $server)
{
$this->server = $server;
}
public function broadcast($event, $data)
{
$this->server->getConnections()->each(function ($connection) use ($data) {
$connection->text(json_encode($data));
});
}
}
use WebSocket\Middleware\ProcessHttpIncomingInterface;
class AuthMiddleware implements ProcessHttpIncomingInterface
{
public function processHttpIncoming($stack, $connection)
{
$request = $stack->handleHttpIncoming();
$token = $request->getHeader('Authorization');
if (!auth()->validate($token)) {
throw new \RuntimeException("Unauthorized");
}
return $request;
}
public function __toString()
{
return 'AuthMiddleware';
}
}
// Usage:
$client->addMiddleware(new AuthMiddleware());
use WebSocket\Middleware\ProcessIncomingInterface;
use WebSocket\Middleware\ProcessOutgoingInterface;
class LoggingMiddleware implements ProcessIncomingInterface, ProcessOutgoingInterface
{
public function processIncoming($stack, $connection)
{
$message = $stack->handleIncoming();
\Log::debug("Incoming: " . $message->getContent());
return $message;
}
public function processOutgoing($stack, $connection, $message)
{
\Log::debug("Outgoing: " . $message->getContent());
return $stack->handleOutgoing($message);
}
public function __toString()
{
return 'LoggingMiddleware';
}
}
use WebSocket\Middleware\ProcessIncomingInterface;
use Illuminate\Cache\RateLimiter;
class RateLimitMiddleware implements ProcessIncomingInterface
{
protected $limiter;
public function __construct(RateLimiter $limiter)
{
$this->limiter = $limiter;
}
public function processIncoming($stack, $connection)
{
$key = 'websocket_rate_limit_' . $connection->getRemoteAddress();
if (!$this->limiter->tooManyAttempts($key, 10)) {
return $stack->handleIncoming();
}
throw new \RuntimeException("Rate limit exceeded");
}
public function __toString()
{
return 'RateLimitMiddleware';
}
}
Use WebSocket clients in queued jobs for async operations:
// Dispatch a job to sync data via WebSocket
SyncDataJob::dispatch($userId);
Listen to Laravel events and broadcast via WebSocket:
// In an event listener
event(new OrderPlaced($order));
broadcast(new WebSocketBroadcast($order))->toWebSocket();
Format WebSocket messages as API resources:
$connection->text(response()->json([
new UserResource($user),
'meta' => ['timestamp' => now()]
]));
Validate incoming WebSocket messages:
$server->onText(function ($server, $connection, $message) {
$data = json_decode($message->getContent(), true);
$validator = Validator::make($data, [
'action' => 'required|in:fetch,update',
'data' => 'sometimes|array',
]);
if ($validator->fails()) {
$connection->text(response()->json(['error' => $validator->errors()]));
return;
}
// Process valid data...
});
receive() is blocking. Avoid calling it in routes or middleware.onText callbacks) or run in a separate process/queue.$client->close() or $server->stop() can leak connections.finally blocks or context managers:
$client = new Client("wss://example.com");
try {
$client->text("Hello");
$response = $client->receive();
} finally {
$client->close();
}
$client->addMiddleware(new AuthMiddleware()) // First
->addMiddleware(new LoggingMiddleware()); // Second
wss:// connections may fail due to missing CA certificates.// config/websocket.php
'ssl' => [
'local_cert' => storage_path('certs/local.crt'),
'local_pk' => storage_path('certs/local.key'),
'passphrase' => env('SSL_PASSPHRASE'),
];
How can I help you explore Laravel packages today?