Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Thruway Bundle Laravel Package

voryx/thruway-bundle

Symfony bundle integrating Thruway, a WAMP v2 client/router, to add real-time RPC and Pub/Sub to Symfony apps. Expose PHP services over WebSocket, publish and subscribe to events, and build reactive features with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: voryx/thruway-bundle is a Symfony 2 bundle, not a Laravel package. There is no service provider, facade, or php artisan vendor:publish path here. If you're on a Laravel stack, you cannot composer require this and expect it to boot — the value is entirely in its Symfony DI wiring (a Bundle class, services.yml extension, compiler passes, and a thruway:router console command). Treat this document as guidance for (a) the Symfony 2 app you may still own, and (b) the pattern you'd reimplement on Laravel's broadcasting stack.

Where to look first: the Resources/config/ directory (the service definitions that wire the router), the Command/ directory (the long-running thruway:router command), and the Annotation/ folder (@Register, @Subscribe, @Topic). That trio contains 90% of what you'll touch day-to-day.

Minimal install (Symfony 2 target):

composer require voryx/thruway-bundle
// app/AppKernel.php
public function registerBundles()
{
    $bundles = [
        // ...
        new Thruway\Bundle\ThruwayBundle(),
    ];
    return $bundles;
}
# app/config/config.yml
thruway:
    realm: "realm1"
    router:
        ip: "0.0.0.0"
        listen_port: 9090

Run the router as a long-running daemon — never via php-fpm or a web request. It blocks and holds a WebSocket listener open:

php bin/console thruway:router

First use case — expose a service method as a callable RPC procedure:

namespace AppBundle\Wamp;

use Thruway\Bundle\Annotation\Register;

class Calculator
{
    /** @Register("com.example.add") */
    public function add($args, $kwargs, $session)
    {
        return [$args[0] + $args[1]]; // result must be an array of positional values
    }
}

Consume it from the browser using autobahn.js (the reference WAMP client):

const conn = new autobahn.Connection({ url: 'ws://localhost:9090/', realm: 'realm1' });
conn.onopen = (session) => {
  session.call('com.example.add', [2, 3]).then(r => console.log(r)); // [5]
};
conn.open();

First-use pitfall to plan for: the @Register/@Subscribe annotations are discovered via the Symfony container and cached. In dev you'll need to clear the cache after adding an annotation (php bin/console cache:clear); otherwise the procedure silently won't exist on the router.

Implementation Patterns

1. RPC services as thin adapters over domain services. Don't put business logic in the annotated method — keep the annotated class as a boundary that calls an injected domain service. The annotated method signature is fixed ($args, $kwargs, $session), and returning arrays that are the wrong shape is the most common source of confused clients.

class OrderWampService
{
    private $orderService;

    public function __construct(OrderService $orderService) { $this->orderService = $orderService; }

    /** @Register("com.example.order.status") */
    public function status($args, $kwargs, $session)
    {
        $order = $this->orderService->find($args[0]);
        return [['id' => $order->getId(), 'state' => $order->getState()]];
    }
}

2. Pub/sub for server→client fan-out. Subscribe-in and publish-out are both annotation-driven; the Topic object returned/held is how you push events. Broadcast on order state changes:

/** @Subscribe("com.example.order.created") */
public function onOrderCreated($args, $kwargs, $session)
{
    // handle inbound event
}

Publish outbound from anywhere with the topic object the bundle injects:

/** @Topic("com.example.order.updated") */
private $topic;

public function pushUpdate(Order $order)
{
    $this->topic->publish([$order->toArray()]);
}

3. Auth via session hooks. The bundle exposes SessionAuthenticateEvent / AuthorizationManager so you can gate realms and topics. Attach a listener that checks a token from the WAMP welcome details. This is where you centralize "can this session subscribe to com.example.order.updated".

4. Use an external Crossbar.io router for anything serious. The thruway:router command is fine for a single-node demo. Crossbar is the production-grade WAMP router; point the bundle's router config at it and stop worrying about router scaling/failover. The bundle then behaves as a client integration only.

5. Wire the Symfony event dispatcher both ways. The bundle bridges WAMP messages to kernel events. If you want to react to an application event with a broadcast, listen on your domain event and call $topic->publish(...) — don't build a parallel event system.

6. Laravel translation pattern. On Laravel you don't port this package; you port its topology. RPC → a controller/job endpoint; pub/sub → Broadcast::on()/channels with Reverb or a Pusher-compatible service; auth → Laravel broadcast channel authorization (Broadcast::channel). The one thing WAMP gives you that Laravel's stack doesn't natively is bidirectional RPC over the same connection — if you genuinely need that, use the voryx/thruway client directly inside an Octane worker behind a narrow internal interface, not this Symfony bundle.

7. Supervise the worker. Wrap thruway:router with systemd/supervisord. The event loop does not self-heal — a crashed router drops all sessions.

Gotchas and Tips

  • Symfony 2 only, last release July 2019, zero Packagist dependents. For a Laravel codebase, do not adopt — there is no integration path. This is the single most important day-to-day takeaway: reading it is cheap, owning it is expensive.
  • PHP version ceiling. A 2019 release resolves against PHP 7.x-era constraints. On PHP 8.1+ (Laravel 10/11, modern Symfony) composer require will likely fail outright, and forcing an install leaves unpatched transitive dependencies on a network-facing port.
  • Return-shape trap on RPC. Handlers must return an array of positional results. Returning a scalar silently produces a client-side [null]-ish result — always return [$value] or [['key' => $value]].
  • Annotation caching. Adding/renaming a @Register/@Subscribe/@Topic requires clearing the Symfony cache so the compiler pass re-discovers it. This is the top cause of "my procedure isn't there."
  • Long-running process memory. The Ratchet event loop accumulates references under sustained load. Set a supervised restart cadence and monitor RSS; do not assume the process runs indefinitely.
  • Silent message loss on reconnect. WAMP pub/sub is at-most-once per delivery; if a session drops mid-broadcast, messages are gone with no ack. If your use case needs delivery guarantees, layer application-level acknowledgement or persistence — the bundle won't do it for you.
  • Config quirks. router.ip/listen_port only matter when using the built-in thruway:router; if you point the bundle at an external Crossbar instance, those settings are inert and the URL lives in your client config. Don't waste a debugging afternoon assuming they're still in play.
  • Debugging. The cleanest debug loop is to add a Monolog channel to the router's logger service and watch thruway:router output directly — the Symfony web debug toolbar does nothing for a console worker. Confirm the router emits the WAMP WELCOME frame before chasing handler bugs.
  • Extension points that matter. SessionAuthenticateEvent (auth) and the realm config are the two you'll customize in real deployments. Everything else is glue you'd fork rather than configure if the dependency is frozen.
  • Don't let it spread. If you inherit a Symfony 2 app that uses this, wrap the RPC/pub-sub surface behind internal interfaces so you can migrate to Mercure (modern Symfony) or Reverb (Laravel) without touching domain code — the exit will be needed.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment