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

Stomp Bundle Laravel Package

happyr/stomp-bundle

Symfony bundle that wraps the Stomp PHP client, adding configuration and service wiring for STOMP-compatible message brokers. It lets Symfony applications send messages to queues or topics on ActiveMQ, RabbitMQ, and similar messaging servers through a simple service.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the verdict before the code. This is a Symfony2-era bundle (registered via AppKernel::registerBundles(), wired with DependencyInjection/Configuration.php and Resources/config/services.xml) that wraps fusesource/stomp-php. It has one star, zero dependents, and no maintenance. You cannot composer require it into a Laravel app — there is no service provider, no config/ publish, no facade. So "getting started" means one of two things:

  • If you are on Laravel (per the brief): you are not installing this. You are reading it as a ~150-line reference spec, then writing your own provider.
  • If you genuinely inherit a Symfony 2/3 app: install and register as the bundle expects, then configure in app/config/config.yml:
happyr_stomp:
    broker:   "tcp://localhost:61613"   # ActiveMQ Classic / Artemis / RabbitMQ STOMP plugin
    username: "guest"
    password: "guest"

Where to look first, in order:

  1. composer.json — PHP floor and symfony/symfony constraint. If it pins ~2.x or PHP >=5.3, the decision is closed.
  2. DependencyInjection/Configuration.php — the only source of truth for config keys.
  3. Resources/config/services.xml — the client service id (likely happyr_stomp.client) and whether it's a shared singleton.
  4. The underlying FuseSource\Stomp\Stomp API — the bundle is a thin wrapper; your day-to-day calls are the library's.

First use case in Laravel terms — replicate what the bundle does with a provider binding a configured client, then consume:

// config/stomp.php
return [
    'broker'   => env('STOMP_BROKER', 'tcp://localhost:61613'),
    'username' => env('STOMP_USERNAME', 'guest'),
    'password' => env('STOMP_PASSWORD', 'guest'),
];
// app/Providers/StompServiceProvider.php
public function register(): void
{
    $this->app->singleton(\FuseSource\Stomp\Stomp::class, function ($app) {
        $c = $app['config']['stomp'];
        return new \FuseSource\Stomp\Stomp($c['broker'], $c['username'], $c['password']);
    });
}

That is essentially the whole package's value: "a pre-configured Stomp object injected from the container." Do not expect queues, retries, serialization, or dead-lettering.

Before any of this, run the two-day spike: an Artisan command connecting to the real broker over the real network (TLS and auth included). Confirm broker, port, and credentials before designing anything.

Implementation Patterns

Inject the client, not the container. Business code takes Stomp as a constructor dependency; only the provider knows how it was built.

class OrderNotifier
{
    public function __construct(private \FuseSource\Stomp\Stomp $stomp) {}

    public function notify(array $order): void
    {
        $this->stomp->send(
            '/queue/orders',
            json_encode($order),
            ['persistent' => 'true']
        );
    }
}

Producer pattern — wrap publishing behind your own interface. This is the single most valuable thing you take from the package: an application-owned MessagePublisher interface with a StompPublisher implementation. It keeps the transport swappable (to AMQP, to Redis) when you inevitably revisit the choice, and it keeps FuseSource\Stomp\* types out of your domain layer.

interface MessagePublisher { public function publish(string $destination, array $payload): void; }

Bind it in the provider and let OrderNotifier depend on the interface.

Consumer pattern — a supervised Artisan command with explicit ack. Do not hand-roll a while (true) into a controller. Put it in an Artisan command with explicit ack and a bounded lifetime.

class StompConsume extends Command
{
    protected $signature = 'stomp:consume {queue} {--max-time=3600}';

    public function handle(\FuseSource\Stomp\Stomp $stomp): int
    {
        $stomp->subscribe('/queue/'.$this->argument('queue'), ['ack' => 'client-individual']);
        $deadline = time() + (int) $this->option('max-time');

        while (time() < $deadline && $frame = $stomp->readFrame()) {
            try {
                ProcessOrder::dispatchSync(json_decode($frame->body, true));
                $stomp->ack($frame);
            } catch (\Throwable $e) {
                report($e);
                $stomp->ack($frame); // or nack/quarantine — you own this decision
            }
        }
        $stomp->unsubscribe();
        return self::SUCCESS;
    }
}

Workflow integration tips:

  • Use client-individual ack, not auto or plain client, so one bad message doesn't ack the batch behind it. The bundle gives you no dead-letter concept — you build it (re-publish to /queue/orders.failed with a retry counter header before acking).
  • Recycle workers. Schedule the consume command via Supervisor with --max-time (or a message counter) so PHP memory growth and stale connections are periodically reset.
  • Health check command. A tiny stomp:health Artisan command that connects and publishes a probe to /queue/health — run it on a schedule and alert on failure. This is the only reliable detector of "worker alive, connection dead."
  • Config via .env, mirroring the bundle's environment-aware config. One connection block per environment; no dynamic reload.
  • Serialize consistently. The bundle offers nothing here — pick JSON, version your payloads, and include a type header so consumers can route without decoding bodies they don't own.

Gotchas and Tips

The framework mismatch is structural, not cosmetic. Symfony bundles register via Bundle classes and DI extensions; Laravel's kernel never invokes those. Laravel uses Symfony components, not the Symfony framework. There is no adapter path — you reimplement, you don't port.

Composer will likely refuse it. Symfony 2.8 is EOL (Nov 2018); constraints will almost certainly block PHP 8.x. Check composer.json before spending further time. If it won't install, the decision is made for you.

The service is a shared singleton — and STOMP is connection-oriented. Long-lived processes hold one connection. After a broker restart, network partition, or idle timeout, the socket dies and readFrame() starts returning false in a loop. Your loop above exits on falsy frames; a naive while (true) loop spins at 100% CPU forever. Always bound the loop and treat "no frame" as a reconnect trigger.

Silent consumer death is the classic failure. Worker process healthy, connection dead, queue backing up invisibly. Without the health probe and queue-depth alerting, you find out from customers. Add both before production.

Message loss on ack. With auto ack, a crash mid-processing drops the in-flight message — and there's no dead-letter concept in this bundle. Use client-individual with explicit ack after success, and a quarantine destination for failures.

Poison messages loop forever. No retry counter exists. Read a retry-count header, increment, re-publish, and quarantine past N attempts.

TLS/credential rotation means worker restarts. No dynamic reload. Plan rotations as deploys, and make sure Supervisor stop signals let in-flight messages finish acking.

Debugging tips:

  • The bundle does almost nothing, so debug the client and the broker, not the bundle. Turn on the underlying library's debug/setReadTimeout to avoid blocking forever on an empty queue.
  • Test against a real broker, not a mock. STOMP's surface (destinations, ack modes, heartbeats, content-length framing) is exactly what a mock gets wrong.
  • Check destination conventions per broker: /queue/name vs /topic/name vs ActiveMQ's queue:// — a mismatch produces silent no-consumption, not an error.

Extension points are yours to invent. The bundle exposes a client; everything else (publisher interface, retry policy, DLQ, health check, serialization) is application code you own. Estimate from the package's scope: ~1–2 engineering days for a production-worthy adapter, plus a few days/year in steady state — unbounded during a PHP major upgrade.

The honest recommendation: don't adopt. If the broker allows AMQP, use a maintained Laravel queue driver and get retries, failed-job storage, and visibility for free. If STOMP is non-negotiable (ActiveMQ/Artemis already in the estate), skip this bundle and take the current stomp-php or enqueue/stomp directly behind your own interface — you inherit a maintained client's issues list instead of this one's silence. Keep this package as a checklist for what the adapter must cover, not as a dependency in your composer.json.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource