src/EventSubscriber/IdempotenceSubscriber.php line 105

Open in your IDE?
  1. <?php
  2. /**
  3.  * @license Silk Software House Sp. Z O.O.
  4.  */
  5. declare(strict_types=1);
  6. namespace App\EventSubscriber;
  7. use App\Annotation\Idempotent;
  8. use App\Entity\CachedResponse;
  9. use App\Repository\CachedResponseRepository;
  10. use Doctrine\ORM\EntityManagerInterface as EntityManager;
  11. use Symfony\Component\HttpFoundation\JsonResponse;
  12. use Symfony\Component\HttpFoundation\Response;
  13. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  14. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  15. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  16. use Symfony\Component\HttpKernel\KernelEvents;
  17. use Doctrine\Common\Annotations\Reader;
  18. use ReflectionClass;
  19. use ReflectionException;
  20. /**
  21.  * Class IdempotenceSubscriber.
  22.  */
  23. class IdempotenceSubscriber implements EventSubscriberInterface
  24. {
  25.     /**
  26.      * Cache repository.
  27.      *
  28.      * @var CachedResponseRepository
  29.      */
  30.     private $cache;
  31.     /**
  32.      * Annotation reader.
  33.      *
  34.      * @var Reader
  35.      */
  36.     private $annotationReader;
  37.     /**
  38.      * IdempotenceSubscriber constructor.
  39.      *
  40.      * @param EntityManager $entityManager
  41.      * @param Reader        $reader
  42.      */
  43.     public function __construct(EntityManager $entityManagerReader $reader)
  44.     {
  45.         $this->cache $entityManager->getRepository(CachedResponse::class);
  46.         $this->annotationReader $reader;
  47.     }
  48.     /**
  49.      * Get subscribed events.
  50.      *
  51.      * @return array
  52.      */
  53.     public static function getSubscribedEvents()
  54.     {
  55.         // return the subscribed events, their methods and priorities
  56.         return [
  57.             KernelEvents::CONTROLLER => [
  58.                 ['onKernelController', -2],
  59.             ],
  60.             KernelEvents::RESPONSE => [
  61.                 ['onKernelResponse'10],
  62.             ],
  63.         ];
  64.     }
  65.     /**
  66.      * On kernel controller.
  67.      *
  68.      * @param ControllerEvent $event
  69.      */
  70.     public function onKernelController(ControllerEvent $event)
  71.     {
  72.         if (!$event->isMasterRequest()) {
  73.             return;
  74.         }
  75.         $annotation $this->getIdempotentAnnotation($event->getController());
  76.         if (null === $annotation) {
  77.             return;
  78.         }
  79.         $idempotenceHash $event->getRequest()->getContent();
  80.         $event->getRequest()->cookies->set('idempotent'$idempotenceHash);
  81.         if ($cached $this->cache->getCachedResponse($idempotenceHash$event->getRequest()->get('_route'))) {
  82.             $event->setController(function () use ($cached) {
  83.                 return $cached;
  84.             });
  85.         }
  86.     }
  87.     /**
  88.      * On kernel response.
  89.      *
  90.      * @param ResponseEvent $event
  91.      */
  92.     public function onKernelResponse(ResponseEvent $event)
  93.     {
  94.         $requestId $event->getRequest()->cookies->get('idempotent');
  95.         if (!$requestId) {
  96.             return;
  97.         }
  98.         $response $event->getResponse();
  99.         if (!$response instanceof JsonResponse || Response::HTTP_CREATED !== $response->getStatusCode()) {
  100.             return;
  101.         }
  102.         $this->cache->cache(
  103.             $requestId,
  104.             $response->getStatusCode(),
  105.             $response->getContent(),
  106.             $event->getRequest()->get('_route')
  107.         );
  108.     }
  109.     /**
  110.      * Does controller have idempotent annotation.
  111.      *
  112.      * @param callable $controllerData
  113.      *
  114.      * @return Idempotent|null
  115.      */
  116.     private function getIdempotentAnnotation($controllerData)
  117.     {
  118.         $annotation null;
  119.         if (!is_array($controllerData)) {
  120.             return null;
  121.         }
  122.         [$controllerObject$methodName] = $controllerData;
  123.         try {
  124.             $controller = new ReflectionClass($controllerObject);
  125.             $method $controller->getMethod($methodName);
  126.             $annotation $this->annotationReader->getMethodAnnotation($methodIdempotent::class);
  127.         } catch (ReflectionException $e) {
  128.             return null;
  129.         }
  130.         return $annotation instanceof Idempotent $annotation null;
  131.     }
  132. }