inhere / event
event manager library of the php
Requires
- php: >7.1.0
Suggests
- inhere/console: a lightweight php console application library.
- inhere/php-validate: Very lightweight data validate tool library
README
简洁, 功能完善的事件管理调度实现
- 实现自 Psr 14 - 事件调度器
- 支持对一个事件添加多个监听器
- 支持设置事件优先级
- 支持快速的事件组注册
- 支持根据事件名称来快速的对事件组监听
- eg 触发
app.run
,app.end
都将同时会触发app.*
事件
- eg 触发
- 支持通配符事件的监听
项目地址
安装
- composer 命令
composer require inhere/event
- composer.json
{ "require": { "inhere/event": "dev-master" } }
事件调度器
事件调度器, 也可称之为事件管理器。事件的注册、监听器注册、调度(触发)都是由它管理的。
use Inhere\Event\EventManager; $em = new EventManager();
事件监听器
监听器允许是:
- function 函数
- 一个闭包
- 一个监听器类(可以有多种方式)
1. function
// ... $em->attach(Mailer::EVENT_MESSAGE_SENT, 'my_function');
2. 闭包
// ... $em->attach(Mailer::EVENT_MESSAGE_SENT, function(Event $event) { // $message = $event->message; // ... some logic });
3. 监听器类(有多种方式)
- 类里面存在跟事件相同名称的方法
此种方式可以在类里面写多个事件的处理方法
class ExamListener1 { public function messageSent(EventInterface $event) { echo "handle the event {$event->getName()}\n"; } }
- 一个类(含有
__invoke
方法)
此时这个类对象就相当于一个闭包
class ExamListener2 { public function __invoke(EventInterface $event) { echo "handle the event {$event->getName()}\n"; } }
- 实现接口
EventHandlerInterface
触发时会自动调用 handle()
方法。
class ExamHandler implements EventHandlerInterface { /** * @param EventInterface $event * @return mixed */ public function handle(EventInterface $event) { // TODO: Implement handle() method. } }
- 实现接口
EventSubscriberInterface
可以在一个类里面自定义监听多个事件
/** * Class EnumGroupListener * @package Inhere\Event\Examples */ class EnumGroupListener implements EventSubscriberInterface { const TEST_EVENT = 'test'; const POST_EVENT = 'post'; /** * 配置事件与对应的处理方法 * @return array */ public static function getSubscribedEvents(): array { return [ self::TEST_EVENT => 'onTest', 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"; } }
快速使用
1. 绑定事件触发
// 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) { // ...发送 $message 的逻辑... $event = new MessageEvent(self::EVENT_MESSAGE_SENT); $event->message = $message; // 事件触发 $this->eventManager->trigger($event); } }
2. 触发事件
$em = new EventManager(); // 绑定事件 $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"; }); // 这里给它设置了更高的优先级 $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); // 执行,将会触发事件 $mailer->send('hello, world!');
3. 运行示例
完整的实例代码在 examples/demo.php
中。
运行: php examples/demo.php
输出:
$ php examples/exam.php
handle the event 'messageSent' on the: ExamListener1::messageSent // 更高优先级的先调用
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
一组事件的监听器
除了一些特殊的事件外,在一个应用中,大多数事件是有关联的,此时我们就可以对事件进行分组,方便识别和管理使用。
- 事件分组 推荐将相关的事件,在名称设计上进行分组
例如:
// 模型相关:
model.insert
model.update
model.delete
// DB相关:
db.connect
db.disconnect
db.query
// 应用相关:
app.start
app.run
app.stop
1. 一个简单的示例应用类
/** * 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' ])); } }
2. 此应用的监听器类
将每个事件的监听器写一个类,显得有些麻烦。我们可以只写一个类用里面不同的方法来处理不同的事件。
- 方式一: 类里面存在跟事件名称相同的方法(
app.start
->start()
)
这种方式简单快捷,但是有一定的限制 - 事件名与方法的名称必须相同。
/** * 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"; } }
- 方式二:实现接口
EventSubscriberInterface
有时候我们并不想将处理方法定义成事件名称一样,想自定义。
此时我们可以实现接口 EventSubscriberInterface
,通过里面的 getSubscribedEvents()
来自定义事件和对应的处理方法
运行示例请看
examples/enum-group.php
/** * Class EnumGroupListener * @package Inhere\Event\Examples */ class EnumGroupListener implements EventSubscriberInterface { const TEST_EVENT = 'test'; const POST_EVENT = 'post'; /** * 配置事件与对应的处理方法 * @return array */ public static function getSubscribedEvents(): array { return [ self::TEST_EVENT => 'onTest', 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"; } }
3. 添加监听
// 这里使用 方式一 $em = new EventManager(); // register a group listener $em->attach('app', new AppListener()); // create app $app = new App($em); // run. $app->run();
4. 运行示例
完整的示例代码在 examples/named-group.php
中。
运行: php examples/named-group.php
输出:
$ 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
事件通配符 *
支持使用事件通配符 *
对一组相关的事件进行监听, 分两种。
*
全局的事件通配符。直接对*
添加监听器($em->attach('*', 'global_listener')
), 此时所有触发的事件都会被此监听器接收到。{prefix}.*
指定分组事件的监听。eg$em->attach('db.*', 'db_listener')
, 此时所有触发的以db.
为前缀的事件(egdb.query
db.connect
)都会被此监听器接收到。
当然,你在事件到达监听器前停止了本次事件的传播
$event->stopPropagation(true);
,就不会被后面的监听器接收到了。
示例,在上面的组事件监听器改下,添加一个 app.*
的事件监听。
// AppListener 新增一个方法 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();
运行示例
运行: php examples/named-group.php
输出:(可以看到每个事件都经过了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
$myEvent = new Event('name', 'target', [ 'some params ...' ]);
- 使用继承了
Event
的子类
这样你可以追加自定义数据
// create event class class MessageEvent extends Event { protected $name = 'messageSent'; // append property ... public $message; }
License
MIT