Zend/EventManager(Part2)

来源:互联网 发布:java错误无法加载主类 编辑:程序博客网 时间:2024/06/06 21:03

从本篇开始,我们将扎进Zend/EventManager的代码,首先介绍EventManager.php和EventManagerInterface.php

通过何谓事件/监听器机制,以及简易实现介绍,归纳出了事件/监听器要考虑的几点:
a. 事件和监听器的对应关系, 如何注册监听器,解除监听器,触发事件?
b. 在触发事件时,如何将当前上下文和参数传递给对应的监听器?
c. 在执行一个事件上的多个监听器时,如何保存和处理每个监听器的返回结果?
下面我们通过分析EventManager.php和EventManagerInterface.php的代码来回答这3个问题。

1. EventManager.php和EventManagerInterface.php的代码实现
首先看EventManagerInterface的代码:Zend/EventManager/EventManagerInterface.php,根据代码的注释来了解EventManagerInterface中定义的方法,结合下面的类图能够更快理解:
EventManager
现在去看EventManager的代码Zend/EventManager/EventManager.php,它实现了EventManagerInterface里的方法。
在EventManager中定义了属性$events = array()来保存事件和监听器的关系,这个数组以事件名称作为索引,每个事件名称又对应一个PriorityQueue, 所有注册到这个事件的监听器都保存到它的PriorityQueue中, 通过对PriorityQueue中保存的监听器指定优先级,而监听器是一个CallbackHandler的实例,这个实例中保存了事件名称和事件的$callback方法。
先只看和监听器相关的方法:触发事件trigger(),注册监听器attach(),解除监听器detach(),获取事件列表getEvents(),获取监听器getListeners(),清除监听器clearListeners(),以下是代码片段和注释:

public function attach($event, $callback = null, $priority = 1){    ...   if (empty($this->events[$event])) {        // 用事件名称$event做为$this->events数组的一个key, 初始化一个        // PriorityQueue实例,作为对应的数组值        // 所有注册到$event的监听器都放入这个PriorityQueue实例中        $this->events[$event] = new PriorityQueue();    }    // 初始化一个CallbackHandler实例,将匿名方法$callback,    // 事件名称$event和监听器的优先级$priority    // 放入这个CallbackHandler实例中    $listener = new CallbackHandler($callback, array('event' => $event,                                     'priority' => $priority));    // 将监听器放入$event对应的PriorityQueue实例中    $this->events[$event]->insert($listener, $priority);    return $listener;}public function detach($listener){    ...    $event = $listener->getMetadatum('event');    if (!$event || empty($this->events[$event])) {        return false;    }    // 将监听器从$event对应的PriorityQueue实例中移除    $return = $this->events[$event]->remove($listener);    ...}public function trigger($event, $target = null, $argv = array(),                                                   $callback = null){    ...    } else {        // 初始化一个Event实例,把事件的名称,上下文(通常是触发事件的方法所        // 在的类实例$this)和参数(由触发事件的方法传入)放入Event实例中        $e = new $this->eventClass();        $e->setName($event);        $e->setTarget($target);        $e->setParams($argv);    }    ...    return $this->triggerListeners($event, $e, $callback);}protected function triggerListeners($event, EventInterface $e,                                               $callback = null){    // 初始化一个ResponseCollection,所有的监听器的执行结果都放入里面    $responses = new ResponseCollection;    $listeners = $this->getListeners($event);    ...    foreach ($listeners as $listener) {        // $listener是一个CallbackHandler的实例,通过方法getCallback()        // 获取到监听器的匿名函数        $listenerCallback = $listener->getCallback();        // 调用匿名函数,并将Event实例$e传给匿名函数,把执行结果写入$response        $responses->push(call_user_func($listenerCallback, $e));        ...        // 这里的$callback是另外一个回调函数,是触发事件时传入的一个回调函数,        // 是对监听器执行结果进行验证,        // 如果不满足验证条件,监听器的执行就结束        if ($callback && call_user_func($callback, $responses->last())) {            $responses->setStopped(true);            break;        }    }    return $responses;}   



2. 回答本文最开始的三个问题
a. 事件和监听器的对应关系, 如何注册监听器,解除监听器,触发事件?

a.1) 事件是被实例化的Event,持有事件名称$event,事件被触发时的上下文$target(即触发事件的对象)和被触发时的参数$argv;
a.2) 监听器是被实例化的CallbackHandler,持有监听器的逻辑处理函数$callback,被监听的事件名称和监听器的优先级,当事件被触发时,这里的$callback函数被调用;
a.3) 一个事件可以被注册多个监听器,这些监听器按照按照优先级被放入队列PriorityQueue的实例中;
a.4) EventManager的$events数组里以事件名称作为索引,对应的值则是这个事件的监听器队列PriorityQueue的实例;
当要注册监听器,解除监听器或者触发事件时,都要指定事件名称,EventManager通过事件名称对$events或者$events[$eventName]进行元素的插入,移除或者调用相应监听器的$callback方法;
b. 在触发事件时,如何将当前上下文和参数传递给对应的监听器?
b.1) 调用触发事件的主体(通常是一个类实例的方法)将事件名称,调用主体的当前实例和其他上下文数据以参数形式传递给EventManager::trigger()方法;
b.2) 在EventManager::trigger()里,把被传递过来的事件名称,调用主题的实例,和上下文数据放入实例$e = new Event()中, 再把$event传递给EventManager::triggerListeners($eventName, $e, $callback)方法;
b.3) EventManager::triggerListeners()根据$eventName找到注册到这个事件的所有监听器,并获取到监听器的处理函数$listenerCallback,通过调用$responses->push(call_user_func($listenerCallback, $e))来执行监听器的处理函数;
b.4) 监听器的执行函数是由开发者定义的,它在接收到事件实例$e后,能够明白在$e中保存的事件触发者传递过来的数据信息,做相应处理后,返回执行结果;
c. 在执行一个事件上的多个监听器时,如何保存和处理每个监听器的返回结果?
c.1) 在EventManager::triggerListeners()里将每个监听器的执行结果放入堆栈ResponseCollection的实例中,在执行完所有的监听器后将ResponseCollection的实例返回给事件的触发者;
c.2) 事件被触发时,还可以传递一个对监听器的执行结果进行验证的函数$callback,在EventManager::triggerListeners()执行监听器处理函数的循环中,会把每个监听器的返回结果传递给$callback执行,如果验证结果为flash则不会再去执行剩余的监听器直接返回当前的$response

0 0
原创粉丝点击