-
Notifications
You must be signed in to change notification settings - Fork 2
Listeners and Priorities
This page covers the mechanics shared by both APIs: what counts as a
listener, how event names are normalised, and the exact contract for
the $priority parameter.
Any value PHP recognises as a callable is a valid listener. Both
Events::on() and EventEmitter::on()/once() validate this with
is_callable() and throw \InvalidArgumentException otherwise.
use InitPHP\Events\Events;
// 1. Closure (most common)
Events::on('e', function ($x) { /* … */ });
// 2. Arrow function (PHP 7.4+)
Events::on('e', fn ($x) => doSomething($x));
// 3. Named function as a string
function my_listener($x) { /* … */ }
Events::on('e', 'my_listener');
// 4. Instance method
class Mailer {
public function send($user) { /* … */ }
}
$mailer = new Mailer();
Events::on('user.registered', [$mailer, 'send']);
// 5. Static method
class Audit {
public static function record($x) { /* … */ }
}
Events::on('e', [Audit::class, 'record']);
// or: Events::on('e', 'Audit::record');
// 6. Invokable object
class Webhook {
public function __invoke($event) { /* … */ }
}
Events::on('order.placed', new Webhook());Listeners can accept any number of parameters. The arguments passed
to trigger() / emit() are forwarded positionally via
call_user_func_array, so the listener simply lists them:
Events::on('order.placed', function (array $order, string $source, int $version) {
/* … */
});
Events::trigger('order.placed', $order, 'web', 2);If your listener declares fewer parameters than were supplied, PHP
silently ignores the extras. If it declares more, you will get a
TypeError (PHP 8+) or a warning + null for missing scalars.
| Return from listener |
Events::trigger() reaction |
EventEmitter::emit() reaction |
|---|---|---|
false (strict) |
Stops the chain; trigger() returns false
|
Ignored — the next listener still runs |
Anything else (null, true, string, object, …) |
Continue to the next listener | Continue to the next listener |
EventEmitter::emit() does not look at return values; it returns
void. If you need veto semantics, use the Events facade
or read Stopping Propagation.
Both APIs lowercase event names before they touch the internal registry. These all reference the same event:
Events::on('User.Registered', $a);
Events::on('user.registered', $b);
Events::on('USER.REGISTERED', $c);
Events::trigger('user.registered'); // fires $a, $b, $cChoose one convention and stick with it for readability. Dot-separated
lowercase (namespace.action) is common; underscored (user_registered)
or kebab (user-registered) work just as well — pick whatever your
codebase already does.
Empty string '' is technically a valid name (though not recommended).
null, integers, or other non-strings throw \InvalidArgumentException.
There are two rules, in this order:
- Listeners with a lower numeric priority fire first, regardless of when they were registered.
- Within the same priority bucket, listeners fire in registration order (FIFO).
The three convenience constants exist for readability — the names map importance onto position:
Event::PRIORITY_HIGH = 10 // most important → runs first
Event::PRIORITY_NORMAL = 100 // default
Event::PRIORITY_LOW = 200 // least important → runs lastEvents::PRIORITY_* are aliases of the Event::PRIORITY_* constants
with the same integer values. EventEmitter::on()'s default priority
is the integer 100 (= PRIORITY_NORMAL) — the constants are not
re-exposed on EventEmitter but the value matches.
use InitPHP\Events\Event;
use InitPHP\Events\Events;
Events::on('boot', fn () => print("low\n"), Event::PRIORITY_LOW); // 200
Events::on('boot', fn () => print("high-a\n"), Event::PRIORITY_HIGH); // 10
Events::on('boot', fn () => print("normal\n")); // 100, default
Events::on('boot', fn () => print("high-b\n"), Event::PRIORITY_HIGH); // 10
Events::trigger('boot');Output:
high-a
high-b
normal
low
-
high-aruns beforehigh-bbecause rule 2 (FIFO inside a priority) preserves their registration order. -
normalruns after bothhigh-*because its numeric priority is larger (rule 1). -
lowruns last for the same reason.
once() listeners are stored in a separate internal registry but the
dispatcher merges them into the same priority-sorted queue at dispatch
time. So a once registered at PRIORITY_HIGH runs before a regular
listener at PRIORITY_NORMAL, and is dropped after firing (or after
the chain halts — the once-contract is "fire at most once").
See Once-listeners, removal & cleanup (and the
Events facade
page) for the full once-contract.
The 1.x line of this package had a bug: EventEmitter::listeners()
applied ksort() to the wrong array level, so listeners ran in
registration order regardless of their $priority. The bug is
fixed in 2.0; the ordering described above is the contract from now on.
If your 1.x code happened to register listeners in ascending priority order (the obvious style), the visible behaviour does not change in 2.0. If you registered them in some other order, you'll see the order flip. Full details in the Migration Guide.
The same callable can be registered multiple times — it will fire as many times as it was attached. Re-registering does not de-duplicate:
$cb = function () { echo "tick\n"; };
Events::on('clock', $cb);
Events::on('clock', $cb);
Events::trigger('clock');
// → tick
// tickEvents::off() / EventEmitter::removeListener() removes all
instances of that callable across every priority slot in which it was
registered.
-
Stopping Propagation — the
return falsecontract onEvents::trigger(). -
Eventsfacade andEventEmitter— the two APIs that expose this behaviour. - API Reference — exact method signatures and exceptions.
- Migration Guide — what changed between 1.x and 2.0.
initphp/events · MIT License · part of the InitPHP family
Source · Issues · Discussions · Packagist · Contributing · Security Policy
Getting Started
Core APIs
Practical
Reference