resque/php-resque
PHP port of Resque: a Redis-backed background job queue for PHP applications. Enqueue jobs, run forked workers across multiple queues, and process tasks asynchronously to keep web requests fast. Includes job classes, queue management, and worker controls. Lightweight and easy to integrate.
Install with Composer and point it at Redis before the first enqueue:
composer require resque/php-resque ext-redis # or predis/predis
require 'vendor/autoload.php';
Resque::setBackend('127.0.0.1:6379'); // host:port or host:port/db
// newer releases also accept: Resque::setBackend('redis://127.0.0.1:6379', 0, 'myapp');
Create a job class with a public perform(); arguments arrive on $this->args:
class SendWelcomeEmailJob
{
public function perform(): void
{
$user = User::find($this->args['user_id']);
Mail::to($user)->send(new WelcomeMail());
}
}
Enqueue it, then run a worker:
Resque::enqueue('mailer', SendWelcomeEmailJob::class, ['user_id' => 42]);
QUEUE=mailer INTERVAL=5 php vendor/bin/resque
Look first at vendor/bin/resque --help for the worker flags, Resque_Event for lifecycle hooks, and the Resque::enqueue() signature ($queue, $class, $args, $trackStatus) — those three areas cover 90% of day-to-day usage. The classic first use case is transactional email or image thumbnail generation: work that is slow, retryable, and non-blocking.
Tracking job status. Pass true as the fourth argument to get a token back; the status is stored in Redis until you delete it:
$token = Resque::enqueue('mailer', SendWelcomeEmailJob::class, $args, true);
$status = (new Resque_Job_Status($token))->get(); // QUEUED, RUNNING, COMPLETE, FAILED
Lifecycle hooks. Resque_Event is how you wire in logging, metrics, and retries without touching job classes:
Resque_Event::listen('onFailure', function ($e, $job) {
Log::error('resque.failed', [
'class' => $job->payload['class'],
'args' => $job->payload['args'],
'error' => $e->getMessage(),
]);
});
Resque_Event::listen('afterPerform', fn ($job) => Metrics::increment('jobs.ok'));
Available events include beforeFirstFork, beforeFork, afterFork, beforePerform, afterPerform, and onFailure. These are ideal for booting Laravel per-fork (see gotchas).
Retries. There is no built-in retry loop. The common pattern is a custom failure backend or an onFailure listener that re-enqueues with a delay (using the companion resque/php-resque-scheduler for enqueueIn/enqueueAt) and caps attempts with a counter stored in $job->payload.
Laravel integration. Boot Resque once in a service provider:
public function register(): void
{
$redis = config('database.redis.default');
Resque::setBackend("{$redis['host']}:{$redis['port']}", $redis['database'] ?? 0);
}
Then run the worker as its own process — do not start it from within the Laravel HTTP process. A small artisan command wrapping Resque_Worker works, or just invoke vendor/bin/resque directly under Supervisor. This keeps queue traffic on the same Redis instance but in the resque:* keyspace, so it coexists cleanly with Laravel's native queue driver (though they will not share jobs).
Worker orchestration. Run one worker process per queue under Supervisor; QUEUE=mailer,default handles multiple queues in priority order, and QUEUE=* for local dev only. Set INTERVAL=5 to avoid a busy Redis poll.
setUp() (runs in the child, before perform) and call DB::reconnect() / Redis::connection()->disconnect().perform()'s return value is ignored; any thrown Throwable triggers onFailure. Wrap risky work in try/catch if you want custom behavior.json_encoded into the payload. Pass scalars/arrays, not models or closures. Large payloads bloat Redis — pass an ID and reload.setUp() is per-job, not per-worker. Put expensive one-time bootstrap (config, autoloader) in the worker script, not setUp().$trackStatus, delete keys with Resque_Job_Status::delete($token) after you consume them or Redis will grow.vendor/autoload.php and bootstrap/app.php if jobs reference app services — otherwise you get Class not found at fork time.stdout_logfile, or register an onFailure handler that pipes into your app logger — otherwise failures vanish when the shell closes.SIGTERM waits for the current job; SIGQUIT kills immediately. Set Supervisor's stopwaitsecs above your longest job, and add RESQUE_MEMORY_LIMIT or a periodic restart to mitigate PHP memory growth in long-running workers.Dynamic property warnings and Throwable handling issues.setBackend(..., $database, 'namespace')) if you share a Redis instance across apps or across PHP and Ruby Resque — key collisions on resque:queue:* are silent and painful to debug.beforeFork/afterFork are your friend for instrumentation. Use them to open/close metrics connections so counters don't survive a broken fork.How can I help you explore Laravel packages today?