frodeborli / gjallarhorn
Event emitter functionality with on(), off(), once() for subscribing to events.
Requires
- frodeborli/skuld: ^1.0
- frodeborli/themis: ^1.1
- psr/log: ^3.0
This package is auto-updated.
Last update: 2024-12-04 04:10:14 UTC
README
Subscribe to, and emit events. Supports both asynchronous events using PHP 8 Fibers and synchronous events.
Usage
Add the EventEmitter
interface and use the EventEmitterTrait
to any class
that emits events.
Event emitter class
use Gjallarhorn\{
EventEmitter,
EventEmitterTrait
};
/**
* These annotations allow you to provide better type hinting in your IDE. Change the `Event $event` according
* to your requirements. In this case we're using PHP 8.1 enums (see below for more information).
*
* @method void on(MatchEvent $event, Closure ...$listener) Subscribe to an event
* @method void off(MatchEvent $event, Closure ...$listener) Unsubscribe from an event
* @method void once(MatchEvent $event, Closure ...$listener) Subscribe to only the next invocation of this event
*/
class FootballMatch implements EventEmitter {
use EventEmitterTrait;
/**
* This function sends off a synchronous event message. If this
* event is emitted from inside an asynchronous event listener,
* then this event will effectively also be asynchronous.
*/
public function triggerGoalEvent(int $player_number, bool $homeTeam) {
return $this->trigger(MatchEvent::GOAL, $player_number, $homeTeam);
}
/**
* This function emits the event and returns a Promise object which
* can be resolved later by calling `$promise->wait()`.
*/
public function triggerGoalEventAsync(int $player_number, bool $homeTeam) {
return $thsi->triggerAsync(MatchEvent::GOAL, $player_number, $homeTeam);
}
}
Subscribing and unsubscribing to events
Adding listeners to an event source object is easy:
/**
* An event handler
*/
function handle_goal_scores(...$args) {
// handle the event
}
/**
* Subscribing to goal scores
*/
$match->on(SomeType::GOAL_SCORED, handle_goal_scores(...));
/**
* Unsubscribing
*/
$match->off(SomeType::GOAL_SCORED, handle_goal_scores(...));
/**
* Subscribe only to the next goal
*/
$match->once(SomeType::GOAL_SCORED, function(int $id, DateTimeInterface $time) {
// The next event
});
Asynchronous event handlers
Events can be triggered asynchronously. In that case the EventEmitterTrait::trigger()
function
will return a Promiselike
object (which is any object having a method with a signature compatible
with function then($onFulfilled, $onRejected)
.
TIP To handle various promise flavors you may consider using the
RaceTrack\Promise::promisify()
method. This method will return aRaceTrack\Promise
instance with all the functionality you need to work with asynchronous code.
Running asynchronous event listeners
<?php
use Gjallarhorn\{
EventEmitter,
EventEmitterTrait
};
enum MyEvent: string {
case GOOD_EVENT = 'GOOD_EVENT';
case BAD_EVENT = 'BAD_EVENT';
}
/**
* @method void on(MyEvent $event, Closure ...$listener) Subscribe to an event
* @method void off(MyEvent $event, Closure ...$listener) Unsubscribe from an event
* @method void once(MyEvent $event, Closure ...$listener) Subscribe only to the next invocation of the event
*/
class SomeClass implements EventEmitter {
use EventEmitterTrait;
public function triggerGoodEvent(): void {
$promise = $this->triggerAsync(MyEvent::GOOD_EVENT, $this);
echo "This happens before all events have been resolved\n";
$results = $promise->await();
}
}
$instance = new SomeClass();
$instance->on(MyEvent::GOOD_EVENT, function(SomeClass $emitter) {
// Sleep for 2 seconds while allowing other code to progress
Fiber::suspend(Promise::sleep(2));
echo "I've slept for 2 seconds\n";
});
$instance->triggerGoodEvent();