laelaps/symfony-gearman-bundle
Symfony bundle that integrates the Gearman job queue into your application. Configure servers, register workers and clients as services, dispatch background jobs, and handle results through a clean, framework-native API.
This is a Symfony 2-era bundle (now tagged 1.0.0) that wires the PECL gearman extension into the Symfony container. Note the release's framing: the 1.x line is explicitly maintained for PHP 5.3 compatibility, and a 2.x branch with a different API is planned (likely PHP 5.5+). That tells you everything about the target environment — this is not a package you adopt for new PHP 8 work. Before touching PHP, make sure the infrastructure exists: a running gearmand and the ext-gearman PECL extension loaded in your SAPI/CLI.
Minimal setup:
# Pin to the 1.x line explicitly; 2.x will be an API break when it lands
composer require "laelaps/symfony-gearman-bundle:^1.0"
// app/AppKernel.php (Symfony 2 style)
$bundles = [
// ...
new Laelaps\Symfony\GearmanBundle\LaelapsGearmanBundle(),
];
# app/config/config.yml — illustrative; verify exact key/node names
laelaps_gearman:
servers:
- { host: 127.0.0.1, port: 4730 }
Where to look first, in this order:
DependencyInjection/Configuration.php — the authoritative config tree (server list, timeouts, service options). Don't trust blog posts; this file is the source of truth for the YAML shape.Resources/config/services.xml (or *.yml) — the actual service IDs for the client/worker. The README may reference slightly different IDs than what is registered.Command/ — any bundled console command for running workers. This is what you'll wire into Supervisor.README.md — for the intended producer/consumer workflow, and CHANGELOG/tags to confirm whether you're on 1.x or a 2.x pre-release.First use case to validate the integration end-to-end: dispatch a background job from a controller and consume it from a CLI worker. Something like:
// Producer (controller/service)
$client = $this->container->get('gearman.client'); // confirm ID in services.xml
$client->doBackground('app.report.generate', json_encode(['reportId' => $id]));
// Consumer (worker command or standalone script)
$worker = $this->container->get('gearman.worker');
$worker->addFunction('app.report.generate', function (\GearmanJob $job) {
$payload = json_decode($job->workload(), true);
// ... do work ...
$job->sendComplete('ok');
});
while ($worker->work()) {
if ($worker->returnCode() !== GEARMAN_SUCCESS) {
// log and decide whether to continue
}
}
If that round trip works, the bundle is integrated; everything else is application architecture.
Treat the bundle as a thin transport, not a job framework. It gives you client/worker services and (probably) a worker command. You own job names, payload schemas, retries, and observability.
Producer side (web requests):
app.report.generate, app.email.send, app.import.run. This makes gearadmin --status readable.doBackground() for fire-and-forget and doNormal()/doHigh()/doLow() when the caller needs a result or priority handling.{"v":1, ...}) so producers and consumers can evolve independently. Never rely on PHP serialize() of objects.Consumer side (CLI workers):
ReportJobHandler), then register the callback in a command. This keeps handlers unit-testable and lets you reuse them outside Gearman.sendData() for progress/intermediate data and sendComplete()/sendFail() to terminate cleanly.; supervisor example
[program:gearman-report-worker]
command=php app/console gearman:worker:run app.report.generate
numprocs=2
autorestart=true
stopwaitsecs=30
Integration tips:
services.xml, since this bundle predates modern autoconfiguration.gearmand -vvv in a terminal and dispatch against it; it's the fastest way to see job names and payloads.This is legacy software, and that's the biggest gotcha. The 1.0.0 release makes the situation explicit rather than fixing it: 1.x is frozen to a PHP 5.3-compatible codebase, which means Symfony 2.x-era APIs, no short-array/modern syntax guarantees, and no room for the bundle to adopt newer Symfony components. The PECL gearman extension itself is end-of-life and does not build cleanly (or at all) on modern PHP 7.4+/8.x. If you're on PHP 8, you cannot run this package's native Gearman calls without a fork or a different transport. Read composer.json constraints before planning anything.
Plan for the 1.x → 2.x split now. The release notes announce an incoming 2.x branch with a different API and (probably) a PHP 5.5 floor. Practical consequences:
"laelaps/symfony-gearman-bundle": "^1.0" (or an explicit tag) — a floating constraint will silently pull a 2.x release with breaking service IDs/config shape.Worker processes are long-running, so state rots.
EntityManager::clear() periodically.Gearman has no built-in retries, backoff, or dead-letter queue. If a worker throws, the job can be lost or silently requeued depending on timing and return codes. Implement retry counting in the payload (or a DB table), cap attempts, and route exhausted jobs to a failure queue/table. Make handlers idempotent—at-least-once delivery means duplicates happen.
Exceptions inside the callback are dangerous. An uncaught exception can kill the worker loop mid-job. Wrap handlers in try/catch, always call sendFail() or sendComplete(), log with the job handle, and let Supervisor restart the process.
Configuration quirks.
GearmanClient::addServer().DependencyInjection and Resources/config directories rather than trusting docs. This is doubly true across the 1.x/2.x boundary, where the notes warn of a different API.Debugging toolkit:
gearadmin --status / gearadmin --workers for queue depth and connected workers.gearman -f app.report.generate -- '{"reportId":1}' to invoke a job manually and bypass your producer.gearmand -vvv for wire-level logging.$job->handle()) in both producer and consumer so you can correlate dispatches with executions.Extension points: because the bundle is thin, the cleanest customization is to leave its services alone and wrap them in your own JobDispatcher and WorkerRunner services. Override service definitions in your app config only when you need to change connection behavior; otherwise keep your code independent so a future migration off this bundle (to Symfony Messenger, Enqueue, or a hosted queue) — or onto the announced 2.x line — is a swap of two classes rather than a rewrite.
How can I help you explore Laravel packages today?