Zend/EventManager(Part2)

从本篇开始,我们将扎进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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值