事件

Testing Is Documentation

tests/Event/DispatchTest.php事件 - 图1

QueryPHP 提供了一个事件组件 \Leevel\Event\Dispatch 对象。

事件适合一些业务后续处理的扩展,比如提交订单的后续通知消息接入,不但提高了可扩展性,而且还降低了系统的耦合性。

Uses

  1. <?php
  2. use Leevel\Di\Container;
  3. use Leevel\Event\Dispatch;
  4. use Leevel\Event\Observer;

事件基本使用

事件系统使用 register 注册监听器,handle 会执行一个事件。

register 函数原型

  1. # Leevel\Event\Dispatch::register
  2. /**
  3. * 注册监听器.
  4. *
  5. * @param array|object|string $event
  6. * @param mixed $listener
  7. */
  8. public function register($event, $listener, int $priority = 500): void;

handle 函数原型

  1. # Leevel\Event\Dispatch::handle
  2. /**
  3. * 执行一个事件.
  4. *
  5. * @param object|string $event
  6. * @param array ...$params
  7. */
  8. public function handle($event, ...$params): void;

fixture 定义

Tests\Event\Listener1

  1. namespace Tests\Event;
  2. class Listener1 extends Listener
  3. {
  4. public function handle($event)
  5. {
  6. $_SERVER['event_name'] = $event;
  7. $_SERVER['test'] = 'hello';
  8. }
  9. }

一般来说监听器需要继承至 \Leevel\Event\Observer,本质上事件使用的是观察者设计模式,而监听器是观察者角色。

Tests\Event\Listener

  1. namespace Tests\Event;
  2. abstract class Listener extends Observer
  3. {
  4. }
  1. public function testBaseUse(): void
  2. {
  3. if (isset($_SERVER['test'])) {
  4. unset($_SERVER['test']);
  5. }
  6. if (isset($_SERVER['event_name'])) {
  7. unset($_SERVER['event_name']);
  8. }
  9. $dispatch = new Dispatch(new Container());
  10. $dispatch->register('event', Listener1::class);
  11. $dispatch->handle('event');
  12. $this->assertSame($_SERVER['test'], 'hello');
  13. $this->assertSame($_SERVER['event_name'], 'event');
  14. unset($_SERVER['test'], $_SERVER['event_name']);
  15. }

register 注册监听器支持监听器对象实例

第二个参数 $listener 支持传递对象实例。

fixture 定义

Tests\Event\Listener2

  1. namespace Tests\Event;
  2. class Listener2 extends Listener
  3. {
  4. public $arg1;
  5. public function __construct($arg1)
  6. {
  7. $this->arg1 = $arg1;
  8. }
  9. public function handle()
  10. {
  11. $_SERVER['test'] = $this->arg1;
  12. }
  13. }
  1. public function testListenerInstance(): void
  2. {
  3. if (isset($_SERVER['test'])) {
  4. unset($_SERVER['test']);
  5. }
  6. $dispatch = new Dispatch(new Container());
  7. $dispatch->register('event', new Listener2('arg_foo'));
  8. $dispatch->handle('event');
  9. $this->assertSame($_SERVER['test'], 'arg_foo');
  10. unset($_SERVER['test']);
  11. }

register 注册监听器支持事件对象实例

第一个参数 $event 支持传递对象实例。

fixture 定义

Tests\Event\Event1

  1. namespace Tests\Event;
  2. class Event1
  3. {
  4. public $arg1;
  5. public function __construct($arg1)
  6. {
  7. $this->arg1 = $arg1;
  8. }
  9. }

Tests\Event\Listener3

  1. namespace Tests\Event;
  2. class Listener3 extends Listener
  3. {
  4. public function handle($event)
  5. {
  6. $_SERVER['test'] = $event->arg1;
  7. }
  8. }
  1. public function testEventInstance(): void
  2. {
  3. if (isset($_SERVER['test'])) {
  4. unset($_SERVER['test']);
  5. }
  6. $dispatch = new Dispatch(new Container());
  7. $dispatch->register($event = new Event1('event_arg_foo'), Listener3::class);
  8. $dispatch->handle($event);
  9. $this->assertSame($_SERVER['test'], 'event_arg_foo');
  10. unset($_SERVER['test']);
  11. }

register 注册监听器支持同时为多个事件绑定监听器

  1. public function testEventAsArray(): void
  2. {
  3. $dispatch = new Dispatch(new Container());
  4. $event = new Event1('event_arg_foo');
  5. $dispatch->register([$event], Listener3::class);
  6. $dispatch->handle($event);
  7. $this->assertSame($_SERVER['test'], 'event_arg_foo');
  8. unset($_SERVER['test']);
  9. }

register 注册监听器支持优先级

第三个参数 $priority 表示注册的监听器的优先级,越小越靠前执行,默认为 500。

fixture 定义

Tests\Event\Listener4

  1. namespace Tests\Event;
  2. class Listener4 extends Listener
  3. {
  4. public function handle($event)
  5. {
  6. $_SERVER['test'] = 'l4';
  7. }
  8. }

Tests\Event\Listener5

  1. namespace Tests\Event;
  2. class Listener5 extends Listener
  3. {
  4. public function handle($event)
  5. {
  6. $_SERVER['test'] = 'l5';
  7. }
  8. }
  1. public function testPriority(): void
  2. {
  3. $dispatch = new Dispatch(new Container());
  4. $dispatch->register('foo', Listener4::class);
  5. $dispatch->register('foo', Listener5::class);
  6. $dispatch->handle('foo');
  7. $this->assertSame($_SERVER['test'], 'l5');
  8. $dispatch = new Dispatch(new Container());
  9. // 第三个参数标识优先级,越小越靠前执行,默认为 500
  10. $dispatch->register('foo', Listener4::class, 5);
  11. $dispatch->register('foo', Listener5::class, 4);
  12. $dispatch->handle('foo');
  13. $this->assertSame($_SERVER['test'], 'l4');
  14. unset($_SERVER['test']);
  15. }

register 注册监听器支持事件通配符

* 表示通配符事件,匹配的事件会执行对应的监听器。

fixture 定义

Tests\Event\WildcardsListener

  1. namespace Tests\Event;
  2. class WildcardsListener extends Listener
  3. {
  4. public function handle($event)
  5. {
  6. $_SERVER['wildcard'] = 'wildcard';
  7. }
  8. }
  1. public function testWildcards(): void
  2. {
  3. $dispatch = new Dispatch(new Container());
  4. $dispatch->register('wildcards*event', WildcardsListener::class);
  5. $dispatch->handle('wildcards123456event');
  6. $this->assertSame($_SERVER['wildcard'], 'wildcard');
  7. unset($_SERVER['wildcard']);
  8. $dispatch->handle('wildcards7896event');
  9. $this->assertSame($_SERVER['wildcard'], 'wildcard');
  10. unset($_SERVER['wildcard']);
  11. $dispatch->handle('wildcards_foobar_event');
  12. $this->assertSame($_SERVER['wildcard'], 'wildcard');
  13. unset($_SERVER['wildcard']);
  14. }

delete 删除事件所有监听器

fixture 定义

Tests\Event\ForRemoveListener

  1. namespace Tests\Event;
  2. class ForRemoveListener extends Listener
  3. {
  4. public function handle($event)
  5. {
  6. $_SERVER['remove'] = 'remove';
  7. }
  8. }
  1. public function testDeleteListeners(): void
  2. {
  3. $dispatch = new Dispatch(new Container());
  4. $dispatch->register('testevent', ForRemoveListener::class);
  5. $dispatch->handle('testevent');
  6. $this->assertSame($_SERVER['remove'], 'remove');
  7. unset($_SERVER['remove']);
  8. $dispatch->delete('testevent');
  9. $dispatch->handle('testevent');
  10. $this->assertFalse(isset($_SERVER['remove']));
  11. }

delete 删除通配符事件所有监听器

  1. public function testDeleteWildcardListeners(): void
  2. {
  3. $dispatch = new Dispatch(new Container());
  4. $dispatch->register('wildcards*event', WildcardsListener::class);
  5. $dispatch->handle('wildcards123456event');
  6. $this->assertSame($_SERVER['wildcard'], 'wildcard');
  7. unset($_SERVER['wildcard']);
  8. $dispatch->delete('wildcards*event');
  9. $dispatch->handle('wildcards7896event');
  10. $this->assertFalse(isset($_SERVER['wildcard']));
  11. }

has 判断事件监听器是否存在

  1. public function testHas(): void
  2. {
  3. $dispatch = new Dispatch(new Container());
  4. $this->assertSame([], $dispatch->get('testevent'));
  5. $this->assertFalse($dispatch->has('testevent'));
  6. $dispatch->register('testevent', Listener1::class);
  7. $this->assertSame([500 => [Listener1::class]], $dispatch->get('testevent'));
  8. $this->assertTrue($dispatch->has('testevent'));
  9. }

独立类监听器必须包含 handle 方法

fixture 定义

Tests\Event\ListenerWithoutRunOrHandleMethod

  1. namespace Tests\Event;
  2. class ListenerWithoutRunOrHandleMethod extends Listener
  3. {
  4. public function notFound($event)
  5. {
  6. }
  7. }
  1. public function testListenerWithoutRunOrHandleMethod(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Observer Tests\\Event\\ListenerWithoutRunOrHandleMethod must has handle method.'
  6. );
  7. $dispatch = new Dispatch(new Container());
  8. $dispatch->register('testevent', ListenerWithoutRunOrHandleMethod::class);
  9. $dispatch->handle('testevent');
  10. }

独立类监听器自动转换为 \Leevel\Event\Observer

一般来说监听器需要继承至 \Leevel\Event\Observer,本质上事件使用的是观察者设计模式,而监听器是观察者角色。

如果是未继承的独立类,系统会自动转换成 \Leevel\Event\Observer 而成为一个观察者角色。

fixture 定义

Tests\Event\ListenerNotExtends

  1. namespace Tests\Event;
  2. class ListenerNotExtends
  3. {
  4. public function handle()
  5. {
  6. $_SERVER['autochange'] = 'autochange';
  7. }
  8. }
  1. public function testListenerNotInstanceofSplObserverWillAutoChange(): void
  2. {
  3. $dispatch = new Dispatch(new Container());
  4. $dispatch->register('testevent', ListenerNotExtends::class);
  5. $dispatch->handle('testevent');
  6. $this->assertSame($_SERVER['autochange'], 'autochange');
  7. unset($_SERVER['autochange']);
  8. }

独立类监听器必须包含 handle 方法

fixture 定义

Tests\Event\ListenerNotExtendsWithoutHandle

  1. namespace Tests\Event;
  2. class ListenerNotExtendsWithoutHandle
  3. {
  4. }
  1. public function testListenerNotInstanceofSplObserverWithoutHandle(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Observer `Tests\\Event\\ListenerNotExtendsWithoutHandle` is invalid.'
  6. );
  7. $dispatch = new Dispatch(new Container());
  8. $dispatch->register('testevent', ListenerNotExtendsWithoutHandle::class);
  9. $dispatch->handle('testevent');
  10. }

监听器支持闭包

一般来说监听器需要继承至 \Leevel\Event\Observer,本质上事件使用的是观察者设计模式,而监听器是观察者角色。

如果是闭包,系统会自动转换成 \Leevel\Event\Observer 而成为一个观察者角色。

  1. public function testListenerIsClosure(): void
  2. {
  3. $dispatch = new Dispatch(new Container());
  4. $dispatch->register('testevent', function () {
  5. $_SERVER['isclosure'] = 'isclosure';
  6. });
  7. $dispatch->handle('testevent');
  8. $this->assertSame($_SERVER['isclosure'], 'isclosure');
  9. unset($_SERVER['isclosure']);
  10. }