前言
事件模式必須基於 PSR-14 去實現。
Hyperf 的事件管理器默認由 hyperf/event 實現,該組件亦可用於其它框架或應用,只需通過 Composer 將該組件引入即可。
composer require hyperf/event
概念
事件模式是一種經過了充分測試的可靠機制,是一種非常適用於解耦的機制,分別存在以下 3 種角色:
事件(Event)
是傳遞於應用代碼與監聽器(Listener)
之間的通訊對象監聽器(Listener)
是用於監聽事件(Event)
的發生的監聽對象事件調度器(EventDispatcher)
是用於觸發事件(Event)
和管理監聽器(Listener)
與事件(Event)
之間的關系的管理者對象
用通俗易懂的例子來說明就是,假設我們存在一個 UserService::register()
方法用於注冊一個賬號,在賬號注冊成功后我們可以通過事件調度器觸發 UserRegistered
事件,由監聽器監聽該事件的發生,在觸發時進行某些操作,比如發送用戶注冊成功短信,在業務發展的同時我們可能會希望在用戶注冊成功之后做更多的事情,比如發送用戶注冊成功的郵件等待,此時我們就可以通過再增加一個監聽器監聽 UserRegistered
事件即可,無需在 UserService::register()
方法內部增加與之無關的代碼。
使用場景
1、用戶注冊之前檢查用戶是否有權注冊
2、用戶注冊成功之后發送短信、發送郵件、記錄日志
這里需要定義兩個事件:
1、用戶注冊權限檢測事件BeforeUserRegister
2、用戶注冊事件UserRegister
還需要定義四個監聽器
1、用戶權限驗證監聽 ValidateRegisterListener.php
2、郵件發送 SendEmailListener.php
3、短信發送 SendSmsListener.php
4、記錄日志 LoginEventListener.php
調用方法
EventController->test()
定義事件
<?php namespace App\Event; /** * @property int $userId */ class UserRegister { public $userId; public function __construct($userId) { $this->userId = $userId; } }
<?php namespace App\Event; use Hyperf\Utils\Context; /** * @property bool $shouldRegister */ class BeforeUserRegister { // protected $shouldRegister; public function __get($name) { // TODO: Implement __get() method. return Context::get(__CLASS__.":".$name); } public function __set($name, $value) { // TODO: Implement __set() method. return Context::set(__CLASS__.":".$name,$value); } }
定義監聽器
php bin/hyperf.php gen:listener SendSmsListener php bin/hyperf.php gen:listener SendEmailListener php bin/hyperf.php gen:listener ValidateRegisterListener php bin/hyperf.php gen:listener LoginEventListener
<?php declare(strict_types=1); namespace App\Listener; use App\Event\BeforeUserRegister; use App\Event\UserRegister; use Hyperf\Event\Annotation\Listener; use Psr\Container\ContainerInterface; use Hyperf\Event\Contract\ListenerInterface; /** * @Listener */ class LoginEventListener implements ListenerInterface { /** * @var ContainerInterface */ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function listen(): array { return [ BeforeUserRegister::class, UserRegister::class, ]; } public function process(object $event) { if($event instanceof BeforeUserRegister){ echo get_class($event).$event->shouldRegister.PHP_EOL; }else if ($event instanceof UserRegister){ echo get_class($event).$event->userId.PHP_EOL; } } }
<?php declare(strict_types=1); namespace App\Listener; use App\Event\UserRegister; use Hyperf\Event\Annotation\Listener; use Psr\Container\ContainerInterface; use Hyperf\Event\Contract\ListenerInterface; /** * @Listener(priority=9) */ class SendEmailListener implements ListenerInterface { /** * @var ContainerInterface */ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function listen(): array { return [ UserRegister::class ]; } /** * * @param UserRegister $event * @author liubo 2020-06-12 15:12 */ public function process(object $event) { echo "發送Email給".$event->userId.PHP_EOL; } }
<?php declare(strict_types=1); namespace App\Listener; use App\Event\UserRegister; use Hyperf\Event\Annotation\Listener; use Psr\Container\ContainerInterface; use Hyperf\Event\Contract\ListenerInterface; /** * @Listener(priority=10) */ class SendSmsListener implements ListenerInterface { /** * @var ContainerInterface */ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function listen(): array { return [ UserRegister::class ]; } /** * * @param UserRegister $event * @author liubo 2020-06-12 15:12 */ public function process(object $event) { echo "發送短信給".$event->userId.PHP_EOL; } }
<?php declare(strict_types=1); namespace App\Listener; use App\Event\BeforeUserRegister; use Hyperf\Event\Annotation\Listener; use Psr\Container\ContainerInterface; use Hyperf\Event\Contract\ListenerInterface; /** * @Listener */ class ValidateRegisterListener implements ListenerInterface { /** * @var ContainerInterface */ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function listen(): array { return [ BeforeUserRegister::class ]; } /** * * //為了調用方便此處先這樣寫 * @param BeforeUserRegister $event * @author liubo 2020-06-12 15:27 */ public function process(object $event) { $event->shouldRegister = (bool)rand(0,1); if(!$event->shouldRegister){ echo "很抱歉,您無權注冊!".PHP_EOL; }else{ echo "歡迎注冊!".PHP_EOL; } } }
使用示例
<?php namespace App\Controller; use App\Service\UserService; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\AutoController; /** * @AutoController() */ class EventController extends AbstractController { /** * @Inject() * @var UserService */ private $userService; public function test(){ return $this->userService->register(); } }
<?php namespace App\Service; use App\Event\BeforeUserRegister; use App\Event\UserRegister; use Hyperf\Di\Annotation\Inject; use Psr\EventDispatcher\EventDispatcherInterface; class UserService { /** * @Inject() * @var EventDispatcherInterface */ private $eventDispatcher; public function register() { //驗證是否有注冊權限 $beforeUserRegister=new BeforeUserRegister(); $this->eventDispatcher->dispatch(new BeforeUserRegister()); if($beforeUserRegister->shouldRegister){ //注冊用戶 $userId =rand(0,9999); //注冊成功后 $this->eventDispatcher->dispatch(new UserRegister($userId)); return $userId; }else{ return "不可注冊!"; } } }
運行結果
歡迎注冊! App\Event\BeforeUserRegister1 發送短信給6195 發送Email給6195 App\Event\UserRegister6195