heristop/jobqueue-bundle
A Symfony bundle for running background jobs and managing queues. It offers a simple way to dispatch, process, and monitor asynchronous tasks, with configurable workers and Doctrine-backed job storage, ideal for offloading time-consuming work from web requests.
Know what you're installing first. heristop/jobqueue-bundle is a Symfony bundle, not a Laravel package. It's a thin DI wrapper that exposes Zend Framework's Zend\Queue component as container services plus a consumer command. There is no service provider, no facade, no config/queue.php driver, and no Laravel auto-discovery. composer require into a Laravel app will technically resolve (it's just a library) but gives you zero integration.
Minimal steps, if you're evaluating it:
DependencyInjection/Configuration.php — that's the real API surface. It tells you exactly which adapters (Array, Db, Memcache, Mongo) are supported and what options each takes.Resources/config/*.xml (or YAML) to get the service IDs you'd inject, and the Command/ directory to see the worker loop.Queue::fake() in spirit — perfect for proving your payload contract before touching a database.First use case: on a Symfony app, configure the Db adapter, push one JSON message, and run the consumer command in a supervisor process. In a Laravel codebase, the realistic first use is reference only: copy the producer/consumer pattern into a service provider and use laminas/laminas-queue (or ideally Illuminate\Queue) directly.
Version reality check before you start: last release was 2016-09-07. Expect PHP 5.6/7.0-era constraints, Symfony 2.x/3.x, and ZF2. On PHP 8 / Symfony 6+ this will not resolve without a fork and --ignore-platform-reqs.
Producer/consumer split is manual. The queue is a dumb buffer — it stores and returns strings. There is no handler registry, no job class resolution, no container-aware dispatch. You own the switch statement in the worker.
Payload envelope convention. Since messages are strings, standardize early:
$queue->send(json_encode([
'class' => App\Jobs\SendInvoice::class,
'payload' => $data,
'attempt' => 0,
]));
Then in the worker: json_decode → resolve class from the container → call handle($payload).
Ack discipline is the whole game. Only delete on success; let the visibility timeout requeue on failure:
while (true) {
$job = $queue->receive();
if (!$job) { sleep(1); continue; }
try {
handle(json_decode($job->body, true));
$queue->delete($job); // ack
} catch (\Throwable $e) {
// do nothing — visibility timeout re-delivers it
$logger->error($e->getMessage());
}
}
That timeout is your retry mechanism. Increment an attempt counter inside the payload yourself and give up after N.
Db adapter ≈ Laravel's database driver. One table, one row per message, timeout/received columns for visibility. It's the only sane default here if you need durability without adding infrastructure.
Bridge it behind an interface. If you're in a mixed Symfony/Laravel shop, define App\Contracts\JobQueue with push() and pop(), bind the Zend implementation in a service provider, and keep Illuminate\Contracts\Queue\Queue as the intended replacement. That one abstraction is the difference between a migration and a rewrite.
Don't share tables across frameworks. If Laravel workers and Symfony workers both need the queue, bridge via Redis or HTTP — not by having both ORMs write to the same queue table with different visibility semantics.
zendframework/zend-queue was superseded by laminas/laminas-queue, which is itself deprecated. Expect Packagist abandonment warnings and zero security patches.retry_after. Long-running jobs get re-delivered to a second worker while the first is still running. Set the timeout comfortably above your p99 job duration or you'll get duplicate execution, which is much worse than a slow retry.serialize() job objects — a deploy that changes a class shape will break every in-flight message. JSON only.Zend\Db, not Eloquent or Doctrine. That means a second connection, no shared transactions with your models, and a schema that lives outside Laravel's migration tooling.SIGTERM yourself and finish the in-flight message. Pair with a supervisor config the same way you'd run queue:work.delete() or an exception swallowed before the ack. Log immediately after receive() and immediately before delete(); the gap tells you everything.vendor/.Illuminate\Queue. This package earns its keep only as a reading reference for the producer/consumer pattern, or inside a legacy Symfony app you're keeping alive while you migrate.How can I help you explore Laravel packages today?