Home > Backend Development > PHP Tutorial > Interpretation of Laravel event system

Interpretation of Laravel event system

不言
Release: 2023-04-02 17:00:01
Original
2070 people have browsed it

This article mainly introduces the interpretation of Laravel event system, which has certain reference value. Now I share it with everyone. Friends in need can refer to

Event system

Laravel's Events provides a simple observer implementation that can subscribe to and listen to various events that occur in the application. The event mechanism is a good way to decouple applications, because an event can have multiple listeners that are independent of each other. The event system in laravel consists of two parts. One is the name of the event. The name of the event can be a string, such as event.email, or it can be an event class, such as App\Events\OrderShipped; The other is the event listener listener, which can be a closure or a listening class, such as App\Listeners\SendShipmentNotification.

We still use the example given in the official documentation to analyze the source code implementation of the event system. However, before the application registers events and listeners, Laravel will first register the event processing when the application starts.eventsService.

Laravel registration event service

Among the basic services registered when the Laravel application is created, there is Eventservice

namespace Illuminate\Foundation;

class Application extends Container implements ...
{
    public function __construct($basePath = null)
    {
        ...
        $this->registerBaseServiceProviders();
        ...
    }
    
    protected function registerBaseServiceProviders()
    {
        $this->register(new EventServiceProvider($this));

        $this->register(new LogServiceProvider($this));

        $this->register(new RoutingServiceProvider($this));
    }
}
Copy after login

EventServiceProvider is /Illuminate/Events/EventServiceProvider

public function register()
{
    $this->app->singleton('events', function ($app) {
        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
            return $app->make(QueueFactoryContract::class);
        });
    });
}
Copy after login

Illuminate\Events\Dispatcher is the real implementation class of events service, and Event The facade is a static proxy for the events service, and event system-related methods are provided by Illuminate\Events\Dispatcher.

Registering events and monitoring in applications

We still use the example given in the official documentation to analyze the source code implementation of the event system. There are two ways to register events and listeners,App\Providers\EventServiceProvider There is a listen array containing all events (keys) and the listeners (values) corresponding to the events to register all event listeners, which can be flexibly configured according to needs. Add event.

/**
 * 应用程序的事件监听器映射。
 *
 * @var array
 */
protected $listen = [
    'App\Events\OrderShipped' => [
        'App\Listeners\SendShipmentNotification',
    ],
];
Copy after login

You can also register event-based closures in the boot method of the App\Providers\EventServiceProvider class.

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

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

You can see that the main job of the \App\Providers\EventProvider class is to register events in the application. The main function of this registration class is to start the event system. This class inherits from \Illuminate\Foundation\Support\Providers\EventServiceProvide.

We said when we added the service provider that after registering all services, the Laravel application will call the boot of all Providers through \Illuminate\Foundation\Bootstrap\BootProviders method to start these services, so the registration of events and listeners in Laravel applications occurs in the boot method of the \Illuminate\Foundation\Support\Providers\EventServiceProvide class, Let’s take a look:

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);
    }
}
Copy after login

You can see that the event system is started through the listening and subscription methods of the events service to create events, corresponding listeners, and event subscribers in the system .

namespace Illuminate\Events;
class Dispatcher implements DispatcherContract
{
    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);
            }
        }
    }
    
    protected function setupWildcardListen($event, $listener)
    {
        $this->wildcards[$event][] = $this->makeListener($listener, true);
    }
}
Copy after login

Event names containing wildcards will be uniformly put into the wildcards array. makeListener is used to create the listener corresponding to the event. :

class Dispatcher implements DispatcherContract
{
    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));
            }
        };
    }
}
Copy after login

When creating listener, it will be judged whether the listening object is a listening class or a closure function.

For closure listening, makeListener will wrap another layer and return a closure function as the event listener.

For the listening class, the listener will continue to be created through createClassListener

class Dispatcher implements DispatcherContract
{
    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
                );
            }
        };
    }

    protected function createClassCallable($listener)
    {
        list($class, $method) = $this->parseClassCallable($listener);

        if ($this->handlerShouldBeQueued($class)) {
            //如果当前监听类是队列的话,会将任务推送给队列
            return $this->createQueuedHandlerCallable($class, $method);
        } else {
            return [$this->container->make($class), $method];
        }
    }
}
Copy after login

For creating the listener through the string of the listening class, a closure is also returned , if the current listening class is to execute a queue task, the returned closure will push the task to the queue after execution. If it is a normal listening class, the closure returned will make the listening object and execute the of the object. handle method. Therefore, the listener returns the closure in order to wrap the context when the event is registered, and call the closure to perform the task when waiting for the event to be triggered.

After the listener is created, it will be placed in the array with the corresponding event name as the key in the listener array. An event name in the listener array corresponds to There can be multiple listener in the array, just like the observers array in the Subject class when we talked about the observer pattern before, but Laravel is more complicated than that Some, its listener array will record the corresponding relationship between multiple Subject and the corresponding Observer.

Trigger event

You can use the event name or event class to trigger the event. When triggering the event, use Event::fire(new OrdershipmentNotification), which also comes from eventsService

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

public function dispatch($event, $payload = [], $halt = false)
{
    //如果参数$event事件对象,那么就将对象的类名作为事件名称,对象本身作为携带数据的荷载通过`listener`方法
    //的$payload参数的实参传递给listener
    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);

        //如果触发事件时传递了halt参数,并且listener返回了值,那么就不会再去调用事件剩下的listener
        //否则就将返回值加入到返回值列表中,等所有listener执行完了一并返回
        if ($halt && ! is_null($response)) {
            return $response;
        }
        //如果一个listener返回了false, 那么将不会再调用事件剩下的listener
        if ($response === false) {
            break;
        }

        $responses[] = $response;
    }

    return $halt ? null : $responses;
}

protected function parseEventAndPayload($event, $payload)
{
    if (is_object($event)) {
        list($payload, $event) = [[$event], get_class($event)];
    }

    return [$event, Arr::wrap($payload)];
}

//获取事件名对应的所有listener
public function getListeners($eventName)
{
    $listeners = isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [];

    $listeners = array_merge(
        $listeners, $this->getWildcardListeners($eventName)
    );

    return class_exists($eventName, false)
                ? $this->addInterfaceListeners($eventName, $listeners)
                : $listeners;
}
Copy after login

After the event is triggered, all listener closures corresponding to the event name will be found from the listeners generated by the previously registered event. These closures are then called to perform tasks in the listener. What needs to be noted is:

  • If the event name parameter is an event object, the class name of the event object will be used as the event name, and itself will be passed to the listener as a time parameter.

  • If the halt parameter is passed when the event is triggered, after the listener returns non-false, the event will not continue to propagate to the remaining listeners, otherwise all The return value of the listener will be returned uniformly as an array after all listeners are executed.

  • If a listener returns a Boolean value false then the event will immediately stop propagating to the remaining listeners.

The principle of Laravel's event system is still the same as the observer pattern mentioned before, but the author of the framework is very skilled and cleverly combines and applies closures to implement the event system, as well as for For events that require queue processing, application events can use the principle of dispersion of concerns to effectively decouple the code logic in the application in some more complex business scenarios. Of course, application events are not suitable for writing code under all circumstances. I wrote before I have written an article on event-driven programming to explain the application scenarios of events. If you are interested, you can read it.

The above is the entire content of this article. I hope it will be helpful to everyone's study. For more related content, please pay attention to the PHP Chinese website!

Related recommendations:

Laravel User Authentication System (Basic Introduction)

Laravel5.5 and above multi-environment. env configuration reading

The above is the detailed content of Interpretation of Laravel event system. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template