Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Event Laravel Package

webman/event

Lightweight event dispatcher for the webman framework. Register listeners, dispatch events, and organize event-driven logic with simple configuration. A small utility for decoupling components and adding hooks in webman applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require webman/event

Webman's composer plugin auto-discovers it; there is no bootstrap code to write. The whole package is a thin dispatcher (a few hundred lines at most), so if anything behaves oddly, open vendor/webman/event/src — you can read it end-to-end in one sitting.

Where to look first

  1. The event map. The plugin reads a plain PHP array of event name => list of listeners. Open the config file the plugin ships (under config/plugin/webman/event/, or config/event.php depending on the version — check what the plugin's src actually loads) and confirm it exists. If it doesn't, create it returning an empty array.
  2. The Webman\Event\Event class — one static emit() method, that's the entire public API.

First use case: user registration

// config/plugin/webman/event/event.php
return [
    'user.register' => [
        [app\event\user\SendWelcomeMail::class, 'handle'],
        [app\event\user\SeedDefaults::class, 'handle'],
    ],
];
// app/event/user/SendWelcomeMail.php
namespace app\event\user;

class SendWelcomeMail
{
    public function handle($user)
    {
        // $user is whatever you passed to emit()
    }
}
// anywhere in your controller / service
use Webman\Event\Event;

Event::emit('user.register', $user);

That's the full loop: register a dot-named event, point it at one or more [Class::class, 'method'] pairs, call Event::emit() with a single payload.

Implementation Patterns

Dot-namespaced event names. Treat event names like a routing table: user.register, order.paid, order.paid.post, plugin.shop.cart.cleared. Prefix by bounded context so two plugins never collide. The dispatcher does string matching, so the naming scheme is your only namespacing mechanism.

One listener class = one side effect. Don't build a "god listener". If user.register needs a welcome mail, a CRM sync, and an audit row, that's three classes in the config array. Order in the array is execution order, so put cheap/fast listeners first.

Fan-out is the whole point. The pattern that pays for itself: OrderService emits order.paid and never imports billing, inventory, or notification code. Each subsystem owns a listener. Adding a fourth reaction later is a config line, not a diff to OrderService.

// config
'order.paid' => [
    [app\event\order\MarkPaid::class, 'handle'],
    [app\event\order\DecrementStock::class, 'handle'],
    [app\event\order\NotifyCustomer::class, 'handle'],
],

Use it for cross-plugin communication. This is the real reason the plugin exists in the webman ecosystem. Plugin A shouldn't use a class from plugin B. Plugin A emits shop.checkout.completed; plugin B registers a listener in its own config. Neither depends on the other's code.

Keep payloads small and explicit. Pass a DTO or a model instance, not the whole $request. Since listeners receive a reference, every listener can mutate it — useful for "enrich the payload" chains, dangerous otherwise. If you rely on mutation, document the contract in the event name or a docblock; there are no typed event classes to enforce it.

Bridge to async work in the listener. Emissions are synchronous. A listener's job is usually to hand off:

public function handle($user)
{
    Redis::send('user-welcome-mail', ['id' => $user->id]); // webman/redis-queue
}

This gives you the decoupling of events with the throughput of a queue. Almost every "this made my request slow" bug in event-driven webman traces back to skipping this step.

Testing pattern. Events are just function calls — no framework harness needed. In a test, emit the event with a fixture payload and assert on side effects, or temporarily swap the config array to a spy listener that records invocations.

Debugging listener wiring. Add a one-line logging listener at the top of a suspicious event's array:

'order.paid' => [
    [app\event\Debug::class, 'log'],   // temporarily first
    // ...
],

Gotchas and Tips

Synchronous means blocking. Every listener runs inside the current request, in the current Workerman worker process. A 300 ms mail send inside user.register adds 300 ms to the response, and nothing else can run on that worker during it. This is the number one pitfall — offload anything with I/O latency to webman/redis-queue or webman/async.

Exceptions cascade. If listener #2 throws, listeners #3+ don't run and the exception propagates into your controller. Decide per listener whether failure should be fatal. For non-critical side effects (analytics, notifications), wrap the body in try/catch and log — otherwise a flaky SMTP server 500s your registration endpoint.

No return values, no propagation control. You can't cancel an event or collect results from listeners. If you need "run these in order and use each result", you want a pipeline, not this package. If you need veto semantics (e.g. before.save returning false), implement it with a shared mutable DTO passed as the payload.

Payload objects are shared by reference. Listener #1 mutating $order changes what listener #3 sees. Usually desirable, occasionally a nasty debugging session. Prefer passing immutable DTOs when the event is purely informational.

Restart the worker after config changes. In webman's resident-process model, the event map is loaded once at boot. Editing the config file does nothing until php start.php reload (or restart). New listener classes are usually picked up by PSR-4 autoloading, but if you rely on a classmap, run composer dump-autoload.

Missing listener methods fail at emit time, not boot time. Config is just an array — the dispatcher only resolves Class::class when the event fires. A typo in a class name surfaces on the first real request, not on startup. Consider a single "smoke test" command that emits every registered event with a dummy payload in staging.

Ordering is implicit. Listeners execute in array order, and there's no priority number. If order matters, comment it in the config — future editors will reorder the array alphabetically and break you.

Don't let memory accumulate. Workers stay alive. Avoid pushing to static arrays or container singletons inside listeners without clearing them; this leaks across requests and is invisible until the process OOMs.

No listeners is not an error. An event name with an empty array (or one never registered) emits silently. That's forgiving during development but means a renamed event quietly stops firing — add a Log::debug("event: $name") in your dev .env if you suspect that.

Extension point is the config, not the class. Because the dispatcher is so small, customization is done by wrapping: define your own Dispatcher facade that logs, filters, or re-emits, and call it from application code instead of Event::emit directly. Swapping the vendor class itself is rarely worth it for a package this size.

Pin the version. Last release was 2023-12-04 and the project is small (9 stars, no dependents). Treat it as stable-but-unmaintained: pin the exact version in composer.json so a future framework bump doesn't silently change behavior, and be prepared to vendor the ~200 lines if you ever need to patch it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console