<?php
/**
* @license SILK SOFTWARE HOUSE SP Z O O
*/
namespace App\EventSubscriber;
use App\Annotation\AppCache;
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Doctrine\Common\Annotations\Reader;
use ReflectionClass;
use ReflectionException;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Cache\TagAwareCacheInterface as CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
/**
* Class AppCacheSubscriber.
*/
class AppCacheSubscriber implements EventSubscriberInterface
{
/**
* Annotation reader.
*
* @var Reader
*/
private $annotationReader;
/**
* Cache.
*
* @var CacheInterface
*/
private $cache;
/**
* Cache key.
*
* @var string
*/
private $key;
/**
* Security.
*
* @var Security
*/
private $security;
/**
* AppCacheSubscriber constructor.
*
* @param CacheInterface $cache
* @param Reader $reader
* @param Security $security
*/
public function __construct(CacheInterface $cache, Reader $reader, Security $security)
{
$this->cache = $cache;
$this->annotationReader = $reader;
$this->security = $security;
}
/**
* Get subscribed events.
*
* @return array
*/
public static function getSubscribedEvents()
{
// return the subscribed events, their methods and priorities
return [
KernelEvents::CONTROLLER_ARGUMENTS => [
['onKernelController', 1],
],
KernelEvents::RESPONSE => [
['onKernelResponse', 10],
],
];
}
/**
* On kernel controller.
*
* @param ControllerArgumentsEvent $event
*/
public function onKernelController(ControllerArgumentsEvent $event)
{
$annotation = $this->getAppCacheAnnotation($event->getController());
if (!$event->getRequest()->isMethod('GET') || !$this->security->getUser() || null === $annotation) {
return;
}
$this->key = 'measurement_'.$event->getRequest()->get('id').'_'.preg_replace("/[^A-Za-z0-9 ]/", '', $event->getRequest()->getPathInfo()).$this->security->getUser()->getId();
if ($cached = $this->cache->getItem($this->key)->get()) {
$event->setController(function () use ($cached) {
return $cached;
});
$event->stopPropagation();
}
}
/**
* On kernel response.
*
* @param ResponseEvent $event
*/
public function onKernelResponse(ResponseEvent $event)
{
if (200 !== $event->getResponse()->getStatusCode()) {
return;
}
if (!isset($this->key)) {
return;
}
$tag = 'measurement_'.$event->getRequest()->get('id');
$response = $event->getResponse();
$this->cache->get($this->key, function (ItemInterface $item) use ($response, $tag) {
$item->tag([$tag, 'user_'.$this->security->getUser()->getId()]);
return $response;
});
}
/**
* Does controller have idempotent annotation.
*
* @param callable $controllerData
*
* @return AppCache|null
*/
private function getAppCacheAnnotation($controllerData)
{
$annotation = null;
if (!is_array($controllerData)) {
return null;
}
list($controllerObject, $methodName) = $controllerData;
try {
$controller = new ReflectionClass($controllerObject);
$method = $controller->getMethod($methodName);
$annotation = $this->annotationReader->getMethodAnnotation($method, AppCache::class);
} catch (ReflectionException $e) {
return null;
}
return $annotation instanceof AppCache ? $annotation : null;
}
}