【Laravel-海贼王系列】第九章, Events 功能解析

Events 注册

  1. 框架如何在启动的时候加载注册的事件?
  1. 框架如何触发事件?
1,先在容器中注册 events 的全局对象。

Application 构造函数中对 events 进行注册代码

protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));
        $this->register(new LogServiceProvider($this));
        $this->register(new RoutingServiceProvider($this));
    }
复制代码

展开 $this->register(new EventServiceProvider($this));

这里的 $this->register() 方法就是调用 EventServiceProvider 对象的 register() 方法,最终在容器中对应的 events 对象

class EventServiceProvider extends ServiceProvider
{
       public function register()
    {
        $this->app->singleton('events', function ($app) {
            return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
                return $app->make(QueueFactoryContract::class);
            });
        });
    }
}
复制代码
2,注册用户定义的事件

这里的部分涉及到 Provider 启动 相关的流程,第八章的时候有讲,我们直接跳到如何启动 EventServiceProvider 这里。

先看 app.providers 中配置要加载的服务提供者。

'providers' => [
        ...
        App\Providers\EventServiceProvider::class,
        ...
    ],
复制代码

App\Providers\EventServiceProvider::class 这里的 boot() 方法是被框架加载服务提供者的时候调用的。

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
    ];

    public function boot()
    {
        parent::boot();
    }
}
复制代码

先启动父类的 boot() 方法

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [];

    protected $subscribe = [];

    public function boot()
    {
        foreach ($this->listens() as $event => $listeners) {
            foreach ($listeners as $listener) {
                Event::listen($event, $listener);
            }
        }

        foreach ($this->subscribe as $subscriber) {
            Event::subscribe($subscriber);
        }
    }
    
    public function register()
    {
        //
    }
    
    public function listens()
    {
        return $this->listen;
    }
}
复制代码

上面代码就是我们注册的核心了,首先先遍历 $listen 这个对象

 protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
    ];
复制代码

这段代码就是绑定事件的核心

  Event::listen($event, $listener);
复制代码

这里我们来看 Event 门面返回的是什么

class Event extends Facade
{
    ...
    
    protected static function getFacadeAccessor()
    {
        return 'events';
    }
}
复制代码

实际上面回到了最初的地方, events 是最初绑定的闭包

 $this->app->singleton('events', function ($app) {
                return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
                      return $app->make(QueueFactoryContract::class);
            });
        });
复制代码

events 就是 Illuminate\Events\Dispatcher 这个类! 我们来看看 Dispatcherlisten 方法

 public function listen($events, $listener)
    {
        foreach ((array) $events as $event) {
            if (Str::contains($event, '*')) {
                $this->setupWildcardListen($event, $listener);
            } else {
                $this->listeners[$event][] = $this->makeListener($listener);
            }
        }
    }

复制代码

这里的代码其实就是赋值的过程 如果是全局的事件就放入 $this->wildcards 中否则就放入 $this->listeners 中。

继续看$this->makeListener($listener);

public function makeListener($listener, $wildcard = false)
    {
        if (is_string($listener)) {
            return $this->createClassListener($listener, $wildcard);
        }

        return function ($event, $payload) use ($listener, $wildcard) {
            if ($wildcard) {
                return $listener($event, $payload);
            }

            return $listener(...array_values($payload));
        };
    }
复制代码

这里解析出来的闭包会赋值给 $this->listeners[$event] 。 到这里我们就已经解析完了框架是如何对已经写好的事件进行注册 的。

3,订阅者的注册

事件除了一对一的绑定,还实现了一对多的绑定就是订阅者

public function boot()
    {
        ...
      
        foreach ($this->subscribe as $subscriber) {
            Event::subscribe($subscriber);
        }
    }
复制代码

回到 boot() 的方法中,执行完常规的 Event 的注册,之后开始注册 subscribeDispatcher 对象中。

 public function subscribe($subscriber)
    {
        $subscriber = $this->resolveSubscriber($subscriber); // 从容器中解析传入的抽象,返回对应实例

        $subscriber->subscribe($this); // 调用返回实例的subscribe($this)方法,同时传入 $dispatcher 对象
    }
复制代码

那么 subscribe($this) 里面都做了什么呢?

这里我列举了一个demo,写法也是参照官方给出的。

 public function subscribe($events)
    {
      $events->listen(
            'App\Events\MyEvents',
            'App\Listeners\MySubscribe@funName()'
      );
    }
复制代码

最后还是调用了 listen方法,只不过这种方式可以支持一个订阅者监听多个事件,根据事件的不同选择性的触发对应的方法。

fire events !

前面都是讲怎么把事件绑定到 $dispatcher 对象中,这节我们开始讲怎么触发事件!

事件调用的核心方法。

public function dispatch($event, $payload = [], $halt = false)
    {
        [$event, $payload] = $this->parseEventAndPayload(
            $event, $payload
        );

        if ($this->shouldBroadcast($payload)) {
            $this->broadcastEvent($payload[0]);
        }

        $responses = [];

        foreach ($this->getListeners($event) as $listener) {
            $response = $listener($event, $payload);

            if ($halt && ! is_null($response)) {
                return $response;
            }
            
            if ($response === false) {
                break;
            }

            $responses[] = $response;
        }

        return $halt ? null : $responses;
    }
复制代码

我们来看个系统的默认的事件返回值

[$event, $payload] = $this->parseEventAndPayload(
            $event, $payload
        );
复制代码

这段代码是事件广播的触发

if ($this->shouldBroadcast($payload)) {
            $this->broadcastEvent($payload[0]);
        }
复制代码

开始遍历从给定事件解析出来的监听器类

foreach ($this->getListeners($event) as $listener) 
    {
         ......
    }
复制代码

我们直接看 $this->getListeners($event);方法

public function getListeners($eventName)
    {
        $listeners = $this->listeners[$eventName] ?? [];

        $listeners = array_merge(
            $listeners,
            $this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName)
        );

        return class_exists($eventName, false)
                    ? $this->addInterfaceListeners($eventName, $listeners)
                    : $listeners;
    }
复制代码

这里的主要逻辑就是从之前的 $this->listeners 中找是否存在绑定的类,如果存在则返回对应的类,否则返回对应的实现接口。

最后执行 $response = $listener($event, $payload);

这里的 $listener 就是在上面绑定的时候调用 makeListerer() 返回的闭包。

public function makeListener($listener, $wildcard = false)
    {
        if (is_string($listener)) {
            return $this->createClassListener($listener, $wildcard);
        }

        return function ($event, $payload) use ($listener, $wildcard) {
            if ($wildcard) {
                return $listener($event, $payload);
            }

            return $listener(...array_values($payload));
        };
    }
复制代码

这里传入的参数 $event 是我们编写的 Event 类的对象,$payload 是你要传入携带的参数。

这里的闭包逻辑后续详解。

结语

Events 提供了解耦开发的优点,框架很多地方都有使用,比如模型的观察器就是基于事件系统来实现的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值