xiaohuilam / laravel

Laravel 深入详解 —— 源代码解析,新手进阶指南

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

11. 事件机制

xiaohuilam opened this issue · comments

根据 Laravel 的 Events 文档 (中文版:事件系统), EventServiceProvider 的使用方法为:

/**
 * 注册应用中的其它事件。
 *
 * @return void
 */
public function boot()
{
    parent::boot();

    Event::listen('event.name', function ($foo, $bar) {
        //
    });
}

如果花点时间研究 Event 这个 Facade,我们可以找到他穿透类其实为 Illuminate\Events\Dispatcher

Illuminate\Events\Dispatcher 类的 __construct 方法为:

public function __construct(ContainerContract $container = null)
{
$this->container = $container ?: new Container;
}

作用是将容器设置到 $this->container 属性

监听

我们刚刚在 EventServiceProvider 调用的 Event::listen 方法的代码为:

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);
}
}
}

如果监听了通配事件,setupWildcardListen 在辗转后跟 else 一样的调用到了 makeListener

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 后,事件们就静静地等待被触发

$this->listeners[$event][] = $this->makeListener($listener);

触发

在我们调用 event() 辅助方法后,触发的逻辑其实走到了 dispatch 方法

public function dispatch($event, $payload = [], $halt = false)
{
// When the given "event" is actually an object we will assume it is an event
// object and use the class as the event name and this event itself as the
// payload to the handler, which makes object based events quite simple.
list($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 a response is returned from the listener and event halting is enabled
// we will just return this response, and not call the rest of the event
// listeners. Otherwise we will add the response on the response list.
if ($halt && ! is_null($response)) {
return $response;
}
// If a boolean false is returned from a listener, we will stop propagating
// the event to any further listeners down in the chain, else we keep on
// looping through the listeners and firing every one in our sequence.
if ($response === false) {
break;
}
$responses[] = $response;
}
return $halt ? null : $responses;
}

核心逻辑就这句:

$response = $listener($event, $payload);