oeltimacreation / php-simplequeue
A lightweight, framework-agnostic background job queue system for PHP with Redis and database drivers
Package info
github.com/oeltimacreation/php-simplequeue
pkg:composer/oeltimacreation/php-simplequeue
Requires
- php: ^8.2
- psr/container: ^1.1|^2.0
- psr/log: ^2.0|^3.0
Requires (Dev)
- dealerdirect/phpcodesniffer-composer-installer: ^1.2
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.2.2
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^11.0
- predis/predis: ^3.0
- slevomat/coding-standard: ^8.29
- squizlabs/php_codesniffer: ^4.0
Suggests
- ext-pcntl: Required for graceful worker shutdown on Unix systems
- ext-pdo: Required for database queue driver
- predis/predis: Required for Redis queue driver (^3.0)
This package is auto-updated.
Last update: 2026-08-18 12:46:26 UTC
README
A small, lightweight, framework-agnostic PHP library for durable background job processing.
SimpleQueue is designed to be physically compact and easy to hold in one head while delivering enterprise-grade job guarantees. Storage is authoritative for job persistence, state transitions, and leases, while Redis or database polling acts as the delivery notification layer.
Key Features
| Feature | Description |
|---|---|
| Zero Heavy Dependencies | Requires only psr/container and psr/log. Redis and PDO remain optional. |
| Two-Layer Architecture | Authoritative storage (PDO / In-Memory) decoupled from notification drivers (Redis / DB / In-Memory). |
| Scheduled Dispatch | Delay initial job availability using dispatchAfter(), dispatchAt(), or $availableAt. |
| At-Least-Once Delivery & Fencing | Worker claims use worker IDs and lease tokens to fence completion, retry, and progress updates. |
| Idempotency & Deduplication | dispatchIdempotent() prevents duplicate active jobs for a given request ID. |
| Bounded Queue Repair | Built-in QueueReconciler detects lost notifications and repairs stale leases safely. |
| Middleware & Execution Context | Wrap handlers in ordered middleware with typed job identity, payload, queue, and attempt context. |
| Typed Worker Events | Stable readonly lifecycle event objects retain the existing string/array listener compatibility layer. |
| Failed-Job Administration | List, inspect, re-queue, and purge failed jobs through AdminManager. |
| PHP 8.2+ Modernization | Strict types everywhere (declare(strict_types=1)), readonly value objects, and PHPStan Level 9 strict compliance. |
Requirements
- PHP: 8.2 or later
- Database (Optional): PDO (MySQL, PostgreSQL, SQLite) for durable persistence
- Redis / Valkey (Optional): Redis 7+ or Valkey 8+ with
predis/predis:^3for Redis delivery
Installation
composer require oeltimacreation/php-simplequeue
# Optional: install Predis only if using the Redis queue driver
composer require predis/predis
Create the background_jobs table using the schema for your database in the database guide.
Quick Start
The fastest way to see the complete dispatch → process → inspect lifecycle is using the in-memory driver and storage:
<?php declare(strict_types=1); use Oeltima\SimpleQueue\Contract\JobHandlerInterface; use Oeltima\SimpleQueue\Driver\InMemoryQueueDriver; use Oeltima\SimpleQueue\JobDispatcher; use Oeltima\SimpleQueue\JobRegistry; use Oeltima\SimpleQueue\QueueManager; use Oeltima\SimpleQueue\Storage\InMemoryJobStorage; use Oeltima\SimpleQueue\Worker; use Oeltima\SimpleQueue\WorkerOptions; final class WelcomeEmailHandler implements JobHandlerInterface { public function handle(int $jobId, array $payload, ?callable $progress = null): mixed { if ($progress !== null) { $progress(percent: 100, message: 'Email sent'); } return ['recipient' => $payload['email']]; } } // 1. Initialize components $storage = new InMemoryJobStorage(); $queues = new QueueManager(driver: new InMemoryQueueDriver()); $registry = new JobRegistry(); $registry->register(type: 'email.welcome', handler: WelcomeEmailHandler::class); // 2. Dispatch a job $dispatcher = new JobDispatcher(storage: $storage, queueManager: $queues); $jobId = $dispatcher->dispatch(type: 'email.welcome', payload: ['email' => 'ada@example.test']); // 3. Process the job with a worker $workerOptions = WorkerOptions::fromArray(['lock_file' => null]); $worker = new Worker( storage: $storage, queueManager: $queues, registry: $registry, queue: 'default', options: $workerOptions, ); $worker->processOne(); // 4. Inspect job status echo $dispatcher->getStatus(jobId: $jobId)?->status->value; // 'completed'
For runnable examples with durable databases and Redis, see examples/.
Scheduled Dispatch
Delay a job's first availability using dispatchAfter(), dispatchAt(), or the $availableAt parameter:
// Dispatch 5 minutes into the future $jobId = $dispatcher->dispatchAfter( delaySeconds: 300, type: 'email.welcome', payload: ['email' => 'ada@example.test'], ); // Dispatch at a specific timestamp $jobId = $dispatcher->dispatchAt( timestamp: strtotime('tomorrow 09:00'), type: 'email.welcome', payload: ['email' => 'ada@example.test'], ); // Dispatch via optional availableAt parameter $jobId = $dispatcher->dispatch( type: 'email.welcome', payload: ['email' => 'ada@example.test'], availableAt: new DateTimeImmutable('+1 hour'), );
Quick API Reference
| Class | Key Methods | Description |
|---|---|---|
JobDispatcher |
dispatch(), dispatchAfter(), dispatchAt(), dispatchBatch(), dispatchIdempotent(), getStatus() |
Main entry point for enqueueing jobs and querying status. |
Worker |
run(), processOne(), withOptions() |
Worker loop executing jobs with signal handling and lease heartbeat. |
JobMiddlewareRegistry |
register(), all(), clear() |
Ordered worker middleware registration. |
QueueManager |
create(), redis(), database() |
Driver factory supporting auto-selection and driver resolution. |
JobRegistry |
register(), get(), has() |
Handler registry mapping job type strings to JobHandlerInterface classes. |
AdminManager |
listFailed(), inspectFailed(), requeueFailed(), purgeFailed() |
Failed-job and dead-letter operations. |
PdoJobStorage |
createJobs(), claimNextAvailable(), markCompleted(), markFailed(), scheduleRetry() |
Durable database persistence implementing JobStorageInterface. |
Documentation Index
- Getting Started — Durable database setup and worker setup
- Database Guide — Schemas, indexes, transactions, and idempotency
- Configuration — Driver auto-selection, polling, and worker options
- Operations — Deployment, supervisor configuration, monitoring, and repair
- Architecture — Two-layer model, lifecycle state machine, and lease fencing
- Extending — Custom handlers, storage implementations, and drivers
- Upgrading — Version upgrade instructions and migration guides
- Runnable Examples — Sample catalogue and benchmark scripts
Delivery Guarantees
SimpleQueue provides at-least-once delivery. Handlers must be written to be idempotent (e.g. using database transaction unique constraints or payment reference checks).
For cross-process request deduplication, use dispatchIdempotent() along with the unique request ID index described in the database guide.
Development & Quality Gates
composer check # Full quality check (tests, PHPStan Level 9, PHPCS, quality ratchet) composer test # Run PHPUnit test suite composer phpstan # Run static analysis composer cs-check # Run code style check composer cs-fix # Auto-fix code style issues
See CONTRIBUTING.md for contribution guidelines and LICENSE for license details.