介绍
参考zf2的EventManager,结合实际项目中的需要写了一个简易的EventManager,主要功能有:
- 订阅者模式;
- 拦截器;
- 事件驱动
具体概念就不介绍了,先来看看模块中的几个类或接口。
EventManager 类
EventManager是核心模块,主要负责为监听事件,添加拦截器,触发事件,下面看一个例子:
$em = new EventManager();
$em->attach('start',function($e){
printf('the parameter is %s',json_encode($e));
});
$params = array('foo' => 'bar', 'baz' => 'bat');
$em->trigger('start',$params);
输出结果为:
the parameter is {"foo":"bar","baz":"bat"}
attach方法的原型为:
/**
* attach a listener to an event
*
* @param $event string|array
* @param $callback callable
* @param $priority int
*/
public function attach($event,$callback,$priority=1)
{
if(is_array($event))
{
foreach($event as $name)
{
$this->attach($name,$callback,$priority);
}
return;
}
if(!$this->events[$event])
$this->events[$event] = array();
if(!$this->events[$event][self::LISTENER])
$this->events[$event][self::LISTENER] = new SplPriorityQueue();
$listener = new Listener($event,$callback,intval($priority));
$this->events[$event][self::LISTENER]->insert($listener,$priority);
}
三个参数分别为:
- 需要监听的事件名,可以是多个事件名组成的数组,事件名可以含有通配符;
- 监听器,任何合法的PHP回调都能作为监听器;
- 优先级,一个事件有多个监听器时优先级高的监听器先被触发。
定义了拦截器需要实现的两个方法:before和after,before方法会先于监听器调用,after方法会在最后被调用,例子如下:
class Interceptor implements InterceptorInterface
{
public function before($e=null)
{
printf('before ');
}
public function after($e=null)
{
printf('after ');
}
}
$em = new EventManager();
$em->attach('start',function($e){
printf('start ');
});
$em->attach('sta*',function($e){
printf('sta* ');
},3);
$em->intercept('start',new Interceptor());
$em->trigger('start');
输出结果为:
before sta* start after
另外还有Listener类和StaticEventManager,后者是一个全局的EventManager,具体实现可查看代码。
<?
interface InterceptorInterface
{
public function before();
public function after();
}
class Listener
{
protected $event = null;
protected $callback = null;
protected $priority = 1;
public function __CONSTRUCT($event,$callback,$priority)
{
$this->event = $event;
$this->callback = $callback;
$this->priority = $priority;
}
public function setEvent($event)
{
$this->event = $event;
}
public function getEvent()
{
return $this->event;
}
public function setCallBack($callback)
{
$this->callback = $callback;
}
public function getCallBack()
{
return $this->callback;
}
public function setPriority($priority)
{
$this->priority = $priority;
}
public function getPriority()
{
return $this->priority;
}
}
class StaticEventManager
{
protected static $instance = null;
protected function __CONSTRUCT(){}
static public function ins()
{
if(self::$instance === null)
{
self::$instance = new EventManager();
}
return self::$instance;
}
}
class EventManager
{
/**
* type
*/
const LISTENER = 0;
const INTERCEPTOR = 1;
/**
* Subscribed events and their listeners and interceptors
*/
protected $events = array();
/**
* intercept the event with an interceptor
*
* @param $event string|array
* @param $interceptor InterceptorInterface
*/
public function intercept($event,InterceptorInterface $interceptor)
{
if(is_array($event))
{
foreach($event as $name)
{
$this->Intercept($name,$aopObj);
}
return;
}
if(!$this->events[$event])
$this->events[$event] = array();
if(!$this->events[$event][self::INTERCEPTOR])
$this->events[$event][self::INTERCEPTOR] = array();
$this->events[$event][self::INTERCEPTOR][] = $interceptor;
}
/**
* attach a listener to an event
*
* @param $event string|array
* @param $callback callable
* @param $priority int
*/
public function attach($event,$callback,$priority=1)
{
if(is_array($event))
{
foreach($event as $name)
{
$this->attach($name,$callback,$priority);
}
return;
}
if(!$this->events[$event])
$this->events[$event] = array();
if(!$this->events[$event][self::LISTENER])
$this->events[$event][self::LISTENER] = new SplPriorityQueue();
$listener = new Listener($event,$callback,intval($priority));
$this->events[$event][self::LISTENER]->insert($listener,$priority);
}
/**
* trigger listeners of an event
*
* @param $event string|array
* @param $params
*/
public function trigger($event,$params=null)
{
if(is_array($event))
{
foreach($event as $name)
{
$this->trigger($name,$callback,$priority);
}
return;
}
$matchEvents = $this->getMatchEvents($event);
$interceptors = array();
$listeners = new SplPriorityQueue();
foreach($matchEvents as $e)
{
$interceptors = array_merge($interceptors,$this->getInterceptors($e));
$this->insertListeners($listeners,$this->getListeners($e));
}
foreach($interceptors as $interceptor)
{
$interceptor->before($params);
}
$this->triggerListeners($listeners,$params);
foreach($interceptors as $interceptor)
{
$interceptor->after($params);
}
}
/**
* retrieve all registered events
*
* @return array
*/
public function getEvents()
{
return array_keys($this->events);
}
/**
* retrieve all matched events for a given event
*
* @return array
*/
public function getMatchEvents($event)
{
$allEvents = $this->getEvents();
$matchEvents = array();
foreach($allEvents as $key)
{
if(preg_match('/'.$key.'/i',$event))
{
$matchEvents[] = $key;
}
}
return $matchEvents;
}
/**
* retrieve all listeners for a given event
*
* @param $event string
* @return SplPriorityQueue
*/
public function getListeners($event)
{
$listeners = new SplPriorityQueue();
if($this->events[$event][self::LISTENER])
$listeners = $this->events[$event][self::LISTENER];
return $listeners;
}
/**
* retrieve all interceptors for a given event
*
* @param $event string
* @return array
*/
protected function getInterceptors($event)
{
$interceptors = array();
if($this->events[$event][self::INTERCEPTOR])
$interceptors = $this->events[$event][self::INTERCEPTOR];
return $interceptors;
}
/**
* Trigger listeners
*
* @param $listeners array
* @param $params
*/
protected function triggerListeners($listeners,$params=null)
{
foreach($listeners as $listener)
{
call_user_func($listener->getCallBack(),$params);
}
}
/**
* Add listeners to the master queue of listeners
*
* @param PriorityQueue $masterListeners
* @param PriorityQueue $listeners
* @return void
*/
protected function insertListeners($masterListeners, $listeners)
{
if (!count($listeners)) {
return;
}
foreach ($listeners as $listener) {
$priority = $listener->getPriority();
if (null === $priority) {
$priority = 1;
}
$masterListeners->insert($listener, $priority);
}
}
}
欢迎交流,如果您有更好的建议,请不吝赐教。