Simple, fully functional event management scheduling implementation
- Implements the Psr 14 - event dispatcher
- Support for adding multiple listeners to an event
- Support for setting event priority
- Support for fast event group registration
- Support for quick event group monitoring based on event name
- eg Trigger
app.run
,app.end
will also trigger theapp.*
event
- eg Trigger
- Support for monitoring of wildcard events
- by composer require
composer require inhere/event
- by
composer.json
{
"require": {
"inhere/event": "^1.0"
// "inhere/event": "dev-master"
}
}
The event dispatcher, also known as the event manager.
Event registration, listener registration, and dispatcher (triggering) are all managed by it.
use Inhere\Event\EventManager;
$em = new EventManager();
listener can be:
- function name
- a closure
- a class(There are many ways)
// ...
$em->attach(Mailer::EVENT_MESSAGE_SENT, 'my_function');
// ...
$em->attach(Mailer::EVENT_MESSAGE_SENT, function(Event $event) {
// $message = $event->message;
// ... some logic
});
a method with the same name as the event in the class
This way you can write multiple event handlers in a class.
class ExamListener1
{
public function messageSent(EventInterface $event)
{
echo "handle the event {$event->getName()}\n";
}
public function otherEvent(EventInterface $event)
{
echo "handle the event {$event->getName()}\n";
}
}
// register
$em->addListener('group name', new ExamListener1);
At this point, this class object is equivalent to a closure.
class ExamListener2
{
public function __invoke(EventInterface $event)
{
echo "handle the event {$event->getName()}\n";
}
}
// register
$em->addListener('event name', new ExamListener2);
The handle()
method is called automatically when triggered.
class ExamHandler implements EventHandlerInterface
{
/**
* @param EventInterface $event
* @return mixed
*/
public function handle(EventInterface $event)
{
// TODO: Implement handle() method.
}
}
// register
$em->addListener('event name', new ExamListener2);
Can customize multiple events in one class, and allow configure priority.
/**
* Class EnumGroupListener
* @package Inhere\Event\Examples
*/
class EnumGroupListener implements EventSubscriberInterface
{
const TEST_EVENT = 'test';
const POST_EVENT = 'post';
/**
* Configuration events and corresponding processing methods
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
self::TEST_EVENT => 'onTest',
// can also configure priority
self::POST_EVENT => ['onPost', ListenerPriority::LOW],
];
}
public function onTest(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
}
public function onPost(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
}
}
// a pre-defined event
class MessageEvent extends Event
{
// append property ...
public $message;
}
// in the business
class Mailer
{
use EventManagerAwareTrait;
const EVENT_MESSAGE_SENT = 'messageSent';
public function send($message)
{
// ... do send $message ...
$event = new MessageEvent(self::EVENT_MESSAGE_SENT);
$event->message = $message;
// will event trigger
$this->eventManager->trigger($event);
}
}
$em = new EventManager();
// binding events
$em->attach(Mailer::EVENT_MESSAGE_SENT, 'exam_handler');
$em->attach(Mailer::EVENT_MESSAGE_SENT, function (EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
});
// add more listeners ...
// give it a higher priority here.
$em->attach(Mailer::EVENT_MESSAGE_SENT, new ExamListener1(), 10);
$em->attach(Mailer::EVENT_MESSAGE_SENT, new ExamListener2());
$em->attach(Mailer::EVENT_MESSAGE_SENT, new ExamHandler());
$mailer = new Mailer();
$mailer->setEventManager($em);
// Execution will trigger the event
$mailer->send('hello, world!');
The complete example code is in examples/demo.php
.
Running: php examples/demo.php
Output:
$ php examples/exam.php
handle the event 'messageSent' on the: ExamListener1::messageSent // Higher priority first call
handle the event 'messageSent' on the: exam_handler
handle the event 'messageSent' on the: {closure}
handle the event 'messageSent' on the: ExamListener2::__invoke
handle the event 'messageSent' on the: Inhere\Event\Examples\ExamHandler::handle
Except for some special events, in an application, most of the events are related, so we can group the events for easy identification and management.
- Event grouping It is recommended to group related events in the name design
Example:
// Model related:
model.insert
model.update
model.delete
// DB related:
db.connect
db.disconnect
db.query
// Application related:
app.start
app.run
app.stop
/**
* Class App
* @package Inhere\Event\Examples
*/
class App
{
use EventManagerAwareTrait;
const ON_START = 'app.start';
const ON_STOP = 'app.stop';
const ON_BEFORE_REQUEST = 'app.beforeRequest';
const ON_AFTER_REQUEST = 'app.afterRequest';
public function __construct(EventManager $em)
{
$this->setEventManager($em);
$this->eventManager->trigger(new Event(self::ON_START, [
'key' => 'val'
]));
}
public function run()
{
$sleep = 0;
$this->eventManager->trigger(self::ON_BEFORE_REQUEST);
echo 'request handling ';
while ($sleep <= 3) {
$sleep++;
echo '.';
sleep(1);
}
echo "\n";
$this->eventManager->trigger(self::ON_AFTER_REQUEST);
}
public function __destruct()
{
$this->eventManager->trigger(new Event(self::ON_STOP, [
'key1' => 'val1'
]));
}
}
It would be a bit of a hassle to write a class for each event listener. We can just write a class to handle different events in different ways.
- Method 1: There is a method in the class with the same name as the event.(
app.start
->start()
)
This method is quick and easy, but with certain restrictions - the name of the event and the method must be the same.
/**
* Class AppListener
* @package Inhere\Event\Examples
*/
class AppListener
{
public function start(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
}
public function beforeRequest(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
}
public function afterRequest(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
}
public function stop(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
}
}
- Method 2:Implementation
EventSubscriberInterface
Sometimes we don't want to define the processing method as the event name, we want to customize the processing method name.
At this point we can implement the interface EventSubscriberInterface
,
through the getSubscribedEvents()
inside to customize the event and the corresponding processing method
/**
* Class EnumGroupListener
* @package Inhere\Event\Examples
*/
class EnumGroupListener implements EventSubscriberInterface
{
const TEST_EVENT = 'test';
const POST_EVENT = 'post';
/**
* Configuration events and corresponding processing methods
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
self::TEST_EVENT => 'onTest',
self::POST_EVENT => ['onPost', ListenerPriority::LOW], // setting priority
];
}
public function onTest(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
}
public function onPost(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event {$event->getName()} on the: $pos\n";
}
}
Please see the running example
examples/enum-group.php
// Note: Use here one way
$em = new EventManager();
// register a group listener
$em->attach('app', new AppListener());
// create app
$app = new App($em);
// run.
$app->run();
The full sample code is in examples/named-group.php
.
Running: php examples/named-group.php
Output:
$ php examples/named-group.php
handle the event 'app.start' on the: Inhere\Event\Examples\AppListener::start
handle the event 'app.beforeRequest' on the: Inhere\Event\Examples\AppListener::beforeRequest
request handling ....
handle the event 'app.afterRequest' on the: Inhere\Event\Examples\AppListener::afterRequest
handle the event 'app.stop' on the: Inhere\Event\Examples\AppListener::stop
Support for using the event wildcard *
to listen for a group of related events, divided into two.
*
global event wildcard。add listener for*
($em->attach('*', 'global_listener')
), At this point all triggered events will be received by this listener.{prefix}.*
Specify listeners for grouping events. eg$em->attach('db.*', 'db_listener')
, At this point, all events triggered bydb.
(egdb.query
db.connect
) will be received by this listener.
Of course, you stop the propagation of this event
$event->stopPropagation(true);
before the event reaches the listener, and it will not be received by subsequent listeners.
For example, in the above group event listener, add an event listener for app.*
.
// AppListener
// add new method to listen all events
class AppListener
{
// ...
public function allEvent(EventInterface $event)
{
$pos = __METHOD__;
echo "handle the event '{$event->getName()}' on the: $pos\n";
}
}
// ...
$em = new EventManager();
$groupListener = new AppListener();
// register a group listener
$em->attach('app', $groupListener);
// all `app.` prefix events will be handled by `AppListener::allEvent()`
$em->attach('app.*', [$groupListener, 'allEvent']);
// create app
$app = new App($em);
// run.
$app->run();
Running: php examples/named-group.php
Output: (You can see that each event has been processed by AppListener::allEvent()
)
$ php examples/named-group.php
handle the event 'app.start' on the: Inhere\Event\Examples\AppListener::start
handle the event 'app.start' on the: Inhere\Event\Examples\AppListener::allEvent
handle the event 'app.beforeRequest' on the: Inhere\Event\Examples\AppListener::beforeRequest
handle the event 'app.beforeRequest' on the: Inhere\Event\Examples\AppListener::allEvent
request handling ....
handle the event 'app.afterRequest' on the: Inhere\Event\Examples\AppListener::afterRequest
handle the event 'app.afterRequest' on the: Inhere\Event\Examples\AppListener::allEvent
handle the event 'app.stop' on the: Inhere\Event\Examples\AppListener::stop
handle the event 'app.stop' on the: Inhere\Event\Examples\AppListener::allEvent
Event Object - Loads the context information associated with the trigger event, user-defined data.
- Direct and simple use of class
Event
$myEvent = new Event('name', 'target', [ 'some params ...' ]);
- Use a subclass that inherits
Event
So you can append custom data
// create event class
class MessageEvent extends Event
{
protected $name = 'messageSent';
// append property ...
public $message;
}