PHPackages                             babelqueue/symfony - 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. [Caching](/categories/caching)
4. /
5. babelqueue/symfony

ActiveSymfony-bundle[Caching](/categories/caching)

babelqueue/symfony
==================

Symfony adapter for BabelQueue: a Messenger serializer that speaks the canonical polyglot envelope, so Symfony services interoperate with Laravel, Go, Python, .NET and Node over one wire format. Built on babelqueue/php-sdk.

v1.2.0(1mo ago)12MITPHPPHP ^8.2CI passing

Since Jun 6Pushed 1mo agoCompare

[ Source](https://github.com/BabelQueue/symfony)[ Packagist](https://packagist.org/packages/babelqueue/symfony)[ Docs](https://babelqueue.com)[ GitHub Sponsors](https://github.com/muhammetsafak)[ RSS](/packages/babelqueue-symfony/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (7)Dependencies (13)Versions (8)Used By (0)

BabelQueue for Symfony
======================

[](#babelqueue-for-symfony)

[![CI](https://github.com/BabelQueue/symfony/actions/workflows/ci.yml/badge.svg)](https://github.com/BabelQueue/symfony/actions/workflows/ci.yml)[![Packagist](https://camo.githubusercontent.com/80488ecbffa7cb83b14a84f5fffa73598c0d334609af9ff1a956d4836e522627/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f626162656c71756575652f73796d666f6e792e737667)](https://packagist.org/packages/babelqueue/symfony)[![License: MIT](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](LICENSE)

> **Polyglot Queues, Simplified.** A Symfony Messenger serializer that speaks the canonical BabelQueue envelope — so your Symfony services exchange messages with Laravel, Go, Python, .NET and Node over one strict JSON format, on the broker you already run.

This is the Symfony adapter. It plugs into **Symfony Messenger**: you keep Messenger's transports, handlers, worker and retry — BabelQueue only changes the **wire format** to the language-agnostic envelope (built by the shared core, [`babelqueue/php-sdk`](https://packagist.org/packages/babelqueue/php-sdk)). The full standard is documented at **[babelqueue.com](https://babelqueue.com)**.

Requirements
------------

[](#requirements)

- PHP `^8.2`
- Symfony `^6.4 | ^7.0` (Messenger)
- A broker Messenger supports (AMQP/RabbitMQ, Redis, …)

Installation
------------

[](#installation)

```
composer require babelqueue/symfony
```

Enable the bundle (if you don't use Symfony Flex) in `config/bundles.php`:

```
return [
    // ...
    BabelQueue\Symfony\BabelQueueBundle::class => ['all' => true],
];
```

Configuration
-------------

[](#configuration)

Point a Messenger transport at the BabelQueue serializer, and map inbound URNs to message classes:

```
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            babel:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'   # e.g. amqp:// or redis://
                serializer: 'babelqueue.messenger.serializer'
        routing:
            'App\Message\OrderCreated': babel
        buses:
            messenger.bus.default:
                middleware:
                    # Auto-forward trace_id from a handled message to any it
                    # dispatches, so a chain of work stays in one trace.
                    - 'babelqueue.messenger.trace_middleware'
```

```
# config/packages/babelqueue.yaml
babelqueue:
    queue: 'orders'            # written to the envelope meta.queue
    messages:                  # urn => message class (needed to consume)
        'urn:babel:orders:created': 'App\Message\OrderCreated'
```

Idempotency (deduplicate redeliveries)
--------------------------------------

[](#idempotency-deduplicate-redeliveries)

Brokers deliver **at least once**, so a worker can see the same logical message twice (a redelivery after a crash, a visibility-timeout expiry, a fan-out). Enable the idempotency middleware to deduplicate on the envelope's canonical `meta.id`, so a handler runs **once** per message. It's **opt-in and off by default** — leaving it disabled changes nothing.

```
# config/packages/babelqueue.yaml
babelqueue:
    idempotency:
        enabled: true          # opt-in; off by default
        store: ~               # service id of a BabelQueue\Idempotency\IdempotencyStore;
                               # null = a bundled in-memory store (single process / tests only)
        ttl: 3600              # in-flight claim TTL in seconds (only used by a ClaimingStore)
```

```
# config/packages/messenger.yaml — register it BEFORE your handlers on the
# consuming bus (alongside the trace middleware).
framework:
    messenger:
        buses:
            messenger.bus.default:
                middleware:
                    - 'babelqueue.messenger.idempotency_middleware'
                    - 'babelqueue.messenger.trace_middleware'
```

How it behaves, per inbound delivery (keyed on `meta.id`):

- **first delivery** → the handler runs once; on a clean return the id is recorded.
- **a duplicate** (same `meta.id` already recorded) → the handler is **skipped** and the message is acked, so the broker stops redelivering.
- **the handler throws** → the id is **not** recorded and the error propagates, so Messenger's retry / failure transport still apply and a later delivery runs again.
- **a message with no usable id** → handled unchanged (fail-open).

The default in-memory store is process-local — fine for a single worker or tests, but a **fleet** of workers must share one store. Point `store` at a service implementing `BabelQueue\Idempotency\IdempotencyStore` (from `babelqueue/php-sdk`), such as the persistent `PdoStore` (Postgres / MySQL / SQLite) or `RedisStore`:

```
# config/services.yaml
services:
    app.babelqueue.idempotency_store:
        class: BabelQueue\Idempotency\RedisStore
        arguments: ['@your.predis.client']
```

```
# config/packages/babelqueue.yaml
babelqueue:
    idempotency:
        enabled: true
        store: 'app.babelqueue.idempotency_store'
```

If the configured store implements `BabelQueue\Idempotency\ClaimingStore` (the persistent `PdoStore` / `RedisStore` do), the middleware uses an **atomic claim**: of N concurrent deliveries of the same id, exactly one runs the handler; the rest are **parked** (redelivered later, not acked) until the winner commits. `ttl` bounds a crash between claim and commit. This is still at-least-once, never exactly-once — keep handlers idempotent.

A message
---------

[](#a-message)

Implement `BabelQueue\Symfony\Contracts\PolyglotMessage`:

```
use BabelQueue\Symfony\Contracts\PolyglotMessage;

final class OrderCreated implements PolyglotMessage
{
    public function __construct(public int $orderId) {}

    public function getBabelUrn(): string
    {
        return 'urn:babel:orders:created';
    }

    public function toPayload(): array
    {
        return ['order_id' => $this->orderId];
    }

    public static function fromBabelPayload(array $data): static
    {
        return new self((int) $data['order_id']);
    }
}
```

Produce &amp; consume
---------------------

[](#produce--consume)

```
// produce — a normal Messenger dispatch
$bus->dispatch(new OrderCreated(1042));
```

On the wire it becomes the canonical envelope, readable by every BabelQueue SDK:

```
{
  "job": "urn:babel:orders:created",
  "trace_id": "…",
  "data": { "order_id": 1042 },
  "meta": { "id": "…", "queue": "orders", "lang": "php", "schema_version": 1, "created_at": 1749132727000 },
  "attempts": 0
}
```

```
// consume — a normal Messenger handler, routed by message class
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler]
final class OnOrderCreated
{
    public function __invoke(OrderCreated $message): void
    {
        // ...
    }
}
```

Run the worker as usual: `php bin/console messenger:consume babel`.

How it maps to Messenger
------------------------

[](#how-it-maps-to-messenger)

- **Routing** is Messenger's job: it routes the decoded message class to a handler.
- **Retry** bridges both ways — Messenger's `RedeliveryStamp` ⇄ the envelope's top-level `attempts`.
- **Tracing** — the inbound `trace_id` is attached as a `BabelTraceStamp`. With `babelqueue.messenger.trace_middleware` on the bus (see config above), any message a handler dispatches **automatically** inherits that `trace_id`, so a whole chain stays in one trace. A message that pins its own `BabelTraceStamp` or implements `HasTraceId` keeps its explicit id.
- **Unknown URN** — a message whose URN isn't mapped throws `MessageDecodingFailedException`, so Messenger routes it to your failure transport (the idiomatic Symfony behavior).
- **Idempotency** — with `babelqueue.messenger.idempotency_middleware` on the bus (opt-in, see above), a redelivery of the same `meta.id` is acked without re-running the handler. The id is surfaced on decode as a `BabelMessageIdStamp`.

Testing
-------

[](#testing)

```
composer install
vendor/bin/phpunit
```

License
-------

[](#license)

MIT © Muhammet Şafak. See [LICENSE](LICENSE).

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance93

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 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.

###  Release Activity

Cadence

Every ~2 days

Total

7

Last Release

34d ago

Major Versions

v0.3.0 → v1.0.02026-06-07

### Community

Maintainers

![](https://www.gravatar.com/avatar/4b6b34f3ac8938d8ee52ba3bd260680855dc5715c7b2929d9380de30d15a67dd?d=identicon)[muhammetsafak](/maintainers/muhammetsafak)

---

Top Contributors

[![muhammetsafak](https://avatars.githubusercontent.com/u/104234499?v=4)](https://github.com/muhammetsafak "muhammetsafak (11 commits)")

---

Tags

babelqueuebroker-agnosticdistributed-tracinginteroperabilityjsonmessage-queuemessagingmessengermicroservicesphppolyglotqueuerabbitmqredisserializationsymfonysymfony-bundlesymfony-messagerjsonsymfonyqueueMessengermicroservicespolyglot

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/babelqueue-symfony/health.svg)

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

PHPackages © 2026

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