Hook Architecture
Core object
The package is centered on Quantum\Hook\HookManager.
It keeps one internal property:
private array $store = [];
The store shape is:
array<string, array<int, callable>>
Each key is a registered hook name. Each value is the queued listeners for that hook.
Registration model
Hook names are created through the protected register() method.
Registration rules are simple:
- if the name already exists in
$store,register()throwsHookException::hookDuplicateName() - otherwise it creates an empty array for that hook name
Because register() is protected, package consumers cannot add hook names directly through the public API. Public use depends on whatever hook names were loaded during construction.
Listener lifecycle
on(string $name, callable $function) appends the callable to $store[$name][].
Before that, it checks exists($name). If the hook name is unknown, it throws HookException::unregisteredHookName().
There is no deduplication for listeners. The same callable can be queued multiple times.
Fire lifecycle
fire(string $name, ?array $args = null) also validates the hook name first.
It then iterates over the queued listeners for that hook:
foreach ($this->store[$name] as $index => $fn) {
unset($this->store[$name][$index]);
$fn($args);
}
This has two important consequences:
- each listener is removed before it runs
- firing the same hook again does nothing unless new listeners were added after the previous fire
The package therefore behaves more like a deferred callback queue than a reusable publish-subscribe bus.
Read access
getRegistered() returns the full internal store.
That includes registered hook names and their currently queued listeners. It is a snapshot of the manager state at the time of the call.
Error surface
The package defines two package-specific runtime errors:
- duplicate hook registration ->
HookException::hookDuplicateName() - missing hook name ->
HookException::unregisteredHookName()
The constructor can also surface dependency errors from config loading and DI resolution because it calls config() and may import config/hooks automatically.