我实现了一个ListenerAggregateInterface用于侦听dispatch(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
);
}
Run Code Online (Sandbox Code Playgroud)
如果某些条件shortCiruit是true其余的听众应该被跳过.因此,我打电话ListenerAggregateInterface::detach删除所有听众.
public function shortCircuit(MvcEvent $e) {
if(condition) {
$this->detach($e->getApplication()->getEventManager());
}
}
Run Code Online (Sandbox Code Playgroud)
我原以为他们现在不再被执行,但事实并非如此.
在调度侦听器时,为当前触发的事件分离侦听器将不起作用.这是因为侦听器在实际执行之前被收集和排序,以便按优先级对它们进行排序.
停止传播也不适用于您当前的方法,因为您只想禁用一组特定的侦听器.
但是有一个解决方案,即仅在需要时跳过监听器,使用小型注册表记录聚合侦听器要跳过的事件.我把它写在了我的头顶,所以它没有经过测试,如果你想使用它,请仔细测试它:
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]);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们在聚合侦听器中使用此注册表:
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
);
}
}
Run Code Online (Sandbox Code Playgroud)
(很抱歉对齐混乱,但是很难将所有内容都装在溢出中:\)
正如您所看到的,当事件被标记为通过注册表"跳过"时,我们只是阻止侦听器被执行.这发生$worldIsExploding = true在第一次监听器执行期间.
之后,我们执行所有其他侦听器并在最后通过低优先级侦听器进行清理.
最终,您还可以调用$this->skippedEvents->restoreListenersForEvent($event)具有高优先级的事件侦听器.即使同一个$event实例与多个Zend\EventManager\EventManagerInterface#trigger()调用一起使用,也会阻止侦听器被跳过.
| 归档时间: |
|
| 查看次数: |
446 次 |
| 最近记录: |