PHPackages                             inverge/websocket - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. inverge/websocket

ActiveLibrary

inverge/websocket
=================

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

00PHP

Since Aug 24Pushed todayCompare

[ Source](https://github.com/Inverge-team/websocket-php-sdk)[ Packagist](https://packagist.org/packages/inverge/websocket)[ RSS](/packages/inverge-websocket/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

Inverge WebSocket — PHP &amp; Laravel SDK
=========================================

[](#inverge-websocket--php--laravel-sdk)

A **backend** SDK for the [Inverge WebSocket service](https://wss.inverge.net). 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 .

- 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
-------

[](#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

[](#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

[](#plain-php)

```
use Inverge\WebSocket\Client;

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

---

Quick start
-----------

[](#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
----------------------------------------------------

[](#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
--------

[](#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 &amp; payload schemas
--------------------------------------

[](#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 &amp; billing
-------------------

[](#usage--billing)

Usage, analytics, and billing statements are **not** part of this SDK — view them in the dashboard at .

---

Laravel notifications
---------------------

[](#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
--------------

[](#error-handling)

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

ExceptionWhen`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
---------------------

[](#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
----------------

[](#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
-------

[](#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
-------

[](#license)

MIT.

###  Health Score

20

—

LowBetter than 12% of packages

Maintenance65

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/16746153?v=4)[Inverge](/maintainers/Inverge)[@inverge](https://github.com/inverge)

---

Top Contributors

[![TheXerr0r](https://avatars.githubusercontent.com/u/57012627?v=4)](https://github.com/TheXerr0r "TheXerr0r (2 commits)")

### Embed Badge

![Health badge](/badges/inverge-websocket/health.svg)

```
[![Health](https://phpackages.com/badges/inverge-websocket/health.svg)](https://phpackages.com/packages/inverge-websocket)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
