Last active
May 24, 2016 23:15
-
-
Save pavarnos/3b984d45427b6bf13990e17103949e0f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use ISV\Service\Cache\CacheInterface; | |
use ISV\Service\Cache\FileCache; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
use Symfony\Component\EventDispatcher\Event; | |
use Symfony\Component\EventDispatcher\EventDispatcher; | |
/** | |
* Overrides the symfony event dispatcher so that we don't call addListener() 400 times for every request when 90% | |
* of requests never dispatch an event. | |
* | |
* When dispatch() is called, we addListener() first and then dispatch(). | |
* | |
* Stole some ideas from ContainerAwareEventDispatcher | |
*/ | |
class LazyEventDispatcher extends EventDispatcher | |
{ | |
const CACHE_KEY = 'LazyEventDispatcher'; | |
// array index in $listeners | |
const SERVICE = 0; | |
const METHOD = 1; | |
const PRIORITY = 2; | |
/** @var CacheInterface */ | |
private $cache; | |
/** @var array eventName => array of ['service', 'method', 'priority'] one per listener */ | |
private $listeners = []; | |
/** @var ContainerInterface */ | |
private $container; | |
/** | |
* LazyEventDispatcher constructor. | |
* @param ContainerInterface $container | |
* @param CacheInterface $cache for unit testing: so we can inject a different cache if needed | |
*/ | |
public function __construct(ContainerInterface $container, CacheInterface $cache = null) | |
{ | |
$this->container = $container; | |
$this->cache = $cache; | |
} | |
/** | |
* @return array | |
*/ | |
public function getLazyListeners() | |
{ | |
if (empty($this->listeners)) { | |
$this->listeners = $this->getCache()->get(self::CACHE_KEY); | |
if (empty($this->listeners)) { | |
$this->listeners = []; | |
} | |
} | |
return $this->listeners; | |
} | |
/** | |
* save them to cache so we can load them later. | |
* called at compile time. | |
*/ | |
public function saveLazyListeners() | |
{ | |
ksort($this->listeners); | |
$this->getCache()->set(self::CACHE_KEY, $this->listeners); | |
} | |
/** | |
* Adds an event listener that listens on the specified events. Called by a container compiler class at build time. | |
* | |
* @param string $eventName The event to listen for | |
* @param string $serviceName usually a class name | |
* @param string $methodName eg onPersonDelete | |
* @param int $priority The higher this value, the earlier an event | |
* listener will be triggered in the chain (defaults to 0) | |
*/ | |
public function addLazyListener($eventName, $serviceName, $methodName, $priority = 0) | |
{ | |
$this->listeners[$eventName][] = [ | |
self::SERVICE => $serviceName, | |
self::METHOD => $methodName, | |
self::PRIORITY => $priority, | |
]; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function dispatch($eventName, Event $event = null) | |
{ | |
if ($this->hasListeners($eventName)) { | |
return parent::dispatch($eventName, $event); | |
} | |
$allListeners = $this->getLazyListeners(); | |
if (!isset($allListeners[$eventName])) { | |
return parent::dispatch($eventName, $event); | |
} | |
// add them then dispatch | |
foreach ($allListeners[$eventName] as $handler) { | |
$service = $this->container->get($handler[self::SERVICE]); | |
$this->addListener($eventName, [$service, $handler[self::METHOD]], $handler[self::PRIORITY]); | |
} | |
return parent::dispatch($eventName, $event); | |
} | |
/** | |
* no need to clear the cache explicitly: it will be automatically rebuilt when the container is rebuilt | |
* @return CacheInterface | |
*/ | |
private function getCache() | |
{ | |
if (empty($this->cache)) { | |
$this->cache = new FileCache(CACHE_PATH); | |
} | |
return $this->cache; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment