workerman/gatewayclient
GatewayClient is a lightweight PHP client for Workerman's GatewayWorker that lets any PHP application push messages to connected WebSocket clients. Send to a single client, a group, or broadcast to everyone, plus check online status and kick connections—no framework dependency required.
workerman/gatewayclient is not a Laravel package — it's a thin, static PHP SDK that lets a normal PHP process (your Laravel app) talk to a running GatewayWorker server over its internal text/binary protocol. It is only useful if you already run a GatewayWorker cluster (Register + Gateway + BusinessWorker processes). If you just need WebSockets with Laravel, use Reverb/Pusher first; reach for this when you're operating Workerman yourself.
Minimal steps
composer require workerman/gatewayclient
127.0.0.1:1236) is what the client dials by default.use GatewayClient\Gateway;
Gateway::$registerAddress = '127.0.0.1:1236';
Gateway::$secretKey = config('gateway.secret_key'); // must match GatewayWorker
Gateway::sendToUid($user->id, json_encode(['event' => 'ping']));
Where to look first
src/Gateway.php — the whole API lives here; read the static method list (~25 methods) rather than the README, which is terse.$registerAddress, $secretKey, $connectTimeout, $timeout.Events.php — the UID/group bindings that make sendToUid work are created there (or via Gateway::bindUid).First use case: authenticated user notifications. User logs in over WebSocket, your BusinessWorker binds their connection to a UID, and your Laravel controller pushes to that UID over HTTP.
1. Configure once, in a service provider. The class uses static global state; setting $registerAddress late (after the first call) has no effect because the connection is cached. Use boot():
public function boot(): void
{
Gateway::$registerAddress = config('gateway.register');
Gateway::$secretKey = config('gateway.secret_key');
Gateway::$connectTimeout = 1;
Gateway::$timeout = 2;
}
2. Wrap it behind a service, not raw static calls. Create App\Realtime\GatewayClient implementing a small interface so controllers/jobs don't depend on a global. This is also your mocking seam for tests.
class RealtimeGateway
{
public function toUser(int|string $uid, array $payload): void
{
$ok = Gateway::sendToUid($uid, json_encode($payload));
if ($ok === false) {
Log::warning('gateway send failed', ['uid' => $uid]);
}
}
}
3. Bind UIDs where you control the connection. Two valid flows:
Events::onConnect/onMessage validates a token and calls Gateway::bindUid($clientId, $uid) inside the worker.Gateway::bindUid($clientId, $uid) from Laravel — but you must already know the clientId, which you only get from the browser or from Gateway::getClientIdByUid().4. Use groups for rooms/topics, but rejoin on reconnect. joinGroup/leaveGroup/sendToGroup map nicely to chat rooms and channel subscriptions. Group membership is in-memory: the client must rejoin after every reconnect, so drive joins from onConnect in the worker, not from the browser.
5. Enqueue non-critical broadcasts. sendToAll and sendToGroup are one socket write each, but a loop over 5,000 users is 5,000 round trips. Push that into a queued job:
SendBroadcast::dispatch($channelId, $payload)->onQueue('realtime');
6. Query presence from HTTP. Gateway::isUidOnline($uid), getClientIdByUid, getAllClientIdCount, getAllUidCount are legitimate for "who's online" widgets, typing indicators, and deciding whether to send an email fallback. Cache results for a few seconds — each is a synchronous round trip.
7. Health check. A tiny artisan command that calls Gateway::getAllClientIdCount() and alerts on false catches a dead Register before users report missing messages.
8. Testing. Bind the interface in the container to a fake recorder in TestCase::setUp; never let unit tests dial 1236.
false on a dead socket rather than throwing. Always check the return or wrap in your own helper that logs — otherwise outages look like "the UI just doesn't update."$registerAccessKey/$secretKey and $registerAddress must be set before the first Gateway:: call. In Octane or long-running queue workers, a later config() change won't apply. Reset with Gateway::closeConnection() if you must reconfigure.getClientIdByUid returns an array — a user in two tabs has two client IDs. sendToUid handles this; your own lookups must too.--secretKey, the client must use the identical value or the connection is dropped with no useful error.$connectTimeout low (1s) so a dead Register doesn't hang HTTP requests. Because the connection is process-static, a stale socket after a Register restart can fail calls until you reconnect — add a retry-once wrapper in your service.register, secret_key, timeout) and read env vars.composer.json and upgrade GatewayWorker and GatewayClient together.docker-compose published ports.How can I help you explore Laravel packages today?