照猫画虎 实现 min-laravel 框架系列之事件

php   laravel  

事件

事件本质上是一对多的关系,即当某个事件发生,会触发一系列的系统更新操作。php 提供了 SplSubject, SplObserver, SplObjectStorage 标准库接口用来实现事件功能。
Laravel 的事件提供了一个简单的观察者实现,允许你在应用中订阅和监听各种发生的事件。

laravel 事件是作为 laravel 框架的核心服务提供者,在 application 初始化的时候注册到服务容器的

在这里关键理解两个步骤:

  1. 事件注册,本质上就是建立 event( 事件 string ) ——> listener ( 具体处理事件的代码函数 ) 之间的关系
  2. 事件调度。dispatch,即该事件发生,找到所有注册过的 listener 并依次调用

EventServiceProvider 服务提供者

在这里进行了一步简化,暂时不讨论 laravel 中队列的概念。因此,将 EventServiceProvider 的 register( 如果不知道 register 函数是干哈的,请看上一篇文章 ) 函数进行简化如下

  1. public function register()
  2. {
  3. $this->app->singleton('events', function ($app) {
  4. return (new Dispatcher($app));
  5. });
  6. }

Dispatcher 核心类

这个类就是负责事件的绑定和调度,核心方法为 listen 、dispatch 两个

listen 方法

  1. public function listen($events, $listener)
  2. {
  3. // 依据带不带通配符 * ,分情况处理
  4. foreach ((array) $events as $event) {
  5. if (Str::contains($event, '*')) {
  6. $this->setupWildcardListen($event, $listener);
  7. } else {
  8. // 常规变量的,保存在 listeners 变量中
  9. $this->listeners[$event][] = $this->makeListener($listener);
  10. }
  11. }
  12. }

setupWildcardListen * 型事件名称

  1. protected function setupWildcardListen($event, $listener)
  2. {
  3. $this->wildcards[$event][] = $this->makeListener($listener, true);
  4. $this->wildcardsCache = [];
  5. }

makeListener 解析处理事件的函数

  1. public function makeListener($listener, $wildcard = false)
  2. {
  3. // 如果 listener 是字符串,表示改事件处理函数是个类
  4. if (is_string($listener)) {
  5. return $this->createClassListener($listener, $wildcard);
  6. }
  7. // listener 本身就是一个可执行闭包
  8. return function ($event, $payload) use ($listener, $wildcard) {
  9. if ($wildcard) {
  10. return $listener($event, $payload);
  11. }
  12. return $listener(...array_values($payload));
  13. };
  14. }

createClassListener 创建执行函数

  1. public function createClassListener($listener, $wildcard = false)
  2. {
  3. return function ($event, $payload) use ($listener, $wildcard) {
  4. // 如果是带通配符的,这里把实际出发的事件名传递给函数了
  5. if ($wildcard) {
  6. return call_user_func($this->createClassCallable($listener), $event, $payload);
  7. }
  8. return call_user_func_array(
  9. $this->createClassCallable($listener), $payload
  10. );
  11. };
  12. }

createClassCallable 通过 IOC 解析类方法

  1. protected function createClassCallable($listener)
  2. {
  3. [$class, $method] = $this->parseClassCallable($listener);
  4. // 这块是队列问题,暂时不考虑
  5. if ( 0 && $this->handlerShouldBeQueued($class)) {
  6. return $this->createQueuedHandlerCallable($class, $method);
  7. }
  8. return [$this->container->make($class), $method];
  9. }

<br><br><br><br>

dispatch 事件调度类

  1. public function dispatch($event, $payload = [], $halt = false)
  2. {
  3. //如果 event 是个对象,那就就以对象名作为事件名,对象本身作为 listener 的参数
  4. [$event, $payload] = $this->parseEventAndPayload(
  5. $event, $payload
  6. );
  7. // 队列,放弃
  8. if ( 0 && $this->shouldBroadcast($payload)) {
  9. $this->broadcastEvent($payload[0]);
  10. }
  11. $responses = [];
  12. foreach ($this->getListeners($event) as $listener) {
  13. $response = $listener($event, $payload);
  14. // If a response is returned from the listener and event halting is enabled
  15. // we will just return this response, and not call the rest of the event
  16. // listeners. Otherwise we will add the response on the response list.
  17. if ($halt && ! is_null($response)) {
  18. return $response;
  19. }
  20. // If a boolean false is returned from a listener, we will stop propagating
  21. // the event to any further listeners down in the chain, else we keep on
  22. // looping through the listeners and firing every one in our sequence.
  23. if ($response === false) {
  24. break;
  25. }
  26. $responses[] = $response;
  27. }
  28. return $halt ? null : $responses;
  29. }

getListeners 获取事件的所有监听器

  1. public function getListeners($eventName)
  2. {
  3. $listeners = $this->listeners[$eventName] ?? [];
  4. $listeners = array_merge(
  5. $listeners,
  6. $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName)
  7. );
  8. return class_exists($eventName, false)
  9. ? $this->addInterfaceListeners($eventName, $listeners)
  10. : $listeners;
  11. }

getWildcardListeners 获取匹配符事件的监听器

  1. protected function getWildcardListeners($eventName)
  2. {
  3. $wildcards = [];
  4. foreach ($this->wildcards as $key => $listeners) {
  5. if (Str::is($key, $eventName)) {
  6. $wildcards = array_merge($wildcards, $listeners);
  7. }
  8. }
  9. return $this->wildcardsCache[$eventName] = $wildcards;
  10. }

addInterfaceListeners 找接口类是否有监听器,借此实现事件冒泡

  1. protected function addInterfaceListeners($eventName, array $listeners = [])
  2. {
  3. foreach (class_implements($eventName) as $interface) {
  4. if (isset($this->listeners[$interface])) {
  5. foreach ($this->listeners[$interface] as $names) {
  6. $listeners = array_merge($listeners, (array) $names);
  7. }
  8. }
  9. }
  10. return $listeners;
  11. }

事件测试

创建事件类

  1. namespace App\Events;
  2. class OrderEvent
  3. {
  4. public $id;
  5. public function __construct( int $id )
  6. {
  7. $this->id = $id;
  8. }
  9. }

创建事件监听类

  1. namespace App\Listeners;
  2. use App\Events\OrderEvent;
  3. class OrderListenerOne
  4. {
  5. public function handle( OrderEvent $one )
  6. {
  7. echo '事件listener :'. $one->id;
  8. }
  9. }

在 index.php 中进行实验

  1. // 绑定事件名称 和 闭包函数
  2. $event->listen('order',function( $a , $b ){
  3. echo "<hr>";
  4. echo $a, "<br>";
  5. echo $b, "<br>";
  6. echo "<hr>";
  7. });
  8. // 字符串名称触发
  9. $event->dispatch("order",[1,11,22]);
  10. // 绑定事件名称 和 类字符串
  11. $event->listen(App\Events\OrderEvent::class,App\Listeners\OrderListenerOne::class);
  12. $one = new App\Events\OrderEvent(1);
  13. $event->dispatch($one,[1,11,22]);


评论 0

发表评论

Top