事件
Testing Is Documentation
QueryPHP 提供了一个事件组件 \Leevel\Event\Dispatch
对象。
事件适合一些业务后续处理的扩展,比如提交订单的后续通知消息接入,不但提高了可扩展性,而且还降低了系统的耦合性。
Uses
<?php
use Leevel\Di\Container;
use Leevel\Event\Dispatch;
use Leevel\Event\Observer;
use Leevel\Kernel\Utils\Api;
事件基本使用
事件系统使用 register
注册监听器,handle
会执行一个事件。
register 函数原型
# Leevel\Event\Dispatch::register
public function register(array|object|string $event, \Closure|\SplObserver|string $listener, int $priority = 500): void;
handle 函数原型
# Leevel\Event\Dispatch::handle
public function handle(object|string $event, ...$params): void // @phpstan-ignore-line;
fixture 定义
Tests\Event\Listener1
namespace Tests\Event;
class Listener1 extends Listener
{
public function handle($event): void
{
$_SERVER['event_name'] = $event;
$_SERVER['test'] = 'hello';
}
}
一般来说监听器需要继承至 \Leevel\Event\Observer
,本质上事件使用的是观察者设计模式,而监听器是观察者角色。
Tests\Event\Listener
namespace Tests\Event;
abstract class Listener extends Observer {}
public function testBaseUse(): void
{
if (isset($_SERVER['test'])) {
unset($_SERVER['test']);
}
if (isset($_SERVER['event_name'])) {
unset($_SERVER['event_name']);
}
$dispatch = new Dispatch(new Container());
$dispatch->register('event1', Listener1::class);
$dispatch->handle('event1');
self::assertSame($_SERVER['test'], 'hello');
self::assertSame($_SERVER['event_name'], 'event1');
unset($_SERVER['test'], $_SERVER['event_name']);
}
register 注册监听器支持监听器对象实例
第二个参数 $listener
支持传递对象实例。
fixture 定义
Tests\Event\Listener2
namespace Tests\Event;
class Listener2 extends Listener
{
public $arg1;
public function __construct($arg1)
{
$this->arg1 = $arg1;
}
public function handle(): void
{
$_SERVER['test'] = $this->arg1;
}
}
public function testListenerInstance(): void
{
if (isset($_SERVER['test'])) {
unset($_SERVER['test']);
}
$dispatch = new Dispatch(new Container());
$dispatch->register('event1', new Listener2('arg_foo'));
$dispatch->handle('event1');
self::assertSame($_SERVER['test'], 'arg_foo');
unset($_SERVER['test']);
}
register 注册监听器支持事件对象实例
第一个参数 $event
支持传递对象实例。
fixture 定义
Tests\Event\Event1
namespace Tests\Event;
class Event1
{
public $arg1;
public function __construct($arg1)
{
$this->arg1 = $arg1;
}
}
Tests\Event\Listener3
namespace Tests\Event;
class Listener3 extends Listener
{
public function handle($event): void
{
$_SERVER['test'] = $event->arg1;
}
}
public function testEventInstance(): void
{
if (isset($_SERVER['test'])) {
unset($_SERVER['test']);
}
$dispatch = new Dispatch(new Container());
$dispatch->register($event = new Event1('event_arg_foo'), Listener3::class);
$dispatch->handle($event);
self::assertSame($_SERVER['test'], 'event_arg_foo');
unset($_SERVER['test']);
}
register 注册监听器支持同时为多个事件绑定监听器
public function testEventAsArray(): void
{
$dispatch = new Dispatch(new Container());
$event = new Event1('event_arg_foo');
$dispatch->register([$event], Listener3::class);
$dispatch->handle($event);
self::assertSame($_SERVER['test'], 'event_arg_foo');
unset($_SERVER['test']);
}
register 注册监听器支持优先级
第三个参数 $priority
表示注册的监听器的优先级,越小越靠前执行,默认为 500。
fixture 定义
Tests\Event\Listener4
namespace Tests\Event;
class Listener4 extends Listener
{
public function handle($event): void
{
$_SERVER['test'] = 'l4';
}
}
Tests\Event\Listener5
namespace Tests\Event;
class Listener5 extends Listener
{
public function handle($event): void
{
$_SERVER['test'] = 'l5';
}
}
public function testPriority(): void
{
$dispatch = new Dispatch(new Container());
$dispatch->register('foo', Listener4::class);
$dispatch->register('foo', Listener5::class);
$dispatch->handle('foo');
self::assertSame($_SERVER['test'], 'l5');
$dispatch = new Dispatch(new Container());
// 第三个参数标识优先级,越小越靠前执行,默认为 500
$dispatch->register('foo', Listener4::class, 5);
$dispatch->register('foo', Listener5::class, 4);
$dispatch->handle('foo');
self::assertSame($_SERVER['test'], 'l4');
unset($_SERVER['test']);
}
register 注册监听器支持事件通配符
*
表示通配符事件,匹配的事件会执行对应的监听器。
fixture 定义
Tests\Event\WildcardsListener
namespace Tests\Event;
class WildcardsListener extends Listener
{
public function handle($event): void
{
$_SERVER['wildcard'] = 'wildcard';
}
}
public function testWildcards(): void
{
$dispatch = new Dispatch(new Container());
$dispatch->register('wildcards*event', WildcardsListener::class);
$dispatch->handle('wildcards123456event');
self::assertSame($_SERVER['wildcard'], 'wildcard');
unset($_SERVER['wildcard']);
$dispatch->handle('wildcards7896event');
self::assertSame($_SERVER['wildcard'], 'wildcard');
unset($_SERVER['wildcard']);
$dispatch->handle('wildcards_foobar_event');
self::assertSame($_SERVER['wildcard'], 'wildcard');
unset($_SERVER['wildcard']);
}
delete 删除事件所有监听器
fixture 定义
Tests\Event\ForRemoveListener
namespace Tests\Event;
class ForRemoveListener extends Listener
{
public function handle($event): void
{
$_SERVER['remove'] = 'remove';
}
}
public function testDeleteListeners(): void
{
$dispatch = new Dispatch(new Container());
$dispatch->register('testevent', ForRemoveListener::class);
$dispatch->handle('testevent');
self::assertSame($_SERVER['remove'], 'remove');
unset($_SERVER['remove']);
$dispatch->delete('testevent');
$dispatch->handle('testevent');
self::assertFalse(isset($_SERVER['remove']));
}
delete 删除通配符事件所有监听器
public function testDeleteWildcardListeners(): void
{
$dispatch = new Dispatch(new Container());
$dispatch->register('wildcards*event', WildcardsListener::class);
$dispatch->handle('wildcards123456event');
self::assertSame($_SERVER['wildcard'], 'wildcard');
unset($_SERVER['wildcard']);
$dispatch->delete('wildcards*event');
$dispatch->handle('wildcards7896event');
self::assertFalse(isset($_SERVER['wildcard']));
}
has 判断事件监听器是否存在
public function testHas(): void
{
$dispatch = new Dispatch(new Container());
self::assertSame([], $dispatch->get('testevent'));
self::assertFalse($dispatch->has('testevent'));
$dispatch->register('testevent', Listener1::class);
self::assertSame([500 => [Listener1::class]], $dispatch->get('testevent'));
self::assertTrue($dispatch->has('testevent'));
}
独立类监听器必须包含 handle 方法
fixture 定义
Tests\Event\ListenerWithoutRunOrHandleMethod
namespace Tests\Event;
class ListenerWithoutRunOrHandleMethod extends Listener
{
public function notFound($event): void {}
}
public function testListenerWithoutRunOrHandleMethod(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage(
'Observer Tests\\Event\\ListenerWithoutRunOrHandleMethod must has handle method.'
);
$dispatch = new Dispatch(new Container());
$dispatch->register('testevent', ListenerWithoutRunOrHandleMethod::class);
$dispatch->handle('testevent');
}
独立类监听器自动转换为 \Leevel\Event\Observer
一般来说监听器需要继承至 \Leevel\Event\Observer
,本质上事件使用的是观察者设计模式,而监听器是观察者角色。
如果是未继承的独立类,系统会自动转换成 \Leevel\Event\Observer
而成为一个观察者角色。
fixture 定义
Tests\Event\ListenerNotExtends
namespace Tests\Event;
class ListenerNotExtends
{
public function handle(): void
{
$_SERVER['autochange'] = 'autochange';
}
}
public function testListenerNotInstanceofSplObserverWillAutoChange(): void
{
$dispatch = new Dispatch(new Container());
$dispatch->register('testevent', ListenerNotExtends::class);
$dispatch->handle('testevent');
self::assertSame($_SERVER['autochange'], 'autochange');
unset($_SERVER['autochange']);
}
独立类监听器必须包含 handle 方法
fixture 定义
Tests\Event\ListenerNotExtendsWithoutHandle
namespace Tests\Event;
class ListenerNotExtendsWithoutHandle {}
public function testListenerNotInstanceofSplObserverWithoutHandle(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage(
'Observer `Tests\\Event\\ListenerNotExtendsWithoutHandle` is invalid.'
);
$dispatch = new Dispatch(new Container());
$dispatch->register('testevent', ListenerNotExtendsWithoutHandle::class);
$dispatch->handle('testevent');
}
监听器支持闭包
一般来说监听器需要继承至 \Leevel\Event\Observer
,本质上事件使用的是观察者设计模式,而监听器是观察者角色。
如果是闭包,系统会自动转换成 \Leevel\Event\Observer
而成为一个观察者角色。
public function testListenerIsClosure(): void
{
$dispatch = new Dispatch(new Container());
$dispatch->register('testevent', static function (): void {
$_SERVER['isclosure'] = 'isclosure';
});
$dispatch->handle('testevent');
self::assertSame($_SERVER['isclosure'], 'isclosure');
unset($_SERVER['isclosure']);
}