汽车4s网站设计谷歌seo推广招聘
大家好,这篇文章将通过我在实际开发工作中的例子,来介绍Symfony的EventDispatcher组件的使用及实现原理。
这个组件在实际开发过程中非常的有用,它能够使代码的业务逻辑变的非常清晰,增加代码的复用性,代码的耦合性也大大降低。
简介
具体的介绍大家可以查看官方的文档,下面是文档地址。
文档地址
组成
一个 dispatcher 对象,保存了事件名称和其对应监听器
一个 event,有一个全局唯一的事件名称。包含一些在订阅器里需要访问的对象。
使用示例
1. 初始化,添加相应监听事件
# 初始时,添加监听器
$dispatcher = new EventDispatcher();$disptacher->addSubscriber(new BIReportSubscriber()); // BI上报功能
$disptacher->addSubscriber(new MediaPlayerSubscriber()); // 维护播放器信息统一
Symfony\Component\EventDispatcher\EventDispatcher
2. 监听的事件
class BIReportSubscriber implements EventSubscriberInterface
{public static function getSubscribedEvents (){// 监听的不同事件,当事件触发时,会调用 onResponse 方法return [MusicResponseEvent::NAME => 'onResponse', ChildrenResponseEvent::NAME => 'onResponse',FmResponseEvent::NAME => 'onResponse',NewsResponseEvent::NAME => 'onResponse',];}public function onResponse(AResponseEvent $event){/** 一些具体的业务逻辑* 进行 BI 上报*/}
class MediaPlayerSubscriber implements EventSubscriberInterface
{public static function getSubscribedEvents (){return [MusicResponseEvent::NAME => 'onResponse',FmResponseEvent::NAME => 'onResponse',ChildrenResponseEvent::NAME => 'onResponse',NewsResponseEvent::NAME => 'onResponse',];}public function onResponse(AResponseEvent $event){/** 一些具体的业务逻辑* 维护播放器信息统一*/}
实现 getSubscribedEvents 方法,完成事件的绑定。当事件触发时,dispatcher 会调用绑定的方法,并将抛出的事件当做参数传入。
事件绑定的方法 onResponse 可以是任何名字。
在 onResponse 方法中,通过 $event 获取要操作的对象。
3. 事件代码
class FmResponseEvent extends Event
{const NAME = 'fm.response'; // 事件名,事件的唯一标识protected $request; // 在监听器里要操作的对象protected $response; // 在监听器里要操作的对象public function __construct (Request $request, Response $response){$this->request = $request;$this->response = $response;}/*** @return Request*/public function getRequest(){return $this->request;}/*** @return Response*/public function getResponse(){return $this->response;}
}
- 继承
Symfony\Component\EventDispatcher\Event
- 在订阅器的业务逻辑上,需要使用
$request 和 $response
对象,所以本事件包含这两个类的对象。
4. 触发事件
$event = new FmResponseEvent($request, $response);$dispatcher->dispatch($event::NAME, $event);
dispathcer
会按照优先级,依次执行订阅器中事件绑定的方法
原码解读
1 简化的 EventDispatcher 源码
class EventDispatcher implements EventDispatcherInterface
{private $listeners = array();private $sorted = array();/*** 触发事件*/public function dispatch($eventName, Event $event){if ($listeners = $this->getListeners($eventName)) {$this->doDispatch($listeners, $eventName, $event);}return $event;}/*** 根据事件名,搜索监听器*/public function getListeners($eventName){if (empty($this->listeners[$eventName])) {return array();}if (!isset($this->sorted[$eventName])) {$this->sortListeners($eventName);}return $this->sorted[$eventName];}/*** 换优先级将监听器排序* @param string $eventName*/private function sortListeners($eventName){krsort($this->listeners[$eventName]);$this->sorted[$eventName] = array();foreach ($this->listeners[$eventName] as $priority => $listeners) {foreach ($listeners as $k => $listener) {if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {$listener[0] = $listener[0]();$this->listeners[$eventName][$priority][$k] = $listener;}$this->sorted[$eventName][] = $listener;}}}protected function doDispatch($listeners, $eventName, Event $event){foreach ($listeners as $listener) {if ($event->isPropagationStopped()) {break;}\call_user_func($listener, $event, $eventName, $this);}/*** 添加订阅器*/public function addSubscriber(EventSubscriberInterface $subscriber){foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {if (is_string($params)) {$this->addListener($eventName, array($subscriber, $params));} elseif (is_string($params[0])) {$this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);} else {foreach ($params as $listener) {$this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);}}}}public function addListener($eventName, $listener, $priority = 0){$this->listeners[$eventName][$priority][] = $listener;unset($this->sorted[$eventName]);}
}
Buy me a cup of coffee :)