Laravel 框架的事件机制

6 篇文章 0 订阅

Laravel 框架的事件处理机制是通过类EventServiceProvider来实现的.

建立事件

首先我需要将自己的事件和监听者注册到app\Providers目录下的EventServiceProvider类中的$listen数组中


然后运行php  artisan event:generate 命令会自动生成相应的事件类和监听者类。然后在相应的类中编写事件的逻辑和监听者的逻辑。调用事件可以使用  Event:: fire(new App\Events\TestEvent()) .这就是调用事件的基本步骤。具体请参考官方文档。

事件触发前

在服务提供者中,一般有register和boot方法,而boot方法一般在框架中已经注册完所有的服务提供者之后运行。而app\Providers目录下的EventServiceProvider类中的boot方法继承父类的boot方法。于是我们查看父类的boot方法如下:

  1. public function boot()  
  2.    {  
  3.        foreach ($this->listens() as $event => $listeners) {  
  4.            foreach ($listeners as $listener) {  
  5.                Event::listen($event$listener);  
  6.            }  
  7.        }  
  8.   
  9.        foreach ($this->subscribe as $subscriber) {  
  10.            Event::subscribe($subscriber);  
  11.        }  
  12.    }  
其实就是获取$listens属性数组并且进行遍历,将数组中的事件(键)和事件监听者(数组的键值)进行绑定。为了查看绑定的过程,我 们又要查看Event::listen  这个方法。由于调用的是facade,我们根据facade模式寻找到对应的类(facade模式原理请查看博文:http://blog.csdn.net/qq_16877261/article/details/77530081)  \Illuminate\Events\Dispatcher找到listen方法如下:
  1. public function listen($events$listener)  
  2.    {  
  3.        foreach ((array)$events as $event) {  
  4.            if (Str::contains($event'*')) {  
  5.                $this->setupWildcardListen($event$listener);  
  6.            } else {  
  7.                $this->listeners[$event][] = $this->makeListener($listener);  
  8.            }  
  9.        }  
  10.    }  
在这里我就不研究通配符形式的事件与事件监听者的绑定。于是其实代码通过
  1. $this->listeners[$event][] = $this->makeListener($listener);  
进行了绑定。我查看makeListener 方法如下:
  1. public function makeListener($listener$wildcard = false)  
  2.    {  
  3.        if (is_string($listener)) {  
  4.            return $this->createClassListener($listener$wildcard);  
  5.        }  
  6.   
  7.        return function ($event$payloaduse ($listener$wildcard) {  
  8.            if ($wildcard) {  
  9.                return $listener($event$payload);  
  10.            } else {  
  11.                return $listener(...array_values($payload));  
  12.            }  
  13.        };  
  14.    }  
由于我们注册的事件监听者是字符串,代码运行到这一步。
  1. return $this->createClassListener($listener$wildcard);  
于是跟踪函数代码如下:
  1. public function createClassListener($listener$wildcard = false)  
  2.    {  
  3.        //将监听者封装成闭包  
  4.        return function ($event$payloaduse ($listener$wildcard) {  
  5.            if ($wildcard) {  
  6.                return call_user_func($this->createClassCallable($listener), $event$payload);  
  7.            } else {  
  8.                return call_user_func_array(  
  9.                    $this->createClassCallable($listener), $payload  
  10.                );  
  11.            }  
  12.        };  
  13.    }  
在这一步,函数将事件监听者封装成闭包的形式。在闭包里面通过如下代码实现了调用事件监听者的handle方法,以便在触发事件后调用闭包从而执行该方法。
  1. return call_user_func_array(  
  2.                    $this->createClassCallable($listener), $payload  
  3.                );  
  1. $this->createClassCallable($listener)  
而这一步返回一个数组 [$listener,'handle'],通过call_user_func_array 函数实现了上述过程。

总结:在boot方法中实现事件监听者的注册与事件的绑定,而在注册与绑定过程中,将会封装一个闭包,并且在闭包中实现调用事件监听者类中的handle方法。

事件触发

事件触发采用如下形式:

  1. Event::fire(new TestEvent());  
在调用fire方法前,会实例化事件类,进行事件的逻辑操作。同样的我们在\Illuminate\Events\Dispatcher找到fire方法如下:
  1. public function fire($event$payload = [], $halt = false)  
  2.    {  
  3.        return $this->dispatch($event$payload$halt);  
  4.    }  
其实fire方法调用的还是dispatch方法。
  1.  public function dispatch($event$payload = [], $halt = false)  
  2.     {  
  3.         // When the given "event" is actually an object we will assume it is an event  
  4.         // object and use the class as the event name and this event itself as the  
  5.         // payload to the handler, which makes object based events quite simple.  
  6.         list($event$payload) = $this->parseEventAndPayload(  
  7.             $event$payload  
  8.         );  
  9. // -----------------------------这部分涉及广播系统,以后会详细讲解,这里就不讲解了。  
  10.         if ($this->shouldBroadcast($payload)) {  
  11.             $this->broadcastEvent($payload[0]);  
  12.         }  
  13. //------------------------------  
  14.         $responses = [];  
  15.   
  16.         foreach ($this->getListeners($eventas $listener) {  
  17.             //$listener 是一个闭包  
  18.             $response = $listener($event$payload);  
  19.             // If a response is returned from the listener and event halting is enabled  
  20.             // we will just return this response, and not call the rest of the event  
  21.             // listeners. Otherwise we will add the response on the response list.  
  22.             if (!is_null($response) && $halt) {  
  23.                 return $response;  
  24.             }  
  25.   
  26.             // If a boolean false is returned from a listener, we will stop propagating  
  27.             // the event to any further listeners down in the chain, else we keep on  
  28.             // looping through the listeners and firing every one in our sequence.  
  29.             if ($response === false) {  
  30.                 break;  
  31.             }  
  32.   
  33.             $responses[] = $response;  
  34.         }  
  35.   
  36.         return $halt ? null : $responses;  
  37.     }  
由于传进来的参数是实例化后的event 对象,所以在dispatch方法中首先通过parseEventAndPayload方法解析出字符串类型的事件名称和数组类型的(元素为事件对象)$payload 。然后根据事件名称 从之前保存过的
  1. $this->listeners  
数组中解析出监听者的闭包,然后通过闭包调用监听者类的handle方法。至此,事件机制原理讲解完了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值