Download the PHP package snipershady/queue-bridge without Composer

On this page you can find all versions of the php package snipershady/queue-bridge. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package queue-bridge

queue-bridge

English | Italiano

Latest Stable Version Total Downloads Monthly Downloads PHP Version License CI

Backend-agnostic message queue for PHP 8.3+: one typed API for Redis Streams and RabbitMQ, with retries, a dead letter queue, delayed messages and automatic Redis version detection. An InMemory backend with the same semantics serves tests and local development.

Switching to RabbitMQ changes one line: StrategyDtoRabbitMq::fromConnection($connection) instead of StrategyDtoRedis::fromPhpRedis($redis).

Contents

The full design documentation (queue concepts, UML diagrams, concurrency, worker scheduling) is in DOC.md.

Installation

Package page: packagist.org/packages/snipershady/queue-bridge · Source: github.com/snipershady/queue-bridge · Releases: CHANGELOG.md

Then install the client of the backend you use:

Backend Client
Redis ext-redis (recommended) or composer require predis/predis
RabbitMQ composer require php-amqplib/php-amqplib
InMemory nothing
Graceful shutdown on SIGTERM/SIGINT, handler timeout ext-pcntl

Requirements: PHP 8.3, 8.4 or 8.5; Redis >= 6.0 (Valkey and managed services reporting redis_version 7.2 work as Redis 7.0); RabbitMQ 3.13 or 4.x.

Redis, RabbitMQ or InMemory?

All three backends implement the same contract and pass the same contract tests: the application code does not change. They differ in how the guarantees are obtained and in what they cost to run.

Redis (Streams) RabbitMQ (quorum queues) InMemory
Persistence AOF (appendfsync everysec: up to ~1 s of writes lost if the server crashes) on disk, fsync before the publisher confirm none: lost when the process exits
High availability replica + Sentinel, or Redis Cluster (keys use hash tags) Raft replication across 3+ nodes —
Backlog size must fit in RAM on disk, beyond RAM process memory
Worker crash recovery after claimIdleMs (default 5 min) immediate, when the connection drops after claimIdleMs
Delayed messages exact (sorted set) wait queues with fixed tiers, precision ≈ one tier exact, driven by a PSR-20 clock
Poison message protection maxDeliveries maxDeliveries + broker x-delivery-limit maxDeliveries
Throughput (one process, batch publish / consume) ~115,000 / ~30,000 msg/s ~25,000 / ~6,000 msg/s ~330,000 / ~68,000 msg/s
Statistics ready, in flight, delayed, DLQ, lag (>= 7.0), consumers ready, in flight (this consumer), delayed, DLQ, consumers ready, in flight, delayed, DLQ, lag
Idempotency store RedisIdempotencyStore on the same server needs Redis or your own store InMemoryIdempotencyStore
Listing the queue (browse) in flight, ready and delayed, read-only ready messages only, read with basic.get + requeue in flight, ready and delayed
Typical use high throughput, low latency, Redis already in the stack durability and replication first, existing RabbitMQ unit tests, local development
Backend Advantages Disadvantages
Redis Highest throughput and lowest latency; exact delays; one component often already present (cache, sessions); idempotency store and queue on the same server; consumer lag available from 7.0; tested from 6.0 to 8.4 with version-specific optimizations Durability depends on the AOF configuration; the backlog must fit in memory and maxmemory-policy must be noeviction; a crashed worker's messages wait claimIdleMs before going to another worker; HA requires Sentinel or Cluster
RabbitMQ Strongest durability: replicated quorum queues and publisher confirms; unacknowledged messages come back immediately when a worker dies; the broker enforces a delivery limit even if the worker cannot; backlog larger than RAM; management UI and mature operations tooling Lower throughput per process; delays rounded to retry tiers; one more piece of infrastructure (three nodes for HA); heartbeat and consumer_timeout limit handler duration; no consumer lag statistic; idempotency needs an external store
InMemory No services; deterministic with a hand-moved clock (delays, retries and expirations without waiting); full semantics, it is the reference implementation of the contract; fastest Not persistent and not shared between processes: for tests and development only

Integration

QueueBridge::factory() returns a factory that receives one typed DTO per backend and a QueueConfig. Everything else (producer, worker, handlers, middleware) is the same on every backend.

Redis

The Redis version is detected from INFO server. If the ACL forbids INFO, pass it: StrategyDtoRedis::fromPhpRedis($redis, RedisVersionEnum::GE70); an override higher than the server throws VersionMismatchException in create().

RabbitMQ

StrategyDtoRabbitMq::fromConnection($connection) and fromPhpAmqpLib($channel) reuse an existing connection. Publisher confirms are always on: retry, reject and requeue acknowledge the original only after the broker has confirmed the copy, so they cannot be turned off. When you build the connection yourself, give it a channel_rpc_timeout (AMQPConnectionConfig::setChannelRPCTimeout()): without one, a network that silently drops packets makes stats() or deadLetters() wait forever (the DSN sets 10 s).

InMemory

From a DSN

Handy for configuration files and environment variables:

Backend Option Default Meaning
Redis client phpredis if ext-redis is loaded, else predis phpredis or predis
Redis version detected force a level: 6.0, 6.2, 7.0, 8.0, 8.2, 8.4
Redis verify_version true refuse an override higher than the server
Redis consumer random consumer name in the group
Redis timeout 2.0 connection timeout in seconds
RabbitMQ prefetch 10 basic_qos prefetch count
RabbitMQ tiers 1000,5000,30000,120000,600000,3600000 retry/delay tiers in ms
RabbitMQ heartbeat 0 heartbeat in seconds
RabbitMQ timeout 3.0 connection timeout in seconds
RabbitMQ rpc_timeout 10.0 maximum wait for a broker reply, in seconds (the socket read/write timeout is raised to match)

Unknown options are an error, so a typo (?verison=8.0) is not silently ignored; memory:// takes no host or option. A Redis user needs a password (an ACL user with nopass accepts any). Credentials must be URL-encoded. Boolean options accept 1/0, true/false, yes/no, on/off. The AMQP vhost defaults to /. Example: redis://app:secret@cache:6379/2?consumer=web-1&timeout=1.5.

Producing messages

Consuming with the Worker

The handler receives the Message (id, channel, raw text, attempts, headers, timestamps) and a DecodedText: $text->isJson(), $text->value (decoded JSON with objects as arrays, or the raw string) and $text->raw. You can also ignore it and decode $message->text yourself.

Handler outcome Result
returns ack
throws RetryableException (any Throwable with retryAnyThrowable: true) retried after the strategy delay (or the exception's delayMs); DLQ after maxAttempts
throws UnrecoverableMessageException; payload in an unknown format DLQ immediately
the worker crashes before the ack delivered again; DLQ after maxDeliveries deliveries
message expired (ttlMs) discarded, or DLQ with expiredMessagePolicy: ExpiredMessagePolicy::DeadLetter
no handler for the channel DLQ, or discarded with unknownChannelPolicy: UnknownChannelPolicy::Discard

Dead letters, statistics and health checks:

Listing and purging the queue itself:

Consuming without the Worker

To embed the queue in an existing daemon or event loop, use the transport directly and decide yourself what to do with each message:

Without the Worker you take over what it does for you: maxAttempts ($delivery->message->attempts), maxDeliveries ($delivery->deliveryCount), expiration ($delivery->message->expiresAt), signals and limits. $transport->release($delivery) gives a message back without counting a failure.

CLI

Command What it does
work --bootstrap=<file.php> runs a worker; the file returns a Closure(WorkerInterface): void that registers the handlers (example). Options: --batch-size, --timeout (ms), --max-messages, --max-runtime (s), --memory-limit (MB), --stop-when-empty
stats [--json] queue statistics
ping backend health check
dlq:list [--limit=20] [--json] dead letters with the failure reason
dlq:requeue [--limit=100 \| --id=<uuid>] requeue the dead letters (up to the limit) or one of them
dlq:purge --force delete every dead letter
queue:list [--limit=20] [--json] messages in the queue (in flight, ready, delayed) without consuming them
queue:purge --force delete the ready and delayed messages

The DSN can also come from the QUEUE_DSN environment variable; --strategy=redis|rabbitmq|in-memory is an optional check against the DSN scheme. -v/-vv print info/debug logs on stderr. An option that the command does not accept is a usage error (work --max-message=5 does not run with the default limit). work validates its options and the DSN before loading the bootstrap file; a bootstrap that throws is a usage error too. Exit codes: 0 success, 1 backend error, 2 usage error.

Run workers under a supervisor with --max-runtime and --memory-limit: the process exits cleanly and is restarted. Ready-made configurations: systemd and Supervisor.

Examples

The examples/ directory (left out of the Composer package) contains runnable scripts. Services and ports are those of docker-compose.yml (docker compose up -d --wait); REDIS_HOST, REDIS_PORT, AMQP_HOST, AMQP_PORT, AMQP_USER, AMQP_PASSWORD point them elsewhere.

File Shows Run
01-quick-start.php publish and consume, JSON and plain text php examples/01-quick-start.php
02-switch-backend.php the same code on every backend, only the DTO changes php examples/02-switch-backend.php memory\|redis\|predis\|rabbitmq
03-worker-bootstrap.php handlers for queue-worker work see the CLI section
04-redis.php reconnection, consumer name, Redis idempotency store, batch, delays, DLQ php examples/04-redis.php [predis]
05-rabbitmq.php connection factory, retry tiers, prefetch, retry through a wait queue, DLQ php examples/05-rabbitmq.php
06-in-memory-tests.php testing handlers with a hand-moved clock: delays, retries, expiration, DLQ php examples/06-in-memory-tests.php
07-manual-consume.php consuming without the Worker: receive, manual decoding, ack/retry/reject php examples/07-manual-consume.php memory\|redis\|predis\|rabbitmq
08-worker-in-code.php Worker started from PHP code: handler class with manual JSON decoding, middleware, metrics, signals, two queues php examples/08-worker-in-code.php memory\|redis\|predis\|rabbitmq

Sample payloads are in examples/deploy/.

Configuration

QueueConfig is the same on every backend:

Option Default Meaning
name default queue name
prefix rq prefix of Redis keys / RabbitMQ resources
maxTextBytes 1 MiB text size limit, in bytes
maxHeadersBytes 64 KiB headers size limit
maxAttempts 3 handler failures before the DLQ
maxDeliveries 10 deliveries (crashes included) before the DLQ; at least maxAttempts + 1
claimIdleMs 300000 Redis: after this idle time an unacknowledged message is taken from a crashed worker
dlqMaxLength 100000 DLQ retention (newest kept): approximate on Redis (MAXLEN ~), exact on RabbitMQ (x-max-length) and InMemory

Redis layout (hash tags keep the keys of a queue in one cluster slot): rq:{orders}:stream, rq:{orders}:delayed, rq:{orders}:dlq, rq:{orders}:idem:<id>; consumer group queue-bridge.

RabbitMQ topology created by setup(): topic exchange rq.orders (routing key = channel), quorum queue rq.orders with x-delivery-limit, wait queues rq.orders.retry.<ms>, classic DLQ rq.orders.dlq with x-max-length = dlqMaxLength (overflow drop-head: the oldest dead letters are dropped, a reject never fails because the DLQ is full).

Recommended server configuration

Redis

Without AOF a restart empties the queue; with an eviction policy Redis may delete queue keys under memory pressure. Use ACL users limited to the queue keys (~rq:*); if the ACL forbids INFO, pass the version explicitly (otherwise the library falls back to the 6.0 baseline and logs a warning).

RabbitMQ

Delivery guarantees and limits

Redis version support

The level is detected once from INFO server; each capability is a strategy chosen at creation, never a version check in the hot path.

Level Commands used Below this level
6.0 Streams, consumer groups, extended XPENDING, Lua scripts, sorted sets, SET NX PX —
6.2 XPENDING ... IDLE, XTRIM MINID client-side idle filter, no safety trim
7.0 XINFO GROUPS lag lag = null
8.0 same as 7.0
8.2 XACKDEL, XTRIM ... ACKED XACK + XDEL, MINID trim
8.4 XREADGROUP ... CLAIM, DELEX ... IFEQ orphans requeued by a Lua script, lock release in Lua

The main stream is never trimmed with MAXLEN (it would delete unread messages): acknowledged entries are deleted, and the trim is only a safety net that removes acknowledged entries. The CI runs the integration tests on Redis 6.0, 6.2, 7.0, 7.4, 8.0, 8.2 and 8.4 with ext-redis and Predis, and on RabbitMQ 3.13 and 4.x, with PHP 8.3, 8.4 and 8.5.

Benchmark

Indicative results: PHP 8.3 CLI with OPcache, Xdebug off, AMD Ryzen 9 9900X; servers in Docker on the same machine, reached through the published ports (Redis 8.4, RabbitMQ 4.x single node); 157-byte JSON payload, batches of 100, one process. "consume" includes receive, a no-op handler and the ack.

Backend publish publishBatch consume
InMemory 300,000 msg/s 327,000 msg/s 68,000 msg/s
Redis, ext-redis 3,000 msg/s 115,000 msg/s 30,600 msg/s
Redis, Predis 3,400 msg/s 95,500 msg/s 32,000 msg/s
RabbitMQ (publisher confirms) 1,100 msg/s 25,000 msg/s 6,000 msg/s

Redis levels forced on the same 8.4 server (ext-redis):

Level publishBatch consume
6.0 107,000 msg/s 32,200 msg/s
6.2 110,000 msg/s 29,400 msg/s
7.0 106,000 msg/s 30,000 msg/s
8.0 110,000 msg/s 30,700 msg/s
8.2 79,000 msg/s 27,400 msg/s
8.4 108,000 msg/s 33,300 msg/s

A single publish() is bound by the network round trip (one per message, plus the publisher confirm on RabbitMQ; here also by Docker's port forwarding): use publishBatch() for throughput. Consumption scales with the number of worker processes. The levels differ in the atomicity and round trips of maintenance operations (orphan recovery, trim), not in the hot path, so their numbers are within run-to-run noise.

Extending

A new backend (for example SQS) implements StrategyDtoInterface, TransportInterface (plus the optional capability interfaces it supports: setup, maintenance, stats, dead letters, batch ack, purge, browse) and TransportBuilderInterface, and is registered in the factory:

The core does not change. EnvelopeCodec converts messages to the versioned wire format, and the abstract TransportContractTestCase in the repository (tests/Contract) verifies the same semantics as the provided backends.

A custom client (company wrapper, framework facade) implements RedisClientInterface or AmqpConnectorInterface and is passed to the DTO constructor.

Development

See UPGRADE.md.

License

GPL-2.0-only.


All versions of queue-bridge with dependencies

PHP Build Version
Package Version
Requires php Version ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0
ext-json Version *
psr/clock Version ^1.0
psr/log Version ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package snipershady/queue-bridge contains the following files

Loading the files please wait ...