Laravel启动分析(四) 事件模型

320 阅读2分钟

我们了解Laravel的事件模式,需要先了解观察者模式

观察者模式的详细介绍 www.jianshu.com/p/4be5de46e…

观察者模式介绍

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

观察者模式实现

观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。

观察者的代码实现

class Subject
{
    private $subject = [];

    public function notify()
    {
        $this->notifyAllObservers();
    }

    public function attach(Observer $observer)
    {
        array_push($this->subject,$observer);
    }

    public function notifyAllObservers()
    {
        foreach ($this->subject as $v) {
            $v->update();
        }
    }
}

abstract class Observer
{
    protected $subject;

    public abstract function update();
}

class BinaryObserver extends Observer
{
    public $subject;
    public function __construct(Subject $subject)
    {
        $this->subject = $subject;
        $this->subject->attach($this);
    }

    public function update()
    {
        echo "BinaryObserver update".PHP_EOL;
    }
}

class HexaObserver extends Observer
{
    public $subject;
    public function __construct(Subject $subject)
    {
        $this->subject = $subject;
        $this->subject->attach($this);
    }

    public function update()
    {
        echo "HexaObserver update".PHP_EOL;
    }
}

//测试
$subject = new Subject();
new BinaryObserver($subject);
new HexaObserver($subject);
$subject->notify();

有了上面的观察者模式的基础之后,我们使用一个简单的方式来模拟实现Laravel的事件机制

class Params
{
    public static $listens = []; //保存已经实例化的对象
}

$listen = [
    "RegisterEvent" => [
        "LoggerListen",
        "RecordListen",
    ]
];
//循环这个数组
foreach ($listen as $event => $listeners) {
    foreach ($listeners as $listener) {
        event($event, $listener);
    }
}

fire(new RegisterEvent("xioabai", "15"));


/**
 * 绑定事件
 * @param $event
 * @param $listener
 */
function event($event, $listener)
{
    Params::$listens[$event][] = make($listener);
}

/**
 * 使用反射来获取到类
 * @param $listen
 */
function make($listen)
{
    $ref = new \ReflectionClass($listen);
    $bool = $ref->getMethod("handler");
    if (empty($bool)) {
        throw new Exception("不存在handler方法");
    }
    return $ref->newInstance();
}

//事件启动
function fire($eventLogic)
{
    //如果是个类 获取类名
    if (is_object($eventLogic)) {
        $listStr = get_class($eventLogic);
    } else {
        if (is_string($eventLogic)) {
            $listStr = $eventLogic; //如果是字符串的话 需要实例化
        }
    }
    foreach (Params::$listens[$listStr] as $event => $listen) {
        call_user_func_array([$listen, "handler"], [$eventLogic]);
    }
}

//事件类
class RegisterEvent
{
    public $name;
    public $age;

    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
    }
}

//监听
class LoggerListen
{
    public function __construct()
    {
    }

    public function handler(RegisterEvent $event)
    {
        echo "LoggerListen" . $event->age;
        echo "LoggerListen" . $event->name;
    }
}
//监听
class RecordListen
{
    public function __construct()
    {

    }

    public function handler(RegisterEvent $event)
    {
        echo "RecordListen" . $event->age;
        echo "RecordListen" . $event->name;
    }
}

上面的代码跟Laravel的代码实现已经很接近了,接下来我们粗略的看看Laravel事件的实现大致代码

//Providers/EventServiceProvider.php
class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        'App\Events\Event' => [
            'App\Listeners\EventListener',
        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
     
    //在前面我们知道服务提供者register之后会调用boot,我们去看他的父类的实现方法
    public function boot()
    {
        parent::boot();
    }
}

//Illuminate/Foundation/Support/Providers/EventServiceProvider.php
public function boot()
{   
    //子类定义的$listen
    foreach ($this->listens() as $event => $listeners) {
        foreach ($listeners as $listener) {
            //调用Listen方法
            Event::listen($event, $listener);
        }
    }

    foreach ($this->subscribe as $subscriber) {
        Event::subscribe($subscriber);
    }
}

//Illuminate/Events/Dispatcher.php
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);
        }
    }
}
//Illuminate/Events/Dispatcher.php
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));
    };
}

//Illuminate/Events/Dispatcher.php
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
        );
    };
}

//Illuminate/Events/Dispatcher.php
protected function createClassCallable($listener)
{
    list($class, $method) = $this->parseClassCallable($listener);

    if ($this->handlerShouldBeQueued($class)) {
        return $this->createQueuedHandlerCallable($class, $method);
    }

    return [$this->container->make($class), $method];
}
//Illuminate/Events/Dispatcher.php
//调用回调函数的handle方法
protected function parseClassCallable($listener)
{
    return Str::parseCallback($listener, 'handle');
}

//Illuminate/Events/Dispatcher.php
protected function createClassCallable($listener)
{
    list($class, $method) = $this->parseClassCallable($listener);

    //如果继承了ShouldQueue 事件就走队列
    if ($this->handlerShouldBeQueued($class)) {
        return $this->createQueuedHandlerCallable($class, $method);
    }
    //make 获取对下对类的实力
    return [$this->container->make($class), $method];
}

以上大致分为三个步骤

  1. Laravel拿到我们定义的数组
protected $listen = [
        'App\Events\Event' => [
            'App\Listeners\EventListener',
        ],
    ];
  1. 对这个数组进行Make获取到实例化的对象,返回回调函数,并且回调函数里面执行handle方法
  2. 将根据事件和监听器对应的关系保存在this->listeners[event][]数组

以上就是事件的注册,接下来我们看看Laravel是怎么调用这些事件的

//Laravel事件的调用
Event::fire(new Observer()); //调用事件,传递我们的事件对象

//Illuminate\Events\Dispatcher.php
public function fire($event, $payload = [], $halt = false)
{
    return $this->dispatch($event, $payload, $halt);
}

//Illuminate\Events\Dispatcher.php
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.
        //如果传递了halt参数,则不会再调用其他事件了,直接返回
        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.
        //如果返回的false将不再调用
        if ($response === false) {
            break;
        }

        $responses[] = $response;
    }

    return $halt ? null : $responses;
}

可以看出事件的执行,其实就是执行上面创建的数组的回调函数,至此Laravel事件的分析就到此为止