inverge/websocket

PHP & Laravel backend SDK for the Inverge WebSocket service — rooms, relations, payload schemas, and server-to-room emit over the API-key partner API.

Maintainers

Package info

github.com/Inverge-team/websocket-php-sdk

Homepage

pkg:composer/inverge/websocket

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-08-24 17:51 UTC

This package is auto-updated.

Last update: 2026-08-24 18:02:53 UTC


README

A backend SDK for the Inverge WebSocket service. It talks to the API-key partner HTTP surface — your server emits events into rooms and manages rooms / relations / payload schemas. It does not open a socket itself (that's the browser/device side), and it does not expose usage or billing — those live in the dashboard at https://wss.inverge.net.

  • Framework-agnostic core (plain PHP, Guzzle transport).
  • First-class Laravel integration: auto-discovered provider, WebSocket facade, publishable config, and a notification channel.
  • Typed exceptions, an inert "disabled" mode for local/CI, and helpers that make correct, cheap room modeling the easy path.

Requires PHP 8.2+.

Install

composer require inverge/websocket

Set your credentials:

WEBSOCKET_BASE_URL=https://websocket.inverge.net
WEBSOCKET_API_KEY=your_partner_api_key
WEBSOCKET_ENABLED=true

Laravel

Nothing to wire up — the provider and WebSocket facade are auto-discovered. Publish the config if you want to tweak it:

php artisan vendor:publish --tag=websocket-config

Plain PHP

use Inverge\WebSocket\Client;

$ws = Client::make('https://websocket.inverge.net', getenv('WEBSOCKET_API_KEY'));

Quick start

use Inverge\WebSocket\Support\Room;

// Emit an event into a room (and any rooms related to it)
$ws->emit(Room::scoped('order', 8821), 'order.created', ['orderId' => 8821]);

// Laravel facade
use Inverge\WebSocket\Laravel\Facades\WebSocket;
WebSocket::emit('order:8821', 'order.created', ['orderId' => 8821]);

Core concept: scope rooms by recipient, not by topic

A room is a delivery list. Put a socket in a room only if it should receive every message sent there. Event names (order.created) are labels on the payload — a room broadcast reaches every member regardless of event name.

Putting everyone in one big orders room and separating by event name delivers everything to everyone — a data leak and a billing blow-up (you're billed per recipient delivery). Instead, scope rooms to the parties who need the message:

use Inverge\WebSocket\Support\Room;

Room::scoped('order', 8821);  // "order:8821"  -> the client + assigned pilot + ops
Room::of('pilot', 17);        // "pilot:17"    -> one driver
Room::of('client', 42);       // "client:42"   -> one customer

Deliveries per emit ≈ members of the target room(s). Keep rooms as small as the set of parties that genuinely need the message.

Emitting

// one event
$ws->emit('order:8821', 'order.created', $payload);

// multiple events, same payload, same room
$ws->emit('order:8821', ['order.created', 'audit.log'], $payload);

// several rooms in ONE request (e.g. the order parties AND the ops dashboard)
$ws->broadcast([
    ['room' => 'order:8821', 'event' => 'order.created', 'payload' => $payload],
    ['room' => 'ops',        'event' => 'order.created', 'payload' => $payload],
]);

// route from a room to its RELATED rooms (relational fan-out), or to one target
$ws->route(from: 'order:8821', to: null, event: 'order.created', payload: $payload);
$ws->route(from: 'order:8821', to: 'ops', event: 'order.created', payload: $payload);

Every emit returns the service ack, e.g. ['ok' => true, 'room' => 'c:1:order:8821', 'related' => ['ops'], 'events' => ['order.created']].

Rooms, relations & payload schemas

use Inverge\WebSocket\Enums\RoomType;

$rooms = $ws->rooms();

$rooms->all();                                   // list rooms
$order = $rooms->create('order:8821', RoomType::Relational);
$rooms->ensure('ops');                           // idempotent get-or-create
$rooms->delete($order['id']);

// relations: an emit to $orderId also fans out to $opsId
$rooms->link($orderId, $opsId);
$rooms->unlink($orderId, $opsId);
$rooms->related('order:8821');                   // -> ['ops']

// payload schema (JSON Schema draft-07 / 2020-12) — reject malformed emits
$rooms->setSchema($orderId, [
    'type' => 'object',
    'required' => ['orderId'],
    'properties' => ['orderId' => ['type' => 'integer']],
]);
$rooms->disableSchema($orderId);                 // keep it attached but off
$rooms->enableSchema($orderId);
$rooms->getSchema($orderId);
$rooms->deleteSchema($orderId);

When a schema is enforced, an emit() with a non-conforming payload throws ValidationException (see below).

Usage & billing

Usage, analytics, and billing statements are not part of this SDK — view them in the dashboard at https://wss.inverge.net.

Laravel notifications

Send realtime events straight from a Notification:

use Illuminate\Notifications\Notification;
use Inverge\WebSocket\Laravel\Notifications\WebSocketChannel;
use Inverge\WebSocket\Laravel\Notifications\WebSocketMessage;
use Inverge\WebSocket\Support\Room;

class OrderCreated extends Notification
{
    public function __construct(private readonly Order $order) {}

    public function via($notifiable): array
    {
        return [WebSocketChannel::class];
    }

    public function toWebSocket($notifiable): WebSocketMessage
    {
        return WebSocketMessage::make()
            ->to(Room::scoped('order', $this->order->id))
            ->event('order.created')
            ->payload(['orderId' => $this->order->id, 'status' => 'pending']);
    }
}

toWebSocket() may instead return a plain array of broadcast messages (['room'=>, 'event'=>, 'payload'=>], ...) to hit several rooms at once.

Resolve the client anywhere via DI or the facade:

public function __construct(private readonly \Inverge\WebSocket\Client $ws) {}
// or
\Inverge\WebSocket\Laravel\Facades\WebSocket::emit(...);

Error handling

All failures extend Inverge\WebSocket\Exceptions\WebSocketException, which carries ->status, ->errorCode, ->details, and ->errors():

Exception When
AuthenticationException 401/403 — bad or missing API key
NotFoundException 404 — unknown resource, or a disabled feature
RoomExistsException 409 — duplicate room name
ValidationException payload failed the room's schema (->errors())
RequestException / ServerException other 4xx / 5xx
ConnectionException transport failure (timeout, DNS, refused)
use Inverge\WebSocket\Exceptions\ValidationException;

try {
    $ws->emit('order:8821', 'order.created', $payload);
} catch (ValidationException $e) {
    report($e);                 // $e->errors() has the schema violations
}

Disabled / no-op mode

When enabled is false or no API key is configured, the SDK makes no network calls: emit-like methods return ['skipped' => true] and reads return empty. This lets your app run untouched in local/dev/CI. Toggle with WEBSOCKET_ENABLED=false.

Custom transport

The default transport is Guzzle. Swap it (shared client, tracing, tests) by implementing Inverge\WebSocket\Contracts\Transport:

$ws = new Client($config, new MyTransport());
// in Laravel: bind your own in a service provider, then rebuild the singleton.

Testing

composer install
composer test

Tests use an in-memory FakeTransport — no network required. Use the same double in your app to assert emits without hitting the service.

License

MIT.