我实施了ListenerAggregateInterface
来监听调度(MvcEvent::EVENT_DISPATCH
)事件。
public function attach(EventManagerInterface $events) {
$this->listeners[] = $events->attach(
MvcEvent::EVENT_DISPATCH,
array($this, 'shortCircuit'),
101
);
$this->listeners[] = $events->attach(
MvcEvent::EVENT_DISPATCH,
array($this, 'listener1'),
33
);
$this->listeners[] = $events->attach(
MvcEvent::EVENT_DISPATCH,
array($this, 'listener2'),
33
);
}
如果shortCiruit
中的某些条件为true
,则应跳过其余的侦听器。因此,我致电ListenerAggregateInterface::detach
删除所有听众。
public function shortCircuit(MvcEvent $e) {
if(condition) {
$this->detach($e->getApplication()->getEventManager());
}
}
我原本以为他们现在不再被执行了,但事实并非如此。
答案 0 :(得分:6)
在调度侦听器时,为当前触发的事件分离侦听器不起作用。这是因为the listeners are collected and sorted before they are actually executed是为了按优先级对它们进行排序。
停止传播也不适用于您当前的方法,因为您只想禁用一组特定的侦听器。
但是有一个解决方案,即仅在需要时跳过监听器,使用小型注册表记录聚合侦听器要跳过的事件。我把它写在了我的头顶,所以它没有经过测试,如果你想使用它,请仔细测试它:
use SplObjectStorage;
use Zend\EventManager\EventInterface;
final class SkippedEventsRegistry {
/** @var SplObjectStorage */
private $skippedEvents;
public function __construct() {
$this->skippedEvents = new SplObjectStorage();
}
/**
* @param callable $callback
* @return callable
*/
public function buildCallback(callable $callback)
{
return function ($event) use ($callback) {
if (isset($this->skippedEvents[$event])) {
return;
}
return $callback($event);
};
}
public function skipListenersForEvent(EventInterface $event) {
$this->skippedEvents[$event] = $event;
}
public function restoreListenersForEvent(EventInterface $event) {
unset($this->skippedEvents[$event]);
}
}
然后我们在聚合侦听器中使用此注册表:
use Zend\EventManager\EventInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\ListenerAggregateTrait;
class MyAggregateListener implements ListenerAggregateInterface {
use ListenerAggregateTrait;
private $skippedEvents;
public function __construct() {
$this->skippedEvents = new SkippedEventsRegistry();
}
public function attach(EventManagerInterface $events) {
$this->listeners[] = $events->attach('SomeEvent',
$this->skippedEvents->buildCallback(function ($event) {
// ... do other things here ...
if ($worldIsExploding) {
// start skipping the other listeners
$this->skippedEvents->skipListenersForEvent($event);
}
}), 9999);
$this->listeners[] = $events->attach('SomeEvent',
$this->skippedEvents->buildCallback(function ($event) {
// ... do other things here ...
}), 8888);
// reset normal execution
// (sadly, happens only if propagation wasn't stopped)
$this->listeners[] = $events->attach(
'SomeEvent',
[$this->skippedEvents, 'restoreListenersForEvent'],
-9999999999
);
}
}
(很抱歉对齐混乱,但很难让溢出中的所有东西都适合:\)
正如您所看到的,当事件被标记为"跳过"时,我们只是阻止侦听器被执行。通过注册表。在第一次侦听器执行期间$worldIsExploding = true
发生这种情况。
之后,我们执行所有其他侦听器并在最后通过低优先级侦听器进行清理。
最后,您还可以在具有高优先级的事件侦听器中调用$this->skippedEvents->restoreListenersForEvent($event)
。即使同一个$event
实例与多个Zend\EventManager\EventManagerInterface#trigger()
调用一起使用,也会阻止侦听器被跳过。
答案 1 :(得分:0)
无需将自己的短路视为the event manager already provides this functionality 。您需要做的就是在事件监听器中使用$event->stopPropagation(true);
。
例如
public function shortCircuit(MvcEvent $e) {
if(condition) {
$e->stopPropagation(true);
}
}