--- title: "Transports" description: "Optional framework-less reference transports for the PHP core — RedisTransport, AmqpTransport, SqsTransport, KafkaTransport, PulsarTransport and StompTransport (Artemis) — plus the one-method Transport seam." source: https://babelqueue.com/docs/php-sdk/1.x/transports/ updated: 2026-06-15T00:00:00.000Z --- # Transports For a **framework-less** PHP producer, the core ships optional reference `Transport` implementations covering **all seven brokers** — Redis, RabbitMQ, Amazon SQS, Apache Kafka, Apache Pulsar and Apache ActiveMQ/Artemis. (Azure Service Bus is the one broker without a PHP transport — [ADR-0021](/docs/spec/1.x/broker-bindings/#azure-service-bus).) They keep the core dependency-free — install only the broker client you use. ## The Transport seam `BabelQueue\Contracts\Transport` is a single method: ```php interface Transport { public function publish(string $payload, ?string $queue = null): ?string; } ``` `$payload` is the `EnvelopeCodec::encode(...)` output. Any broker client can implement it — phpredis (`ext-redis`) users, for example, can do it in one line (`rpush`). ## RedisTransport ```bash composer require predis/predis ``` ```php use BabelQueue\Codec\EnvelopeCodec; use BabelQueue\Transport\RedisTransport; $transport = new RedisTransport(new Predis\Client('redis://localhost:6379')); $envelope = EnvelopeCodec::fromJob($job, 'orders'); $transport->publish(EnvelopeCodec::encode($envelope), 'orders'); // a Go / Python / Node consumer reads the identical envelope off "orders" ``` `RedisTransport` produces with `RPUSH`, matching the reliable-queue pattern the other SDKs use. ## AmqpTransport ```bash composer require php-amqplib/php-amqplib ``` ```php use BabelQueue\Transport\AmqpTransport; $transport = new AmqpTransport(/* php-amqplib connection */); $transport->publish(EnvelopeCodec::encode($envelope), 'orders'); ``` `AmqpTransport` declares a durable queue, publishes persistent messages, and sets the contract AMQP properties (`type` = URN, `correlation_id` = `trace_id`, `message_id` = `meta.id`, plus `x-schema-version` / `x-source-lang` / `x-attempts`), so consumers can route on properties without parsing the body. ## SqsTransport ```bash composer require aws/aws-sdk-php ``` A produce-side Amazon SQS transport: it sends the canonical envelope as the `MessageBody` with the [§3 `MessageAttributes`](/docs/spec/1.x/broker-bindings/#amazon-sqs). It is decoupled from the AWS SDK behind a one-method `BabelQueue\Transport\SqsClient` seam, so wrap a real `Aws\Sqs\SqsClient` in a one-line adapter: ```php use BabelQueue\Transport\SqsClient; use BabelQueue\Transport\SqsTransport; $adapter = new class (new Aws\Sqs\SqsClient([/* region, credentials */])) implements SqsClient { public function __construct(private Aws\Sqs\SqsClient $c) {} public function sendMessage(array $args): mixed { return $this->c->sendMessage($args); } }; $transport = new SqsTransport($adapter, 'https://sqs.eu-central-1.amazonaws.com/123456789012/orders'); $transport->publish(EnvelopeCodec::encode($envelope)); // a Go / Python / Node / Java / .NET consumer reads the identical envelope + attributes ``` The projected attributes (`bq-job` = URN, `bq-trace-id`, `bq-message-id`, plus `bq-schema-version` / `bq-source-lang` / `bq-created-at`) let a consumer route without decoding the body; FIFO sets `MessageGroupId` / `MessageDeduplicationId`. ## KafkaTransport Apache Kafka (§6) is PHP's one **opt-in** transport: its only viable client is the `ext-rdkafka` C extension, so it deliberately relaxes the zero-extension rule ([ADR-0019](/docs/spec/1.x/broker-bindings/#apache-kafka)). The transport stays decoupled behind the one-method `BabelQueue\Transport\KafkaProducer` seam, so you wrap your real `RdKafka\Producer` in a one-line adapter: ```bash pecl install rdkafka # the ext-rdkafka extension ``` ```php use BabelQueue\Transport\KafkaProducer; use BabelQueue\Transport\KafkaTransport; $producer = new class (/* RdKafka\Producer */) implements KafkaProducer { public function produce(string $topic, string $payload, array $headers, ?int $timestampMs = null): void { // produce to $topic with the bq-* headers + record timestamp } }; $transport = new KafkaTransport($producer, 'orders'); $transport->publish(EnvelopeCodec::encode($envelope), 'orders'); // a Java / Go / Node / Python / .NET consumer reads the identical envelope off the topic ``` The record **value** is the canonical envelope; the contract fields ride `bq-` **record headers** (`bq-job` = URN, `bq-trace-id`, `bq-message-id`, `bq-schema-version`, `bq-source-lang`, `bq-attempts` — hyphens, per §6), and the record timestamp mirrors `meta.created_at`. ## PulsarTransport Apache Pulsar (§5) has no mature native PHP client, so PHP produces over Pulsar's native **WebSocket API** with a pure-PHP WebSocket client — **GR-7 stays intact** (no C extension, [ADR-0020](/docs/spec/1.x/broker-bindings/#apache-pulsar)). It is decoupled behind the one-method `BabelQueue\Transport\PulsarWebSocketClient` seam: ```bash composer require textalk/websocket ``` ```php use BabelQueue\Transport\PulsarTransport; use BabelQueue\Transport\PulsarWebSocketClient; $client = new class (/* textalk/websocket client */) implements PulsarWebSocketClient { public function publish(string $topic, string $payload, array $properties): void { // base64 the payload into a WS producer frame with the bq-* properties; check the ack } }; $transport = new PulsarTransport($client, 'orders'); // tenant/namespace default to public/default $transport->publish(EnvelopeCodec::encode($envelope), 'orders'); // the BabelQueue "queue" maps to persistent://public/default/orders ``` The message **value** is the canonical envelope; `bq-` native **properties** (string→string, hyphens) carry the contract fields. The body's `meta.created_at` stays authoritative — the adapter must not set the frame's `eventTime`. ## StompTransport (Artemis) Apache ActiveMQ **Artemis** (§7) speaks AMQP 1.0, which has no strong PHP client, so PHP reaches it over **STOMP** (the mature pure-PHP `stomp-php` client; Artemis ships a STOMP acceptor) — **GR-7 intact** ([ADR-0018](/docs/spec/1.x/broker-bindings/#apache-activemq-artemis)). Artemis bridges STOMP ↔ AMQP 1.0 ↔ JMS on the same address, so a STOMP-produced message is consumed natively by the Java (JMS) and .NET / Node / Python / Go (AMQP 1.0) SDKs: ```bash composer require stomp-php/stomp-php ``` ```php use BabelQueue\Transport\StompClient; use BabelQueue\Transport\StompTransport; $client = new class (/* Stomp\Client */) implements StompClient { public function send(string $destination, string $body, array $headers): void { // SEND the envelope JSON to the anycast address with the bq_ headers } }; $transport = new StompTransport($client, 'orders'); $transport->publish(EnvelopeCodec::encode($envelope), 'orders'); ``` Routing is **body-authoritative** (a STOMP header can't set the `x-opt-jms-type` annotation, so consumers fall back to the body's `job` URN, which is always present). The §7 fields ride `correlation-id` (= `trace_id`) and the **`bq_`** application properties — note the **underscores** (JMS forbids hyphens in property names — [ADR-0017](/docs/spec/1.x/broker-bindings/#apache-activemq-artemis)). > Consuming with the core. These transports are **producers**. PHP consumes Kafka and > Pulsar with the framework-less [`KafkaConsumer` / `PulsarConsumer`](/docs/php-sdk/1.x/consuming-messages/), > and Artemis with the Laravel [`babelqueue-artemis`](/docs/laravel/1.x/apache-activemq-artemis/) > drop-in driver. > On Laravel or Symfony you don't need these — the [Laravel driver](/docs/laravel/1.x/introduction/) > and the [Symfony serializer](/docs/symfony/1.x/introduction/) move the bytes through > the framework's own transport.