hoa/event
Hoa\Event is a lightweight PHP event library for decoupled, event-driven code. Attach listeners to named events, fire them with data buckets, and use listenable sources to route notifications cleanly across your application. Part of the Hoa project.
hoa/event is not a Laravel package — no service provider, no facade, no config file, no Blade/directive integration. It is a standalone PHP 5-era event/messaging micro-library that you composer require hoa/event:^1.0 and instantiate by hand. There is nothing to publish and nothing to register in config/app.php.
Where to look first (in this order):
README.md — the only docs that exist; the Hoa project docs site is the "real" manual.Hoa/Event/Event.php — the event object; getEventId(), getSource(), getBucket().Hoa/Event/Bucket.php — the object actually handed to every listener. This is the API you will touch in 90% of your listener closures: getData() / setData() / getSource().Hoa/Event/Listenable.php + Hoa/Event/Listener.php — the on() / getListener() / fire() contract that sources implement.Minimal first use case — a local, in-process hook inside one of your own classes, wrapped so it doesn't leak into the rest of the app:
// composer require hoa/event:^1.0
use Hoa\Event\{Bucket, Event, Listenable, Listener};
final class OrderHookPoint implements Listenable
{
private Listener $listener;
public function __construct()
{
$this->listener = new Listener(); // check ctor signature in Listener.php
}
public function on($eventId, $listener) { return $this->listener->on($eventId, $listener); }
public function getListener($eventId = null) { return $this->listener->getListener($eventId); }
public function fire(Event $event, array $o = []) { return $this->listener->fire($event, $o); }
}
$hooks = new OrderHookPoint();
$hooks->on('order:placed', function (Bucket $bucket) {
$data = $bucket->getData();
$bucket->setData($data + ['audited' => true]); // visible to the next listener
});
Honest framing for a Laravel team: this buys you nothing that Illuminate\Contracts\Events\Dispatcher doesn't already give you — and Laravel's dispatcher gives you priorities, subscribers, wildcards, queued/broadcast events, and Event::fake() in tests. 0 dependents, archived since 2017‑08‑30. Reach for it only if you are porting legacy Hoa code (hoa/stream, hoa/websocket) and need API parity, not for new work.
Bucket-as-payload (the one genuinely distinct idea). Listeners don't get a cloned value object; they all receive the same Bucket and mutate its data. Later listeners see earlier mutations. This is a fold/pipe, not a notification fan-out — model it explicitly, e.g. a "normalise → validate → enrich → persist" chain, and document that order matters.
Adapter to the Laravel dispatcher. If you must keep Hoa semantics while living in a Laravel app, keep the boundary thin: inside the Hoa listener, translate to a Laravel event and event(new ...). Do not let Hoa event IDs escape the adapter class — one file, one direction.
Fire-and-forget inside a service. Use it as a plugin seam for classes you own (a parser, an exporter) where you want third parties to hook into a fire() call without depending on the container. Constructor-inject the Listenable wrapper so you can swap it for a fake in tests.
Bridge to existing Laravel events. Wrap Illuminate\Events\Dispatcher behind the Hoa interface rather than the reverse: implement Listenable::on() as $dispatcher->listen($eventId, $listener). This keeps new code on Laravel's scheduler and lets you delete the Hoa shim later without touching call sites.
Naming convention. Hoa source IDs use hoa://event/...-style strings; follow a dotted convention of your own (order:placed, order:saved) and centralise the constants. There is no string-matching or wildcard engine here, so typos fail silently — everything is keyed by exact array index.
Xcallable coercion quirks, or hoa/consistency transitive constraints that can pin your other Hoa packages. Run the full suite on your target PHP before committing.fire() on an event ID with no listeners simply does nothing and returns no error. Add a debug helper that dumps getListener() contents when an expected hook doesn't run — this is the debugging trap.Listenable has no priorities. Registration order is execution order. If order matters, either sort at the call site or keep listeners idempotent; there's no $priority argument to lean on.Listener accumulate closures forever; there is no forget()/removeListener() in the public contract. Rebuild the wrapper per request/job, or cap registrations.Event::fake() will not intercept these. You must hand-roll a spy listener (a closure that appends to an array) or inject a stub Listenable. Budget for that in any test plan.Hoa\Event\Listenable (or compose Hoa\Event\Listener) and delegate. There's no container binding, no config/event.php, no facade accessor — which also means no accidental coupling if you keep the wrapper final and narrow.on($id, callable) and fire(Event) are trivially mappable to Dispatcher::listen() / Dispatcher::dispatch(), write the wrapper first and treat hoa/event as an implementation detail from day one. That is the only way this dependency stays removable.How can I help you explore Laravel packages today?