照猫画虎 实现 min-laravel 框架系列之事件
- laravel
- 2020-06-08
- 3681
- 0
事件
事件本质上是一对多的关系,即当某个事件发生,会触发一系列的系统更新操作。php 提供了 SplSubject, SplObserver, SplObjectStorage 标准库接口用来实现事件功能。
Laravel 的事件提供了一个简单的观察者实现,允许你在应用中订阅和监听各种发生的事件。
laravel 事件是作为 laravel 框架的核心服务提供者,在 application 初始化的时候注册到服务容器的
在这里关键理解两个步骤:
- 事件注册,本质上就是建立 event( 事件 string ) ——> listener ( 具体处理事件的代码函数 ) 之间的关系
- 事件调度。dispatch,即该事件发生,找到所有注册过的 listener 并依次调用
EventServiceProvider 服务提供者
在这里进行了一步简化,暂时不讨论 laravel 中队列的概念。因此,将 EventServiceProvider 的 register( 如果不知道 register 函数是干哈的,请看上一篇文章 ) 函数进行简化如下
public function register()
{
$this->app->singleton('events', function ($app) {
return (new Dispatcher($app));
});
}
Dispatcher 核心类
这个类就是负责事件的绑定和调度,核心方法为 listen 、dispatch 两个
listen 方法
public function listen($events, $listener)
{
// 依据带不带通配符 * ,分情况处理
foreach ((array) $events as $event) {
if (Str::contains($event, '*')) {
$this->setupWildcardListen($event, $listener);
} else {
// 常规变量的,保存在 listeners 变量中
$this->listeners[$event][] = $this->makeListener($listener);
}
}
}
setupWildcardListen * 型事件名称
protected function setupWildcardListen($event, $listener)
{
$this->wildcards[$event][] = $this->makeListener($listener, true);
$this->wildcardsCache = [];
}
makeListener 解析处理事件的函数
public function makeListener($listener, $wildcard = false)
{
// 如果 listener 是字符串,表示改事件处理函数是个类
if (is_string($listener)) {
return $this->createClassListener($listener, $wildcard);
}
// listener 本身就是一个可执行闭包
return function ($event, $payload) use ($listener, $wildcard) {
if ($wildcard) {
return $listener($event, $payload);
}
return $listener(...array_values($payload));
};
}
createClassListener 创建执行函数
public function createClassListener($listener, $wildcard = false)
{
return function ($event, $payload) use ($listener, $wildcard) {
// 如果是带通配符的,这里把实际出发的事件名传递给函数了
if ($wildcard) {
return call_user_func($this->createClassCallable($listener), $event, $payload);
}
return call_user_func_array(
$this->createClassCallable($listener), $payload
);
};
}
createClassCallable 通过 IOC 解析类方法
protected function createClassCallable($listener)
{
[$class, $method] = $this->parseClassCallable($listener);
// 这块是队列问题,暂时不考虑
if ( 0 && $this->handlerShouldBeQueued($class)) {
return $this->createQueuedHandlerCallable($class, $method);
}
return [$this->container->make($class), $method];
}
<br><br><br><br>
dispatch 事件调度类
public function dispatch($event, $payload = [], $halt = false)
{
//如果 event 是个对象,那就就以对象名作为事件名,对象本身作为 listener 的参数
[$event, $payload] = $this->parseEventAndPayload(
$event, $payload
);
// 队列,放弃
if ( 0 && $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;
}
getListeners 获取事件的所有监听器
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;
}
getWildcardListeners 获取匹配符事件的监听器
protected function getWildcardListeners($eventName)
{
$wildcards = [];
foreach ($this->wildcards as $key => $listeners) {
if (Str::is($key, $eventName)) {
$wildcards = array_merge($wildcards, $listeners);
}
}
return $this->wildcardsCache[$eventName] = $wildcards;
}
addInterfaceListeners 找接口类是否有监听器,借此实现事件冒泡
protected function addInterfaceListeners($eventName, array $listeners = [])
{
foreach (class_implements($eventName) as $interface) {
if (isset($this->listeners[$interface])) {
foreach ($this->listeners[$interface] as $names) {
$listeners = array_merge($listeners, (array) $names);
}
}
}
return $listeners;
}
事件测试
创建事件类
namespace App\Events;
class OrderEvent
{
public $id;
public function __construct( int $id )
{
$this->id = $id;
}
}
创建事件监听类
namespace App\Listeners;
use App\Events\OrderEvent;
class OrderListenerOne
{
public function handle( OrderEvent $one )
{
echo '事件listener :'. $one->id;
}
}
在 index.php 中进行实验
// 绑定事件名称 和 闭包函数
$event->listen('order',function( $a , $b ){
echo "<hr>";
echo $a, "<br>";
echo $b, "<br>";
echo "<hr>";
});
// 字符串名称触发
$event->dispatch("order",[1,11,22]);
// 绑定事件名称 和 类字符串
$event->listen(App\Events\OrderEvent::class,App\Listeners\OrderListenerOne::class);
$one = new App\Events\OrderEvent(1);
$event->dispatch($one,[1,11,22]);