SIGNALS Documentation
API Reference

Plugin Hook Registry

Contract for accumulating plugin hook handler registrations and resolving them by hook name, type, and priority.

Overview

App\Sdk\Hooks\PluginHookRegistry is the in-memory registry of plugin hook handlers. Registrations accumulate at plugin boot and are consumed by App\Sdk\Hooks\PluginHookDispatcher.

The registry stores ordered PluginHookRegistration value objects. It does not invoke handlers, isolate failures, or soft-disable plugins — those behaviours live on the dispatcher.

Public surface

Method Purpose
register() Append one PluginHookRegistration
for() Return matching registrations for a hook name and HookType, priority ascending
hookNames() Return distinct hook names that have at least one registration of the given type
all() Return every accumulated registration in append order
flush() Clear all registrations

Accepted registration shape

Each entry is an immutable App\Sdk\Hooks\PluginHookRegistration:

new PluginHookRegistration(
    package: 'signals/xero-sync',
    hookName: 'account.updated',
    type: HookType::Event,
    priority: 50,
    handler: $callable,
)

App\Sdk\Hooks\HookType cases:

Case Value Dispatcher method
Event event dispatchEvent()
Filter filter applyFilters()
Validator validator runValidators()
Decorator decorator applyDecorators()

There is no keyed identity and no duplicate rejection. Multiple packages may register the same hook name and type; register() always appends.

Ordering and lookup behavior

for($hookName, $type) filters by exact hook name and type, then sorts by priority ascending (lower first). Equal priorities retain relative order from the filtered list after usort.

hookNames($type) returns distinct hook names that have at least one registration of that type, in first-seen append order.

all() returns the raw append-order list. flush() empties the registry (used between boots / tests).

Runtime consumption and failure isolation

PluginHookDispatcher is the runtime consumer:

  1. It resolves handlers through for($hook, $type) before each dispatch path
  2. A throwing handler is logged and skipped so core continues
  3. Consecutive failures are counted per package in cache key plugin-hooks:failures:{package}
  4. After config('plugins.hook_failure_threshold') (default 10) the package is soft-disabled
  5. Success resets the counter; slow handlers above plugins.hook_timeout_seconds log a warning but are not interrupted

The registry itself never catches exceptions or mutates plugin status.

Worked example

This is the current accumulation and lookup shape:

$registry->register(new PluginHookRegistration(
    package: 'signals/xero-sync',
    hookName: 'account.updated',
    type: HookType::Event,
    priority: 10,
    handler: $handler,
));

$registry->for('account.updated', HookType::Event); // priority-ascending list
$registry->hookNames(HookType::Event);             // ['account.updated', ...]

PluginHookDispatcher::dispatchEvent('account.updated', ...$payload) walks that for() result and invokes each handler under failure isolation.