artisan-build/resonance

Laravel package for beautiful Reverb, Pusher, Soketi, and Ably integration in pure PHP.

Fund package maintenance!
artisan-build

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 1

pkg:composer/artisan-build/resonance

v0.1.1 2026-01-18 08:21 UTC

This package is auto-updated.

Last update: 2026-01-18 08:23:44 UTC


README

Resonance Logo

Resonance

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Resonance is a Laravel WebSocket client for CLI commands, queue workers, and background processes. Inspired by Laravel Echo's API, it provides a familiar interface for real-time event listening—but from your PHP backend instead of the browser.

Built on artisan-build/pusher-websocket-php and ReactPHP's async event loop, Resonance connects to Reverb, Pusher, Soketi, and other Pusher-compatible servers.

Why Resonance?

Laravel Echo handles real-time features in the browser, but what about server-side PHP processes that need to listen for WebSocket events? Resonance fills that gap with:

  • Echo-inspired API - Familiar listen(), private(), join() methods
  • Manager pattern - Connect to multiple WebSocket servers simultaneously
  • Extensible drivers - Add custom broadcasters with extend()
  • Laravel-native - Config files, facades, and service providers out of the box

Installation

composer require artisan-build/resonance

You can publish the config file with:

php artisan vendor:publish --tag="resonance-config"

Configuration

Resonance follows Laravel's convention of defining connections in config and selecting via environment variable:

// config/resonance.php
return [
    'default' => env('RESONANCE_CONNECTION', 'reverb'),

    'connections' => [
        'reverb' => [
            'broadcaster' => 'reverb',
            'key' => env('REVERB_APP_KEY'),
            'authToken' => env('RESONANCE_AUTH_TOKEN'),
            'host' => env('REVERB_HOST', '127.0.0.1'),
            'port' => env('REVERB_PORT', 8080),
            'forceTLS' => env('REVERB_SCHEME', 'https') === 'https',
            'channelAuthorization' => [
                'endpoint' => env('APP_URL').'/broadcasting/auth',
            ],
        ],

        'pusher' => [
            'broadcaster' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'authToken' => env('RESONANCE_AUTH_TOKEN'),
            'cluster' => env('PUSHER_APP_CLUSTER', 'mt1'),
            'forceTLS' => true,
            'channelAuthorization' => [
                'endpoint' => env('APP_URL').'/broadcasting/auth',
            ],
        ],

        'null' => [
            'broadcaster' => 'null',
        ],
    ],

    'namespace' => 'App.Events',
];

Configuration Options

Option Description
broadcaster The broadcaster type: reverb, pusher, or null
key Your app key for the WebSocket server
authToken Bearer token for channel authorization (e.g., Sanctum token)
host WebSocket server hostname
port WebSocket server port
forceTLS Whether to use secure WebSocket (wss://)
cluster Pusher cluster (for Pusher broadcaster)
channelAuthorization.endpoint Full URL to your broadcasting auth endpoint

Note: The authToken is your authentication token (like a Laravel Sanctum token) used to authorize private/presence channel subscriptions. This is not the Reverb app secret—channel authorization is handled server-side where the signing secret lives.

Use RESONANCE_CONNECTION=null in your .env.testing to disable WebSocket connections during tests.

Usage

Basic Channel Subscription

use ArtisanBuild\Resonance\Facades\Resonance;

// Using the default connection from config
$channel = Resonance::channel('orders');

$channel->listen('OrderShipped', function ($event) {
    echo "Order shipped: " . $event['order_id'];
});

Multiple Connections

Resonance uses the Manager pattern, allowing you to work with multiple connections:

use ArtisanBuild\Resonance\Facades\Resonance;

// Use a specific connection
Resonance::connection('reverb')->listen('orders', 'OrderShipped', function ($event) {
    // Handle internal events from your Reverb server
});

Resonance::connection('pusher')->listen('analytics', 'PageView', function ($event) {
    // Handle events from a third-party Pusher service
});

Custom Drivers

Extend Resonance with custom broadcasters:

use ArtisanBuild\Resonance\Facades\Resonance;

Resonance::extend('ably', function ($app, $config) {
    return new AblyConnector($config);
});

Direct Instantiation

Or instantiate directly with custom options:

use ArtisanBuild\Resonance\Resonance;

$resonance = new Resonance([
    'broadcaster' => 'reverb',
    'key' => 'your-app-key',
    'authToken' => 'your-sanctum-token',
    'host' => '127.0.0.1',
    'port' => 8080,
    'forceTLS' => false,
    'channelAuthorization' => [
        'endpoint' => 'https://your-app.com/broadcasting/auth',
    ],
]);

$resonance->listen('orders', 'OrderShipped', function ($event) {
    echo "Order shipped: " . $event['order_id'];
});

Private Channels

// Subscribe to a private channel
$channel = $resonance->private('orders.123');

$channel->listen('OrderUpdated', function ($event) {
    // Handle the event
});

Presence Channels

// Join a presence channel
$presence = $resonance->join('chat.room.1');

$presence->here(function ($users) {
    // Users currently in the channel
});

$presence->joining(function ($user) {
    // A user joined
});

$presence->leaving(function ($user) {
    // A user left
});

$presence->listen('NewMessage', function ($event) {
    // Handle chat message
});

Connection Management

// Register a callback for when connection is established
$resonance->connected(function () {
    echo "Connected to WebSocket server!";
});

// Get the socket ID (useful for excluding sender)
$socketId = $resonance->socketId();

// Leave a specific channel
$resonance->leave('orders');

// Leave a single channel variant
$resonance->leaveChannel('private-orders.123');

// Disconnect entirely
$resonance->disconnect();

Encrypted Private Channels

$channel = $resonance->encryptedPrivate('sensitive-data');

$channel->listen('SecretEvent', function ($event) {
    // Decrypted event data
});

Real-World Example: CLI Chat with Community Prompts

Here's a real-time chat application built with AsyncPrompt from artisan-build/community-prompts, demonstrating how Resonance enables interactive CLI tools:

<?php

namespace App\Console\Prompts;

use ArtisanBuild\Resonance\Facades\Resonance;
use ArtisanBuild\Resonance\Resonance as ResonanceInstance;
use Illuminate\Support\Collection;
use Laravel\Prompts\AsyncPrompt;
use Laravel\Prompts\Key;

class ChatPrompt extends AsyncPrompt
{
    protected ResonanceInstance $socket;
    protected mixed $channel;
    public Collection $messages;

    public function __construct()
    {
        $this->messages = collect([['system', 'Connecting...']]);

        $this->on('key', fn ($key) => match ($key) {
            Key::ENTER => $this->sendMessage($this->value()),
            Key::ESCAPE => $this->disconnect(),
            default => null,
        });

        $this->connect();
    }

    protected function connect(): void
    {
        // Use the facade - config provides auth token and endpoint
        $this->socket = Resonance::connection('reverb');

        $this->socket->connected(function () {
            $this->channel = $this->socket->private('chat');

            // Listen for all events on the channel
            $this->channel->listenToAll(function ($event, $data) {
                $this->handleEvent($event, $data);
            });

            // Or listen for specific events
            $this->channel->listen('MessageSent', function ($data) {
                $this->messages->push(['left', $data['message']]);
                $this->render();
            });

            // Listen for client whispers (peer-to-peer)
            $this->channel->listenForWhisper('typing', function ($data) {
                $this->typing = true;
                $this->render();
            });

            $this->messages = collect([['system', 'Connected! Start chatting...']]);
            $this->render();
        });
    }

    protected function sendMessage(string $message): void
    {
        if (empty($message)) return;

        // Send a whisper to other clients (no server round-trip)
        $this->channel->whisper('message', ['text' => $message]);

        $this->messages->push(['right', $message]);
        $this->typedValue = '';
        $this->render();
    }

    protected function disconnect(): void
    {
        $this->socket->disconnect();
        $this->submit();
    }
}

This example showcases:

  • Facade usage with config-driven auth token and endpoint
  • Private channel subscription with automatic Bearer token authentication
  • listenToAll() for catching all channel events
  • listenForWhisper() for peer-to-peer client events
  • whisper() for sending client events without server round-trips

API Reference

Channel Methods

Method Description
listen($event, $callback) Listen for a specific event
listenToAll($callback) Listen for all events on the channel
listenForWhisper($event, $callback) Listen for client whisper events
stopListening($event) Stop listening to an event
stopListeningForWhisper($event) Stop listening for a whisper
notification($callback) Listen for Laravel notifications
subscribed($callback) Callback when subscription succeeds
error($callback) Callback when subscription fails

Private Channel Methods

Method Description
whisper($event, $data) Send a client event to other subscribers

Presence Channel Methods

Method Description
here($callback) Get current members when joining
joining($callback) Called when a member joins
leaving($callback) Called when a member leaves
whisper($event, $data) Send a client event to other subscribers

Supported Broadcasters

Broadcaster Status
Laravel Reverb Supported
Pusher Channels Supported
Soketi Supported (via Pusher connector)
Ably Planned

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.