定制 hermod/laravel-wamp 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

hermod/laravel-wamp

Composer 安装命令:

composer require hermod/laravel-wamp

包简介

A modern WAMP client for Laravel 12+

README 文档

README

Latest Version PHP Version License

Hermod is a modern WAMP v2 client for Laravel 12+, built on AMPHP v3 and PHP Fibers. Born as an actively maintained alternative to Thruway, it supports RPC (Caller/Callee) and PubSub with JSON serialization, MessagePack, and CBOR.

In Norse mythology, Hermod is the messenger of the gods — the one who carries messages between realms.

Requirements

  • PHP 8.2+
  • Laravel 12.x+
  • A WAMP v2 router (e.g., Crossbar.io)

Installation

composer require hermod/laravel-wamp

Publish the configuration:

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

Configuration

Add the variables to your .env:

# Connection
WAMP_URL=ws://localhost:8080/ws
WAMP_REALM=realm1
WAMP_SERIALIZER=json

# Authentication (anonymous by default)
WAMP_AUTH_METHOD=anonymous

# Automatic reconnect
WAMP_RECONNECT=true
WAMP_RECONNECT_MAX=5
WAMP_RECONNECT_DELAY=1
WAMP_RECONNECT_MAX_DELAY=30
WAMP_RECONNECT_MULTIPLIER=2

# Heartbeat
WAMP_HEARTBEAT=true
WAMP_HEARTBEAT_INTERVAL=30

Ticket Authentication

WAMP_AUTH_METHOD=ticket
WAMP_AUTH_ID=myuser
WAMP_AUTH_TICKET=my-secret-token

Authentication with WAMP-CRA

WAMP_AUTH_METHOD=wampcra
WAMP_AUTH_ID=myuser
WAMP_AUTH_SECRET=my-secret

Multiple connections in config/hermod.php

'connections' => [

    'default' => [
        'transport'  => 'websocket',
        'url'        => env('WAMP_URL', 'ws://localhost:8080/ws'),
        'realm'      => 'realm1',
        'serializer' => 'json',
        'auth'       => ['method' => 'anonymous'],
        'reconnect'  => ['enabled' => true, 'max_attempts' => 5],
        'heartbeat'  => ['enabled' => true, 'interval' => 30],
    ],

    'local_tcp' => [
        'transport'  => 'rawsocket',
        'url'        => 'tcp://localhost:8081',
        'realm'      => 'realm1',
        'serializer' => 'msgpack',
        'auth'       => ['method' => 'anonymous'],
        'reconnect'  => ['enabled' => true, 'max_attempts' => 5],
        'heartbeat'  => ['enabled' => true, 'interval' => 30],
    ],

    'local_unix' => [
        'transport'  => 'rawsocket',
        'url'        => 'unix:///var/run/crossbar/router.sock',
        'realm'      => 'realm1',
        'serializer' => 'cbor',
        'auth'       => ['method' => 'anonymous'],
        'reconnect'  => ['enabled' => true, 'max_attempts' => 5],
        'heartbeat'  => ['enabled' => true, 'interval' => 30],
    ],

    'secure' => [
        'transport'  => 'websocket',
        'url'        => env('WAMP_SECURE_URL', 'wss://router.example.com/ws'),
        'realm'      => 'secure_realm',
        'serializer' => 'cbor',
        'auth'       => [
            'method' => 'wampcra',
            'authid' => env('WAMP_AUTH_ID'),
            'secret' => env('WAMP_AUTH_SECRET'),
        ],
        'reconnect'  => ['enabled' => true, 'max_attempts' => 10],
        'heartbeat'  => ['enabled' => true, 'interval' => 20],
    ],

],

Usage Examples

RPC — Procedure Registration (Callee)

Without Authentication (Anonymous)

In your AppServiceProvider:

use Hermod\Laravel\Events\WampServeStarted;
use Illuminate\Support\Facades\Event;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        Event::listen(WampServeStarted::class, function (WampServeStarted $event) {

            // Simple procedure — add two numbers
            $event->client->register('com.myapp.somma', function (array $args): int {
                return $args[0] + $args[1];
            });

            // Procedure with kwargs — personalized greeting
            $event->client->register('com.myapp.saluta', function (array $args, array $kwargs): string {
                $nome  = $kwargs['nome']  ?? 'Utente';
                $titolo = $kwargs['titolo'] ?? '';
                return "Ciao, {$titolo} {$nome}!";
            });

            // Procedure with access to the Laravel DB
            $event->client->register('com.myapp.utente.trova', function (array $args): array {
                $user = \App\Models\User::find($args[0]);
                return $user?->toArray() ?? [];
            });

            // Procedure with error handling
            $event->client->register('com.myapp.divisione', function (array $args): float {
                if ($args[1] === 0) {
                    throw new \InvalidArgumentException('Divisione per zero non consentita.');
                }
                return $args[0] / $args[1];
            });

        });
    }
}

Start the worker:

php artisan wamp:serve

With Ticket Authentication

WAMP_AUTH_METHOD=ticket
WAMP_AUTH_ID=backend-service
WAMP_AUTH_TICKET=supersecrettoken123

The AppServiceProvider remains identical — authentication is transparent.

php artisan wamp:serve
# The client will automatically authenticate with the configured ticket

With WAMP-CRA authentication

WAMP_AUTH_METHOD=wampcra
WAMP_AUTH_ID=backend-service
WAMP_AUTH_SECRET=my-hmac-secret
php artisan wamp:serve
# The client will automatically calculate the HMAC-SHA256 signature

With specific connection

php artisan wamp:serve --connection=secure

RPC — Procedure Call (Caller)

Synchronous call

use Hermod\Laravel\Facades\Wamp;

// In a controller, job, command, etc.
Wamp::connect();

// Call with positional arguments
$somma = Wamp::call('com.myapp.somma', [3, 5]);
// → 8

// Call with kwargs
$saluto = Wamp::call('com.myapp.saluta', [], ['nome' => 'Mario', 'titolo' => 'Dr.']);
// → "Ciao, Dr. Mario!"

// Call with error handling
try {
    $risultato = Wamp::call('com.myapp.divisione', [10, 0]);
} catch (\Hermod\Exceptions\RpcException $e) {
    Log::error("Errore RPC: {$e->getMessage()}", ['wamp_error' => $e->wampError]);
}

Wamp::disconnect();

Asynchronous call

use Hermod\Laravel\Facades\Wamp;
use function Amp\Future\await;

Wamp::connect();

// Single asynchronous call
$future = Wamp::callAsync('com.myapp.somma', [10, 20]);
$result = $future->await();
// → 30

// Parallel calls — executed simultaneously
$futures = [
    'somma'   => Wamp::callAsync('com.myapp.somma',    [1, 2]),
    'saluto'  => Wamp::callAsync('com.myapp.saluta',   [], ['nome' => 'Mario']),
    'utente'  => Wamp::callAsync('com.myapp.utente.trova', [42]),
];

$risultati = await($futures);
// $risultati['somma']  → 3
// $risultati['saluto'] → "Ciao, Mario!"
// $risultati['utente'] → ['id' => 42, 'name' => '...']

Wamp::disconnect();

In a Laravel Route

use Hermod\Laravel\Facades\Wamp;
use Illuminate\Support\Facades\Route;

Route::get('/calcola/{a}/{b}', function (int $a, int $b) {
    Wamp::connect();
    $result = Wamp::call('com.myapp.somma', [$a, $b]);
    Wamp::disconnect();
    return response()->json(['risultato' => $result]);
});

Route::get('/utente/{id}', function (int $id) {
    Wamp::connect();
    $utente = Wamp::call('com.myapp.utente.trova', [$id]);
    Wamp::disconnect();

    if (empty($utente)) {
        return response()->json(['error' => 'Utente non trovato'], 404);
    }

    return response()->json($utente);
});

From terminal (debug)

# Call with positional arguments
php artisan wamp:call com.myapp.somma 3 5

# Calling with kwargs in JSON
php artisan wamp:call com.myapp.saluta --kwargs='{"nome":"Mario","titolo":"Dr."}'

# Call on specific connection
php artisan wamp:call com.myapp.somma 10 20 --connection=secure

# Output atteso:
# ✓ Risultato (12.5ms):
# 30

PubSub — Publisher

Fire and Forget Publication

use Hermod\Laravel\Facades\Wamp;

Wamp::connect();

// Positional array → args
Wamp::publish('com.myapp.notifiche', ['messaggio importante']);

// Associative array → kwargs automatically
Wamp::publish('com.myapp.ordini', ['ordine_id' => 123, 'stato' => 'spedito']);

// Args and kwargs combined
Wamp::publish('com.myapp.eventi', [1, 2, 3], ['extra' => 'info']);

Wamp::disconnect();

Publication with acknowledgement

use Hermod\Laravel\Facades\Wamp;

Wamp::connect();

// Waiting for PUBLISHED confirmation from the router
$publicationId = Wamp::publishWithAck(
    'com.myapp.notifiche',
    ['messaggio' => 'Ordine confermato!']
)->await();

Log::info("Messaggio pubblicato", ['publication_id' => $publicationId]);

Wamp::disconnect();

From a Laravel Job

namespace App\Jobs;

use Hermod\Laravel\Facades\Wamp;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

class NotificaOrdineSpe­dito implements ShouldQueue
{
    use Queueable;

    public function __construct(
        private readonly int    $ordineId,
        private readonly string $corriere,
    ) {}

    public function handle(): void
    {
        Wamp::connect();

        Wamp::publish('com.myapp.ordini.spediti', [], [
            'ordine_id' => $this->ordineId,
            'corriere'  => $this->corriere,
            'timestamp' => now()->toISOString(),
        ]);

        Wamp::disconnect();
    }
}

Da un Observer Eloquent

namespace App\Observers;

use App\Models\Ordine;
use Hermod\Laravel\Facades\Wamp;

class OrdineObserver
{
    public function updated(Ordine $ordine): void
    {
        if ($ordine->isDirty('stato')) {
            Wamp::connect();

            Wamp::publish('com.myapp.ordini.aggiornati', [], [
                'ordine_id'  => $ordine->id,
                'stato_nuovo' => $ordine->stato,
                'stato_vecchio' => $ordine->getOriginal('stato'),
            ]);

            Wamp::disconnect();
        }
    }
}

PubSub — Subscriber

Subscribing to a topic

In your AppServiceProvider:

use Hermod\Laravel\Events\WampServeStarted;

Event::listen(WampServeStarted::class, function (WampServeStarted $event) {

    // Simple subscription
    $event->client->subscribe('com.myapp.notifiche', function (array $args, array $kwargs): void {
        Log::info('Notifica ricevuta', [
            'args'   => $args,
            'kwargs' => $kwargs,
        ]);
    });

    // Subscription with business logic
    $event->client->subscribe('com.myapp.ordini.spediti', function (array $args, array $kwargs): void {
        $ordineId = $kwargs['ordine_id'] ?? null;
        $corriere = $kwargs['corriere']  ?? 'sconosciuto';

        if ($ordineId) {
            \App\Models\Ordine::find($ordineId)?->update([
                'notifica_spedizione_inviata' => true,
            ]);

            Log::info("Ordine {$ordineId} spedito con {$corriere}");
        }
    });

    // Dynamic subscription and unsubscription
    $subscription = $event->client->subscribe('com.myapp.temporaneo', function (array $args): void {
        Log::debug('Evento temporaneo ricevuto', $args);
    });

    // You can unsubscribe later
    // $event->client->unsubscribeById($subscription);

});

Start the worker subscriber:

php artisan wamp:serve

Automatic Laravel Event

Every WAMP EVENT received automatically dispatches a Laravel event, without having to register an explicit handler:

use Hermod\Laravel\Events\WampEventReceived;

// In EventServiceProvider or AppServiceProvider
Event::listen(WampEventReceived::class, function (WampEventReceived $event): void {

    // Filter by topic
    match ($event->topic) {
        'com.myapp.ordini.spediti' => dispatch(new \App\Jobs\ProcessaSpedizione(
            ordineId: $event->kwargs['ordine_id'],
        )),
        'com.myapp.notifiche' => \App\Models\Notifica::create([
            'contenuto' => $event->args[0] ?? '',
            'ricevuta_at' => now(),
        ]),
        default => Log::debug("Evento WAMP non gestito: {$event->topic}"),
    };

});

Combining RPC and PubSub in the same worker

use Hermod\Laravel\Events\WampServeStarted;

Event::listen(WampServeStarted::class, function (WampServeStarted $event) {

    // ── RPC Procedures ────────────────────────────────────
    $event->client->register('com.myapp.utente.crea', function (array $args, array $kwargs): array {
        $user = \App\Models\User::create([
            'name'  => $kwargs['nome'],
            'email' => $kwargs['email'],
        ]);

        // Publish event after user creation
        $event->client->publish('com.myapp.utenti.creati', [], [
            'user_id' => $user->id,
            'email'   => $user->email,
        ]);

        return $user->toArray();
    });

    $event->client->register('com.myapp.statistiche', function (): array {
        return [
            'utenti_totali' => \App\Models\User::count(),
            'ordini_attivi' => \App\Models\Ordine::where('stato', 'attivo')->count(),
        ];
    });

    // ── PubSub Subscriptions ──────────────────────────────
    $event->client->subscribe('com.myapp.cache.invalida', function (array $args, array $kwargs): void {
        $chiave = $kwargs['chiave'] ?? '*';
        \Illuminate\Support\Facades\Cache::forget($chiave);
        Log::info("Cache invalidata per chiave: {$chiave}");
    });

    $event->client->subscribe('com.myapp.broadcast', function (array $args, array $kwargs): void {
        // Forward the WAMP event as a Laravel broadcast event
        \Illuminate\Support\Facades\Broadcast::on('general')
            ->event(new \App\Events\WampBroadcast($kwargs))
            ->sendNow();
    });

});

Automatic Reconnect

Reconnect is configured in config/hermod.php and works transparently during listen(). When the connection drops, Hermod:

  1. Detects the disconnection in the listening loop
  2. Waits for the initial delay (default: 1 second)
  3. Attempts to reconnect
  4. If it fails, it applies exponential backoff (1s → 2s → 4s → 8s → 16s → max 30s)
  5. After a successful reconnect, it automatically re-registers all procedures and subscriptions
// Custom configuration for critical environment
// config/hermod.php
'reconnect' => [
    'enabled'      => true,
    'max_attempts' => 10,      // maximum 10 attempts
    'base_delay'   => 0.5,     // starts from 0.5 seconds
    'max_delay'    => 60.0,    // 60-second cap
    'multiplier'   => 2.0,     // doubles with every failure
],

Artisan Commands

# Start a WAMP worker (Callee + Subscriber)
php artisan wamp:serve
php artisan wamp:serve --connection=secure
php artisan wamp:serve --realm=myrealm
php artisan wamp:serve --serializer=cbor

# Make an RPC call from the terminal
php artisan wamp:call com.myapp.somma 3 5
php artisan wamp:call com.myapp.saluta --kwargs='{"nome":"Mario"}'
php artisan wamp:call com.myapp.somma 10 20 --connection=secure

Serializers

Driver Subprotocol Stato Note .env
json wamp.2.json ✅ Stabile Default, nessuna dipendenza extra WAMP_SERIALIZER=json
cbor wamp.2.cbor ✅ Stabile Richiede spomky-labs/cbor-php WAMP_SERIALIZER=cbor
msgpack wamp.2.msgpack ✅ Stabile Richiede rybakit/msgpack WAMP_SERIALIZER=msgpack

Authentication

Method Use case Required configuration
anonymous Local development, closed networks none
ticket API key, static JWT authid, ticket
wampcra Secure authentication with HMAC authid, secret

Roadmap

Version Content
v0.1 RPC Core — Caller, Callee, JSON, CBOR ✅
v0.2 PubSub — Publisher, Subscriber ✅
v0.3 Auth — WAMP-CRA, Ticket, Reconnect ✅
v1.0 MessagePack, RawSocket, Complete documentation ✅

Testing

# All tests
./vendor/bin/pest

# With coverage
./vendor/bin/pest --coverage --min=80

# For specific suite
./vendor/bin/pest --filter=Auth
./vendor/bin/pest --filter=Reconnect
./vendor/bin/pest --filter=PubSub
./vendor/bin/pest --filter=Rpc
./vendor/bin/pest --filter=RawSocket
./vendor/bin/pest --filter=Msgpack

Contribute

Read CONTRIBUTING.md for guidelines.

License

Hermod is open source software released under the [MIT] License (LICENSE.md).


hermod/laravel-wamp 适用场景与选型建议

hermod/laravel-wamp 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 11 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 04 月 18 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「rpc」 「websocket」 「laravel」 「pubsub」 「WAMP」 「amphp」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 hermod/laravel-wamp 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 hermod/laravel-wamp 我们能提供哪些服务?
定制开发 / 二次开发

基于 hermod/laravel-wamp 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 11
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 32
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-04-18