PHPackages                             tomaj/hermes - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. tomaj/hermes

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

tomaj/hermes
============

Simple php background processing library

5.0.0(7mo ago)38261.2k↓20.6%11[10 issues](https://github.com/tomaj/hermes/issues)5MITPHPPHP ^7.4 || ^8.0CI failing

Since Oct 28Pushed 3mo ago6 watchersCompare

[ Source](https://github.com/tomaj/hermes)[ Packagist](https://packagist.org/packages/tomaj/hermes)[ Docs](https://github.com/tomaj/hermes)[ RSS](/packages/tomaj-hermes/feed)WikiDiscussions master Synced yesterday

READMEChangelog (7)Dependencies (23)Versions (41)Used By (5)

Hermes
======

[](#hermes)

**Background job processing PHP library**

[![Latest Stable Version](https://camo.githubusercontent.com/5f533a5706cb87e4c5a103cd0d60a52131f27d9833d79d8ffec14350229f8ec0/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f746f6d616a2f6865726d65732e737667)](https://packagist.org/packages/tomaj/hermes)[![PHPStan](https://camo.githubusercontent.com/d117944b58da8146f96b4ef7403807610a20eeb3fbcaaaf95157bbcdad1686eb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230382d627269676874677265656e2e737667)](https://phpstan.org/)

What is Hermes?
---------------

[](#what-is-hermes)

Hermes is a lightweight PHP library for background job processing. When you need to handle time-consuming tasks outside of HTTP requests—such as sending emails, calling external APIs, or processing data—Hermes provides a clean, efficient solution.

Key features:

- **Multiple queue backends**: Support for Redis, RabbitMQ, Amazon SQS, and more
- **Simple integration**: Easy to add to existing projects with minimal setup
- **Extensible architecture**: Create custom drivers and handlers for your specific needs
- **Production-ready**: Built-in support for priorities, retries, and graceful shutdown

Installation
------------

[](#installation)

This library requires PHP 7.4 or later.

The recommended installation method is via Composer:

```
$ composer require tomaj/hermes
```

Library is compliant with [PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md), [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md), [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) and [PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).

Optional Dependencies
---------------------

[](#optional-dependencies)

Hermes supports logging through any PSR-3 compatible logger. For more information, see [psr/log](https://github.com/php-fig/log).

While the library works without logging, we recommend installing [monolog](https://github.com/Seldaek/monolog) for production environments to track message processing and debugging.

Supported Drivers
-----------------

[](#supported-drivers)

Hermes includes built-in support for multiple queue backends:

- **[Redis](https://redis.io/)** - Two implementations available: [phpredis](https://github.com/phpredis/phpredis) (native extension) or [Predis](https://github.com/nrk/predis) (pure PHP)
- **[Amazon SQS](https://aws.amazon.com/sqs/)** - AWS Simple Queue Service integration
- **[RabbitMQ](https://www.rabbitmq.com/)** - Industry-standard message broker
- **[ZeroMQ](https://zeromq.org/)** - Available as a separate package: [tomaj/hermes-zmq-driver](https://github.com/tomaj/hermes-zmq-driver)

**Note:** You need to install the corresponding client libraries for your chosen driver. For example, to use Redis with Predis, add `predis/predis` to your `composer.json` and configure your Redis connection.

Concept - How Hermes Works
--------------------------

[](#concept---how-hermes-works)

Hermes acts as a message broker between your web application and background workers. Here's the flow:

```
+--------------------------------------------------------+
|         Web Application (HTTP Request)                |
|                                                        |
|  /file.php --> emit(Message) --> Hermes Emitter       |
+------------------------+-------------------------------+
                         |
                         v
                  +-------------+
                  |    Queue    |
                  | Redis/Rabbit|
                  +------+------+
                         |
                         v
+--------------------------------------------------------+
|        Background Worker (PHP CLI)                    |
|                                                        |
|  Dispatcher --> wait() --> Handler::handle()          |
|                                                        |
|  * Continuously listens for new messages              |
|  * Calls registered handler to process each message   |
+--------------------------------------------------------+

```

Implementation steps:

1. **Choose a driver**: Select a queue backend (Redis, RabbitMQ, etc.) and register it with the Dispatcher and Emitter
2. **Emit messages**: Send messages to the queue when you need background processing
3. **Create handlers**: Write handler classes to process your messages
4. **Run the worker**: Create a PHP CLI script that runs continuously to process messages from the queue

How to Use
----------

[](#how-to-use)

This example demonstrates using the Redis driver to send emails in the background.

### Emitting Messages

[](#emitting-messages)

Emit messages from anywhere in your application—it's quick and straightforward:

```
use Redis;
use Tomaj\Hermes\Message;
use Tomaj\Hermes\Emitter;
use Tomaj\Hermes\Driver\RedisSetDriver;

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$driver = new RedisSetDriver($redis);
$emitter = new Emitter($driver);

$message = new Message('send-email', [
	'to' => 'test@test.com',
	'subject' => 'Testing hermes email',
	'message' => 'Hello from hermes!'
]);

$emitter->emit($message);
```

### Processing Messages

[](#processing-messages)

To process messages, create a PHP CLI script that runs continuously. Here's a simple implementation with a handler:

```
# file handler.php
use Redis;
use Tomaj\Hermes\Driver\RedisSetDriver;
use Tomaj\Hermes\Dispatcher;
use Tomaj\Hermes\Handler\HandlerInterface;

class SendEmailHandler implements HandlerInterface
{
    // here you will receive message that was emitted from web application
    public function handle(MessageInterface $message)
    {
    	$payload = $message->getPayload();
    	mail($payload['to'], $payload['subject'], $payload['message']);
    	return true;
    }
}

// create dispatcher like in the first snippet
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$driver = new RedisSetDriver($redis);
$dispatcher = new Dispatcher($driver);

// register handler for event
$dispatcher->registerHandler('send-email', new SendEmailHandler());

// at this point this script will wait for new message
$dispatcher->handle();
```

To keep the worker running continuously on your server, use a process manager like [supervisord](http://supervisord.org), [upstart](http://upstart.ubuntu.com/), [monit](https://mmonit.com/monit/), or [god](http://godrb.com/).

Logging
-------

[](#logging)

Hermes supports any PSR-3 compliant logger. Set a logger for the Dispatcher or Emitter to track message flow and handler execution.

To enable logging in your handlers, add the `Psr\Log\LoggerAwareTrait` trait (or implement `Psr\Log\LoggerAwareInterface`)—the Dispatcher and Emitter will automatically inject the logger.

Example using [monolog](https://github.com/Seldaek/monolog):

```
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// create a log channel
$log = new Logger('hermes');
$log->pushHandler(new StreamHandler('hermes.log'));

// $driver = ....

$dispatcher = new Dispatcher($driver, $log);
```

To add logging within your handlers:

```
use Redis;
use Tomaj\Hermes\Driver\RedisSetDriver;
use Tomaj\Hermes\Dispatcher;
use Tomaj\Hermes\Handler\HandlerInterface;
use Psr\Log\LoggerAwareTrait;

class SendEmailHandlerWithLogger implements HandlerInterface
{
    // enable logger
    use LoggerAwareTrait;

    public function handle(MessageInterface $message)
    {
        $payload = $message->getPayload();

        // log info message
    	$this->logger->info("Trying to send email to {$payload['to']}");

    	mail($payload['to'], $payload['subject'], $payload['message']);
    	return true;
    }
}
```

Retry
-----

[](#retry)

If your handler fails, you can automatically retry by adding the `RetryTrait` to your handler class. Override the `maxRetry()` method to control the number of retry attempts (default is 25).

**Note:** Retry functionality requires a driver that supports delayed execution (the `$executeAt` message parameter).

```
declare(strict_types=1);

namespace Tomaj\Hermes\Handler;

use Tomaj\Hermes\MessageInterface;

class EchoHandler implements HandlerInterface
{
    use RetryTrait;

    public function handle(MessageInterface $message): bool
    {
        throw new \Exception('this will always fail');
    }

    // optional - default is 25
    public function maxRetry(): int
    {
        return 10;
    }
}
```

Priorities
----------

[](#priorities)

You can configure multiple queues with different priority levels to ensure high-priority messages are processed first.

Example with Redis driver:

```
use Tomaj\Hermes\Driver\RedisSetDriver;
use Tomaj\Hermes\Emitter;
use Tomaj\Hermes\Message;
use Tomaj\Hermes\Dispatcher;

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$driver = new RedisSetDriver($redis);
$driver->setupPriorityQueue('hermes_low', Dispatcher::DEFAULT_PRIORITY - 10);
$driver->setupPriorityQueue('hermes_high', Dispatcher::DEFAULT_PRIORITY + 10);

$emitter = new Emitter($driver);
$emitter->emit(new Message('type1', ['a' => 'b'], Dispatcher::DEFAULT_PRIORITY - 10));
$emitter->emit(new Message('type1', ['c' => 'd'], Dispatcher::DEFAULT_PRIORITY + 10));
```

Key points about priorities:

- Use priority constants from the `Dispatcher` class or any numeric value
- Higher numbers indicate higher priority
- You can pass an array of queue names to `Dispatcher::handle()` to create workers that process specific queues

Graceful Shutdown
-----------------

[](#graceful-shutdown)

Hermes workers can be gracefully stopped without losing messages.

When you provide an implementation of `Tomaj\Hermes\Shutdown\ShutdownInterface` to the `Dispatcher`, Hermes checks `ShutdownInterface::shouldShutdown()` after each message. If it returns `true`, the worker shuts down cleanly.

**Important:** Hermes handles shutdown, but automatic restart must be managed by your process controller (e.g., supervisord, systemd, or Docker).

Two shutdown implementations are available:

### SharedFileShutdown

[](#sharedfileshutdown)

Trigger shutdown by creating or touching a specific file:

```
$shutdownFile = '/tmp/hermes_shutdown';
$shutdown = Tomaj\Hermes\Shutdown\SharedFileShutdown($shutdownFile);

// $log = ...
// $driver = ....
$dispatcher = new Dispatcher($driver, $log, $shutdown);

// ...

// shutdown can be triggered be calling `ShutdownInterface::shutdown()`
$shutdown->shutdown();
```

### RedisShutdown

[](#redisshutdown)

Trigger shutdown by setting a Redis key:

```
$redisClient = new Predis\Client();
$redisShutdownKey = 'hermes_shutdown'; // can be omitted; default value is `hermes_shutdown`
$shutdown = Tomaj\Hermes\Shutdown\RedisShutdown($redisClient, $redisShutdownKey);

// $log = ...
// $driver = ....
$dispatcher = new Dispatcher($driver, $log, $shutdown);

// ...

// shutdown can be triggered be calling `ShutdownInteface::shutdown()`
$shutdown->shutdown();
```

Scaling Hermes
--------------

[](#scaling-hermes)

Hermes can easily scale to handle high message volumes. Simply run multiple worker instances—either on the same machine or distributed across multiple servers.

Requirements for scaling:

1. **Network-capable driver**: Your driver must support remote connections (Redis, RabbitMQ, and Amazon SQS all support this)
2. **At-most-once delivery**: Each message should be delivered to only one worker

Both Redis and RabbitMQ drivers satisfy these requirements and are designed for high-throughput scenarios.

Extending Hermes
----------------

[](#extending-hermes)

Hermes uses interface-based architecture, making it easy to extend. You can create custom drivers, use different loggers, or implement your own message serialization.

### Creating a Custom Driver

[](#creating-a-custom-driver)

Each driver must implement `Tomaj\Hermes\Driver\DriverInterface` with two methods: `send()` and `wait()`.

Here's an example driver using [Gearman](http://gearman.org/):

```
namespace My\Custom\Driver;

use Tomaj\Hermes\Driver\DriverInterface;
use Tomaj\Hermes\Message;
use Closure;

class GearmanDriver implements DriverInterface
{
	private $client;

	private $worker;

	private $channel;

	private $serializer;

	public function __construct(GearmanClient $client, GearmanWorker $worker, $channel = 'hermes')
	{
		$this->client = $client;
		$this->worker = $worker;
		$this->channel = $channel;
		$this->serializer = $serialier;
	}

	public function send(Message $message)
	{
		$this->client->do($this->channel, $this->serializer->serialize($message));
	}

	public function wait(Closure $callback)
	{
		$worker->addFunction($this->channel, function ($gearmanMessage) use ($callback) {
			$message = $this->serializer->unserialize($gearmanMessage);
			$callback($message);
		});
		while ($this->worker->work());
	}
}
```

### Creating a Custom Serializer

[](#creating-a-custom-serializer)

To use custom serialization, create a class that implements `Tomaj\Hermes\SerializerInterface`. Add the `Tomaj\Hermes\Driver\SerializerAwareTrait` to your driver to enable the `setSerializer()` method.

Example using [jms/serializer](http://jmsyst.com/libs/serializer):

```
namespace My\Custom\Serializer;

use Tomaj\Hermes\SerializerInterface;
use Tomaj\Hermes\MessageInterface;

class JmsSerializer implements SerializerInterface
{
	public function serialize(MessageInterface $message)
	{
		$serializer = JMS\Serializer\SerializerBuilder::create()->build();
		return $serializer->serialize($message, 'json');
	}

	public function unserialize($string)
	{
		$serializer = JMS\Serializer\SerializerBuilder::create()->build();
		return $serializer->deserialize($message, 'json');
	}
}
```

### Scheduled Execution

[](#scheduled-execution)

Since version 2.0, you can schedule messages for future execution by passing a timestamp as the fourth parameter to the `Message` constructor. Currently supported by `RedisSetDriver` and `PredisSetDriver`.

Upgrade Guide
-------------

[](#upgrade-guide)

### From v3 to v4

[](#from-v3-to-v4)

**Breaking Changes:**

- **Renamed Restart → Shutdown** to better reflect functionality. Hermes can gracefully stop its own process, but restarting must be handled by an external process manager.
    - `RestartInterface` → `ShutdownInterface`
    - All implementation classes and namespaces have been updated accordingly

Changelog
---------

[](#changelog)

See [CHANGELOG](CHANGELOG.md) for a detailed list of changes and version history.

Testing
-------

[](#testing)

```
$ composer test
```

### Code Coverage

[](#code-coverage)

To generate code coverage reports:

```
# Generate coverage reports locally
$ composer coverage
# or use the helper script
$ ./coverage.sh
```

The coverage reports will be generated in:

- **HTML report**: `build/coverage/index.html` (open in browser to see line-by-line coverage)
- **Clover XML**: `build/logs/clover.xml` (for CI/CD integration)

**Online Coverage Reports**: Coverage reports are automatically published to GitHub Pages after each successful test run on the main branch.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) and [CONDUCT](CONDUCT.md) for details.

Security
--------

[](#security)

If you discover any security-related issues, please email  instead of using the issue tracker.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

59

—

FairBetter than 98% of packages

Maintenance67

Regular maintenance activity

Popularity46

Moderate usage in the ecosystem

Community28

Small or concentrated contributor base

Maturity80

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 91.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~153 days

Recently: every ~438 days

Total

25

Last Release

226d ago

Major Versions

0.4.0 → 1.0.02016-09-02

1.3.1 → 2.0.02018-10-17

2.2.2 → 3.0.02020-10-13

2.2.3 → 4.0.02021-02-02

4.2.0 → 5.0.02025-11-19

PHP version history (5 changes)0.1.0PHP &gt;= 5.4.0

1.3.0PHP &gt;= 5.5.0

2.0.0PHP &gt;= 7.1.0

3.0.0PHP &gt;= 7.2.0

5.0.0PHP ^7.4 || ^8.0

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/446736?v=4)[Tomas Majer](/maintainers/tomaj)[@tomaj](https://github.com/tomaj)

---

Top Contributors

[![tomaj](https://avatars.githubusercontent.com/u/446736?v=4)](https://github.com/tomaj "tomaj (216 commits)")[![lulco](https://avatars.githubusercontent.com/u/9377319?v=4)](https://github.com/lulco "lulco (7 commits)")[![markoph](https://avatars.githubusercontent.com/u/6843562?v=4)](https://github.com/markoph "markoph (4 commits)")[![burithetech](https://avatars.githubusercontent.com/u/3502143?v=4)](https://github.com/burithetech "burithetech (3 commits)")[![rootpd](https://avatars.githubusercontent.com/u/812909?v=4)](https://github.com/rootpd "rootpd (3 commits)")[![gitter-badger](https://avatars.githubusercontent.com/u/8518239?v=4)](https://github.com/gitter-badger "gitter-badger (1 commits)")[![ricco24](https://avatars.githubusercontent.com/u/1409647?v=4)](https://github.com/ricco24 "ricco24 (1 commits)")[![cursoragent](https://avatars.githubusercontent.com/u/199161495?v=4)](https://github.com/cursoragent "cursoragent (1 commits)")

---

Tags

background-jobsmessagemessage-brokereventbackgroundworkers

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tomaj-hermes/health.svg)

```
[![Health](https://phpackages.com/badges/tomaj-hermes/health.svg)](https://phpackages.com/packages/tomaj-hermes)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.8k543.8M20.1k](/packages/laravel-framework)[grumpydictator/firefly-iii

Firefly III: a personal finances manager.

23.9k69.5k](/packages/grumpydictator-firefly-iii)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

564576.7k51](/packages/ecotone-ecotone)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M574](/packages/shopware-core)[dagger/dagger

Dagger PHP SDK

261.1k](/packages/dagger-dagger)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
