Laravel 框架的事件机制

栏目: PHP · 发布时间: 8年前

内容简介:Laravel 框架的事件机制

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

建立事件

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

Laravel 框架的事件机制

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

事件触发前

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

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

其实就是获取$listens属性数组并且进行遍历,将数组中的事件(键)和事件监听者(数组的键值)进行绑定。为了查看绑定的过程,我 们又要查看Event::listen  这个方法。由于调用的是facade,我们根据facade模式寻找到对应的类(facade模式原理请查看博文:http://blog.csdn.net/qq_16877261/article/details/77530081)  \Illuminate\Events\Dispatcher找到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);
            }
        }
    }

在这里我就不研究通配符形式的事件与事件监听者的绑定。于是其实代码通过

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

进行了绑定。我查看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);
            } else {
                return $listener(...array_values($payload));
            }
        };
    }

由于我们注册的事件监听者是字符串,代码运行到这一步。

return $this->createClassListener($listener, $wildcard);

于是跟踪函数代码如下:

public function createClassListener($listener, $wildcard = false)
    {
        //将监听者封装成闭包
        return function ($event, $payload) use ($listener, $wildcard) {
            if ($wildcard) {
                return call_user_func($this->createClassCallable($listener), $event, $payload);
            } else {
                return call_user_func_array(
                    $this->createClassCallable($listener), $payload
                );
            }
        };
    }

在这一步,函数将事件监听者封装成闭包的形式。在闭包里面通过如下代码实现了调用事件监听者的handle方法,以便在触发事件后调用闭包从而执行该方法。

return call_user_func_array(
                    $this->createClassCallable($listener), $payload
                );
$this->createClassCallable($listener)

而这一步返回一个数组 [$listener,'handle'],通过call_user_func_array 函数实现了上述过程。

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

事件触发

事件触发采用如下形式:

Event::fire(new TestEvent());

在调用fire方法前,会实例化事件类,进行事件的逻辑操作。同样的我们在\Illuminate\Events\Dispatcher找到fire方法如下:

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

其实fire方法调用的还是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) {
            //$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 (!is_null($response) && $halt) {
                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;
    }

由于传进来的参数是实例化后的event 对象,所以在dispatch方法中首先通过parseEventAndPayload方法解析出字符串类型的事件名称和数组类型的(元素为事件对象)$payload 。然后根据事件名称 从之前保存过的

$this->listeners

数组中解析出监听者的闭包,然后通过闭包调用监听者类的handle方法。至此,事件机制原理讲解完了。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Collective Intelligence

Programming Collective Intelligence

Toby Segaran / O'Reilly Media / 2007-8-26 / USD 39.99

Want to tap the power behind search rankings, product recommendations, social bookmarking, and online matchmaking? This fascinating book demonstrates how you can build Web 2.0 applications to mine the......一起来看看 《Programming Collective Intelligence》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具