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.
Installation:
composer require phrity/websocket
Basic Client Usage (Request/Response):
use WebSocket\Client;
$client = new Client("wss://echo.websocket.org/");
$client
->addMiddleware(new \WebSocket\Middleware\CloseHandler())
->addMiddleware(new \WebSocket\Middleware\PingResponder());
$client->text("Hello");
$response = $client->receive();
echo $response->getContent(); // Output: "Hello"
$client->close();
Basic Server Usage (Continuous Listening):
use WebSocket\Server;
$server = new Server();
$server
->addMiddleware(new \WebSocket\Middleware\CloseHandler())
->addMiddleware(new \WebSocket\Middleware\PingResponder())
->onText(function ($server, $connection, $message) {
$connection->text("Echo: " . $message->getContent());
});
$server->start();
Request/Response Workflow:
$client = new Client("wss://api.example.com/ws");
$client->text(json_encode(['action' => 'fetch_data']));
$response = $client->receive(); // Blocking call
$data = json_decode($response->getContent(), true);
Event-Driven Subscription:
$client = new Client("wss://stream.example.com");
$client->onText(function ($client, $connection, $message) {
$data = json_decode($message->getContent(), true);
// Process real-time updates
});
$client->start(); // Non-blocking
Reconnection Logic:
$client = new Client("wss://reliable.example.com");
$client->onError(function ($client, $connection, $error) {
if ($error instanceof \WebSocket\Exception\ReconnectException) {
$client->reconnect();
}
});
Multi-Connection Handling:
$server = new Server();
$server->onText(function ($server, $connection, $message) {
$connection->text("Processed: " . $message->getContent());
});
$server->start();
Connection-Specific Logic:
$server = new Server();
$server->onOpen(function ($server, $connection) {
$connection->setMeta(['user_id' => 123]);
});
$server->onText(function ($server, $connection, $message) {
$userId = $connection->getMeta('user_id');
// Apply user-specific logic
});
Middleware Stack:
$server = new Server();
$server
->addMiddleware(new \WebSocket\Middleware\CloseHandler())
->addMiddleware(new \WebSocket\Middleware\PingResponder())
->addMiddleware(new \WebSocket\Middleware\CompressionHandler());
Service Provider Setup:
// app/Providers/WebSocketServiceProvider.php
public function register()
{
$this->app->singleton(\WebSocket\Server::class, function ($app) {
$server = new \WebSocket\Server();
$server->onText([$this->app['websocket.handler'], 'handle']);
return $server;
});
}
Event-Driven Controllers:
// app/Http/Controllers/WebSocketController.php
public function handle($server, $connection, $message)
{
$data = json_decode($message->getContent(), true);
// Dispatch Laravel events or queue jobs
event(new WebSocketMessageReceived($data));
}
Broadcasting with Laravel Echo:
// resources/js/bootstrap.js
import Echo from 'laravel-echo';
window.Pusher = require('pusher-js');
window.Echo = new Echo({
broadcaster: 'websocket',
wsHost: window.location.hostname,
wsPort: 6001,
forceTLS: false,
enabledTransports: ['ws', 'wss'],
});
Blocking Calls:
Client::receive() is blocking. Use onText for non-blocking workflows.start() for event-driven logic.Middleware Order:
CloseHandler first to ensure graceful shutdowns.Connection Leaks:
$client->close() or $server->shutdown() can leak resources.try-finally blocks or Laravel's illuminate/support for cleanup:
try {
$client->start();
} finally {
$client->close();
}
SSL/TLS Issues:
wss:// requires valid certificates. Self-signed certs may fail silently.stream_context_create for custom CA paths:
$context = stream_context_create([
'ssl' => ['verify_peer' => false, 'allow_self_signed' => true],
]);
$client = new Client("wss://localhost", [], $context);
Fragmented Messages:
onText handler can handle partial data.Message::isFinalFragment() to check completeness.Logging:
setLogger:
$logger = new \Monolog\Logger('websocket');
$client->setLogger($logger);
$client->addMiddleware(new \WebSocket\Middleware\LoggingMiddleware($logger));
Connection Identity:
Connection::getIdentity() to track connections (v3.7+):
$server->onOpen(function ($server, $connection) {
$identity = $connection->getIdentity();
// Store in database or cache
});
Timeouts:
start() to avoid hanging:
$client->start(5.0); // 5-second timeout
Custom Middleware:
namespace App\Middleware;
use WebSocket\Middleware\MiddlewareInterface;
class AuthMiddleware implements MiddlewareInterface
{
public function __invoke($next)
{
return function ($client, $connection, $message) use ($next) {
if (!$this->validateToken($message->getContent())) {
$connection->close(1008, "Unauthorized");
return;
}
return $next($client, $connection, $message);
};
}
private function validateToken(string $content): bool { /* ... */ }
}
Register it:
$client->addMiddleware(new \App\Middleware\AuthMiddleware());
PSR-15 Middleware:
Nyholm\PsrHttp\Factory\Psr17Factory for PSR-15 compatibility:
$httpFactory = new \Nyholm\PsrHttp\Factory\Psr17Factory();
$client = new \WebSocket\Client("wss://example.com", [], [], $httpFactory);
Custom Identity:
\WebSocket\Identity\IdentityInterface for connection tracking:
class UserIdentity implements IdentityInterface
{
public function getId(): string { return $this->userId; }
}
$connection->setIdentity(new UserIdentity($userId));
Queue Integration:
onText to avoid blocking:
$server->onText(function ($server, $connection, $message) {
dispatch(new ProcessWebSocketMessage($message->getContent()));
});
Artisan Commands:
// app/Console/Commands/StartWebSocket.php
public function handle()
{
$server = resolve(\WebSocket\Server::class);
$server->start();
}
$schedule->command('websocket:start')->daily();
**Route Caching
How can I help you explore Laravel packages today?