PHPackages                             thesis/dispatcher - 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. thesis/dispatcher

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

thesis/dispatcher
=================

Thesis Dispatcher

0.2.0(1mo ago)112MITPHPPHP ^8.4CI passing

Since May 6Pushed 1mo agoCompare

[ Source](https://github.com/thesis-php/dispatcher)[ Packagist](https://packagist.org/packages/thesis/dispatcher)[ Fund](https://www.tinkoff.ru/cf/5MqZQas2dk7)[ RSS](/packages/thesis-dispatcher/feed)WikiDiscussions 0.2.x Synced 1w ago

READMEChangelog (2)Dependencies (5)Versions (4)Used By (0)

Thesis Dispatcher
=================

[](#thesis-dispatcher)

A lightweight hook dispatcher for PHP. Handlers subscribe to hook classes and are called when those hooks are dispatched.

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

[](#installation)

```
composer require thesis/dispatcher
```

Usage
-----

[](#usage)

### Subscribing and dispatching

[](#subscribing-and-dispatching)

```
use Thesis\Dispatcher;

final readonly class UserRegistered
{
    public function __construct(
        public string $email,
    ) {}
}

$dispatcher = new Dispatcher();

$dispatcher->subscribe(UserRegistered::class, function (UserRegistered $hook): void {
    echo "Welcome, {$hook->email}!\n";
});

$dispatcher->dispatch(new UserRegistered('user@example.com'));
// Welcome, user@example.com!
```

### Unsubscribing

[](#unsubscribing)

`subscribe()` returns an unsubscribe closure:

```
$unsubscribe = $dispatcher->subscribe(UserRegistered::class, function (): void {
    // ...
});

$unsubscribe(); // removed, will not be called on next dispatch
```

### Unsubscribing from within a handler

[](#unsubscribing-from-within-a-handler)

The handler receives an unsubscribe closure as its second argument — useful for one-shot handlers:

```
$dispatcher->subscribe(UserRegistered::class, function (UserRegistered $hook, \Closure $unsubscribe): void {
    sendWelcomeEmail($hook->email);
    $unsubscribe(); // run once, then remove self
});
```

### Multiple handlers

[](#multiple-handlers)

All handlers subscribed to the same hook are called in subscription order:

```
$dispatcher->subscribe(UserRegistered::class, $sendWelcomeEmail);
$dispatcher->subscribe(UserRegistered::class, $createDefaultSettings);
$dispatcher->subscribe(UserRegistered::class, $notifyAdmins);

$dispatcher->dispatch(new UserRegistered('user@example.com'));
// all three handlers are called in order
```

### Subscribing and unsubscribing during dispatch

[](#subscribing-and-unsubscribing-during-dispatch)

It is safe to subscribe or unsubscribe handlers while a hook is being dispatched. Handlers added during dispatch are called in the same pass; unsubscribed ones are skipped:

```
$dispatcher->subscribe(UserRegistered::class, function (UserRegistered $hook, \Closure $unsubscribe) use ($dispatcher): void {
    echo "first\n";
    $unsubscribe(); // removes itself — skipped on future dispatches, does not affect others

    $dispatcher->subscribe(UserRegistered::class, function (): void {
        echo "third\n"; // added mid-dispatch — still called in this same pass
    });
});

$dispatcher->subscribe(UserRegistered::class, function (): void {
    echo "second\n";
});

$dispatcher->dispatch(new UserRegistered('user@example.com'));
// first
// second
// third

$dispatcher->dispatch(new UserRegistered('user@example.com'));
// second
// third
```

Hook classes must be final
--------------------------

[](#hook-classes-must-be-final)

All hook classes must be declared `final` (enums are also accepted, as they are implicitly final).

The dispatcher matches hooks by exact class name. If inheritance were allowed, a handler subscribed to a parent class would silently not fire for subclass instances — which is confusing. Requiring `final` makes this contract explicit: one class, one set of handlers, no surprises.

It also nudges toward treating hooks as simple sealed value objects, which is the right model for them.

PSR-14
------

[](#psr-14)

This library deliberately does not implement [psr/event-dispatcher](https://packagist.org/packages/psr/event-dispatcher). A few reasons:

- The PSR calls them *events*; we prefer *hooks* — a subtly different mental model that better reflects push-based notifications rather than something that "happened" in the domain.
- `StoppableEventInterface` conflates stopping propagation with the event itself, which we consider a design smell. Stopping propagation is a dispatcher concern, not a data concern.
- PSR-14 adoption in the ecosystem is limited, so the interoperability argument is weak in practice.

If you do need PSR-14 compatibility, writing a thin adapter is straightforward.

License
-------

[](#license)

[MIT](LICENSE)

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance93

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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 ~0 days

Total

4

Last Release

33d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2552865?v=4)[Valentin Udaltsov](/maintainers/vudaltsov)[@vudaltsov](https://github.com/vudaltsov)

---

Top Contributors

[![vudaltsov](https://avatars.githubusercontent.com/u/2552865?v=4)](https://github.com/vudaltsov "vudaltsov (7 commits)")

### Embed Badge

![Health badge](/badges/thesis-dispatcher/health.svg)

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

###  Alternatives

[yeesoft/yii2-comments

Comments Module For Yii2 Framework

227.8k2](/packages/yeesoft-yii2-comments)

PHPackages © 2026

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