Looking to hire Laravel developers? Try LaraJobs

laravel maintained by babelqueue

Description
Laravel adapter for BabelQueue: a drop-in polyglot queue driver. PHP produces strict JSON envelopes that Go, Python, Java, .NET and Node consumers read without PHP's serialize(). Built on babelqueue/php-sdk.
Last update
2026/06/06 11:22 (dev-main)
License
Downloads
0

Comments
comments powered by Disqus

BabelQueue for Laravel

CI Packagist License: MIT

Polyglot Queues, Simplified. A drop-in Laravel queue driver that produces a strict, language-agnostic JSON envelope — so Go, Python, Java, .NET and Node.js services can consume your jobs without PHP's serialize().

Laravel's native queue serialises jobs with PHP serialize(), producing an object graph only PHP can read. BabelQueue replaces just the serialization layer with a frozen JSON envelope and URN-based routing, over the broker you already run (Redis or RabbitMQ). No sidecar, no proxy, no broker plugin.

This is the PHP/Laravel SDK. The full cross-language standard and the canonical wire contract are documented at babelqueue.com.


Requirements

  • PHP ^8.2
  • Laravel ^11.0 | ^12.0
  • Redis or RabbitMQ

Installation

composer require babelqueue/laravel
php artisan vendor:publish --tag=babelqueue-config

Add a polyglot connection to config/queue.php:

'connections' => [
    'bq-redis' => [
        'driver'      => 'babelqueue-redis',
        'connection'  => 'default',   // an illuminate/redis connection
        'queue'       => 'default',
        'retry_after' => 90,
    ],

    // RabbitMQ alternative
    'bq-rabbit' => [
        'driver'        => 'babelqueue-rabbitmq',
        'host'          => env('RABBITMQ_HOST', '127.0.0.1'),
        'port'          => env('RABBITMQ_PORT', 5672),
        'user'          => env('RABBITMQ_USER', 'guest'),
        'password'      => env('RABBITMQ_PASSWORD', 'guest'),
        'vhost'         => env('RABBITMQ_VHOST', '/'),
        'queue'         => 'default',
        'exchange'      => '',
        'exchange_type' => 'direct',
    ],
],

The wire envelope

Every message is encoded as this frozen, schema_version: 1 envelope (full spec at babelqueue.com):

{
  "job": "urn:babel:orders:created",
  "trace_id": "7b3f9c2a-e41d-4f88-9b2a-1c0d5e6f7a8b",
  "data": { "order_id": 1042, "amount": 99.90 },
  "meta": { "id": "…", "queue": "default", "lang": "php", "schema_version": 1, "created_at": 1749132727000 },
  "attempts": 0
}
  • job — the message URN, never a class name. Convention: urn:babel:<context>:<event>.
  • trace_id — cross-service correlation id, preserved across every hop.
  • data — your pure-JSON payload.

Producing messages

Typed job (primary):

use BabelQueue\Contracts\ShouldQueuePolyglot;

final class CreateOrder implements ShouldQueuePolyglot
{
    public function __construct(private int $orderId, private float $amount) {}

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

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

CreateOrder::dispatch(1042, 99.90)->onConnection('bq-redis');

Facade (sugar):

use BabelQueue\Facades\BabelQueue;

BabelQueue::publish('urn:babel:orders:created', ['order_id' => 1042, 'amount' => 99.90]);

Continuing a trace from a handler? Implement BabelQueue\Contracts\HasTraceId on the downstream job (or pass the traceId argument) and the inbound trace_id is forwarded instead of a new one being minted.

Consuming messages

Map URNs to handlers in config/babelqueue.php:

'handlers' => [
    'urn:babel:orders:created' => \App\Consumers\OnOrderCreated::class,
],
final class OnOrderCreated
{
    // $data, $meta, $traceId and $message are injected by name.
    public function handle(array $data, array $meta, string $traceId): void
    {
        logger()->info('order created', ['id' => $data['order_id'], 'trace' => $traceId]);
    }

    // Optional: called once when retries are exhausted.
    public function failed(array $data, ?\Throwable $e): void
    {
        report($e);
    }
}

Run a worker against the polyglot connection like any other:

php artisan queue:work bq-redis

Unknown URNs

config/babelqueue.phpon_unknown_urn: fail (default) · delete · release · dead_letter.

Dead-letter queue (cross-language)

Enable in config/babelqueue.php:

'dead_letter' => [
    'enabled' => true,
    'suffix'  => '.dlq',   // failures from "orders" go to "orders.dlq"
],

Permanently-failed (and, with on_unknown_urn => 'dead_letter', unroutable) messages are republished to the DLQ as the same envelope plus an additive dead_letter block (reason, error, failed_at, original_queue, attempts, lang). Because the DLQ is an ordinary queue, any SDK can triage it.

Testing

composer install
vendor/bin/phpunit

Links

License

MIT © Muhammet Şafak. See LICENSE.