vaibhavpandeyvpz/qatar
Framework agnostic PHP library for publishing and consuming jobs using Redis and ElasticMQ backends.
Qatar (कतार)
Framework agnostic PHP library for publishing and consuming jobs using Redis and ElasticMQ/SQS backends.
Qatar:
कतार(Queue)
Features
- 🚀 Fast: Efficient job processing with Redis or ElasticMQ/SQS backends
- 🔄 Reliable: Automatic retries with configurable delays
- ⏰ Delayed Jobs: Schedule jobs to run in the future
- 👷 Multiple Workers: Run multiple workers concurrently
- 🎯 Simple API: Clean, intuitive interface for job management
- 🔧 Flexible: Framework agnostic - use with any PHP application
- 📝 Type-safe: Full PHP 8.2+ type hints and modern language features
- ⚡ Graceful Shutdown: Workers handle termination signals properly
- 💾 Two Backends: Choose between Redis (fast) or ElasticMQ/SQS (distributed).
- Note: Backend-specific drivers (
predisoraws-sdk-php) are suggested and must be installed separately.
- Note: Backend-specific drivers (
Requirements
- PHP 8.2 or higher
- Redis server + PhpRedis OR
predis/predis(forRedisQueue) - ElasticMQ or AWS SQS +
aws/aws-sdk-php(forElasticMQQueue) - JSON extension (
ext-json) for payload serialization (usually enabled by default)
Installation
Install via Composer:
composer require vaibhavpandeyvpz/qatarLocal Development Setup
For local testing, use Docker Compose to run Redis and ElasticMQ:
# Start services
docker-compose up -d
# Verify services are running
docker-compose ps
# Run tests
./vendor/bin/phpunitThis will start:
- Redis on
localhost:6379 - ElasticMQ on
localhost:9324(SQS-compatible API) - ElasticMQ UI on
localhost:9325(monitoring)
Quick Start
Creating a Job
Implementing Qatar\Job is simple:
<?php
use Qatar\Job;
class SendEmailJob extends Job
{
public function handle(array $payload): void
{
// $payload has data passed during push()
mail($payload['to'], $payload['subject'], $payload['body']);
}
}By extending Job, you get default implementations for:
retries(): Returns3delay(): Returns60secondsfailed(): Empty handler
Override them as needed:
class SendEmailJob extends Job
{
public function handle(array $payload): void { ... }
public function failed(\Throwable $exception, array $payload): void
{
// Handle permanent failure
}
public function retries(): int
{
return 5;
}
}Publishing Jobs
use Qatar\RedisQueue;
// Create a Redis client (Predis or PhpRedis)
$redis = new Predis\Client('tcp://127.0.0.1:6379');
// Create a queue instance
$queue = new RedisQueue($redis, 'emails');
// Basic push
$queue->push(SendEmailJob::class, [
'to' => 'user@example.com',
'subject' => 'Hello!',
'body' => 'Welcome to Qatar.',
]);
// Delayed push (5 minutes)
$queue->push(SendEmailJob::class, [...], delay: 300);Running Workers
use Qatar\Worker;
use Qatar\RedisQueue;
$redis = new Predis\Client('tcp://127.0.0.1:6379');
$queue = new RedisQueue($redis, 'emails');
$worker = new Worker($queue);
$worker->work();API Reference
Qatar\Queue Interface
| Method | Description |
|---|---|
push(string $job, array $payload, ?int $delay = null): string |
Add a job to the queue. Returns job ID. |
pop(?int $timeout = null): ?JobPayload |
Retrieve next job. Optionally wait for $timeout seconds. |
ack(string $id): bool |
Acknowledge successful completion of a job. |
nack(string $id, ?int $delay = null): bool |
Record a failure and schedule a retry. |
size(): int |
Get total number of pending and delayed jobs. |
purge(): void |
Clear all jobs from the queue. |
Qatar\Worker Class
The worker executes jobs from a queue. It is not final, so you can extend it to override resolveJob() for dependency injection.
use Qatar\Worker;
use Qatar\Job;
class ContainerWorker extends Worker
{
protected function resolveJob(string $jobClass, array $payload): Job
{
// Use your DI container here
return $this->container->get($jobClass);
}
}WorkerOptions
| Option | Default | Description |
|---|---|---|
sleep |
3 |
Seconds to wait when the queue is empty. |
maxJobs |
null |
Max number of jobs to process before stopping. |
maxTime |
null |
Max seconds to run before stopping. |
memory |
128 |
Memory limit in MB. Worker stops if exceeded. |
stopOnEmpty |
false |
If true, worker quits when queue is empty. |
Advanced Usage
Exponential Backoff
Implement custom backoff logic by using the attempts property in the payload:
class BackoffJob extends Job
{
public function handle(array $payload): void
{
// The attempt number is managed by the queue backend
}
public function retries(): int { return 5; }
public function delay(): int
{
// Custom logic here
return 60;
}
}Redis SSL/TLS Support
The RedisQueue supports secure connections over SSL/TLS. You can use the rediss:// or tls:// schemes in your connection string:
$queue = new RedisQueue('rediss://your-secure-redis-host:6379');When using ext-redis, the library automatically handles the tls:// prefix requirement for secure connections.
Signal Handling
Workers gracefully stop when they receive SIGTERM or SIGINT. They finish the current job before exiting.
Monitoring
- Redis: Use
redis-cli MONITORorLLEN qatar:default:ready. - ElasticMQ: Visit
http://localhost:9325for the stats UI.
Testing
Run tests with code coverage:
./vendor/bin/phpunitLicense
MIT License. See LICENSE for details.
Author
Vaibhav Pandey
- GitHub: @vaibhavpandeyvpz