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
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.events
Service.
Among the basic services registered when the Laravel application is created, there is Event
service
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)); } }
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); }); }); }
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
.
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', ], ];
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) { // }); }
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); } }
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); } }
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)); } }; } }
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]; } } }
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
.
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 events
Service
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; }
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!