valtzu / symfony-websocket-client
WebSocket client implementing Symfony HttpClientInterface
Package info
github.com/valtzu/symfony-websocket-client
pkg:composer/valtzu/symfony-websocket-client
0.1.1
2026-10-26 16:41 UTC
Requires
- php: >=8.4
- symfony/http-client: ^6.4 || ^7.0 || ^8.0
- symfony/http-client-contracts: ^3.4
Requires (Dev)
- phpunit/phpunit: ^10.5 || ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A Symfony\Contracts\HttpClient\HttpClientInterface implementation that speaks WebSocket (RFC 6455).
Installation
composer require valtzu/symfony-websocket-client
Usage
$client = new WebSocketClient(['timeout' => 10]); $ws = $client->request('GET', 'wss://ws.postman-echo.com/raw'); $ws->getStatusCode(); // connects and performs the handshake; 101 on success $ws->send('Hello world'); foreach ($client->stream($ws, 5) as $chunk) { if ($chunk->isTimeout()) { break; } if ($chunk->isFirst()) { continue; } echo $chunk->getContent(); // one complete message per chunk } $ws->close();
- Requests are lazy: connection and handshake happen on first use (
getStatusCode(),send(),stream()...). Failures throwTransportException. - Only
GETis allowed. Headers,query,auth_*,base_uri,timeoutand the TLS options (verify_peer,cafile, ...) work as usual. stream()yieldsFirstChunk, oneDataChunkper message (fragments are reassembled), thenLastChunkonce the connection is closed. Ping frames are answered automatically while streaming; an idle timeout yields a timeout chunk.getContent()blocks until the server closes the connection and returns all messages concatenated.- A non-101 handshake reply (e.g. 403) is exposed as a normal response and
getContent()throws the usualHttpExceptionInterface. WebSocketResponseaddssend(string $payload, bool $binary = false),ping()andclose().
Symfony bundle
Register the bundle:
// config/bundles.php return [ // ... Valtzu\WebSocketClient\ValtzuWebSocketClientBundle::class => ['all' => true], ];
The bundle
- decorates
http_client, sows://andwss://requests are handled by the WebSocket client while everything else goes to the regular HTTP client, - registers an autowirable
Valtzu\WebSocketClient\WebSocketClientservice, - adds a WebSocket panel to the Symfony profiler in debug mode.
public function __construct(private HttpClientInterface $httpClient) {} public function __invoke(): Response { $ws = $this->httpClient->request('GET', 'wss://ws.postman-echo.com/raw'); $ws->send('Hello world'); // $ws is a WebSocketResponse // ... }
Optional configuration:
# config/packages/valtzu_websocket_client.yaml valtzu_websocket_client: default_options: timeout: 10 headers: Authorization: 'Bearer ...'
Notes:
HttpClientInterface::stream()accepts either only WebSocket responses or only HTTP responses, not a mix of both.- Scoped clients (
framework.http_client.scoped_clients) are not WebSocket-aware; injectWebSocketClientfor those cases. WebSocketClientis the more explicit option when you do not want to rely on thehttp_clientdecoration.
Profiler
In debug mode every connection is recorded with its handshake and each frame sent or received.

