src/Products/SchoolNowBundle/Subscriber/ApiControllerSubscriber.php line 94

Open in your IDE?
  1. <?php
  2. namespace Products\SchoolNowBundle\Subscriber;
  3. use App\Component\ViewLayer\Views\JsonView;
  4. use Cms\CoreBundle\Service\ContextManager;
  5. use Cms\TenantBundle\Model\SimpleTenantableInterface;
  6. use Products\SchoolNowBundle\Controller\AbstractAdminApiController;
  7. use Products\SchoolNowBundle\Controller\AbstractApiController;
  8. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  9. use Symfony\Component\HttpFoundation\JsonResponse;
  10. use Symfony\Component\HttpFoundation\Response;
  11. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  12. use Symfony\Component\HttpKernel\Event\RequestEvent;
  13. use Symfony\Component\HttpKernel\Event\ViewEvent;
  14. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  15. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  16. use Symfony\Component\HttpKernel\KernelEvents;
  17. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  18. use Symfony\Component\Security\Core\Security;
  19. /**
  20.  *
  21.  */
  22. final class ApiControllerSubscriber implements EventSubscriberInterface
  23. {
  24.     // DI
  25.     protected Security $security;
  26.     protected ContextManager $cm;
  27.     /**
  28.      * {@inheritDoc}
  29.      */
  30.     public static function getSubscribedEvents(): array
  31.     {
  32.         return [
  33.             KernelEvents::REQUEST => [
  34.                 ['onKernelRequest'0],
  35.             ],
  36.             KernelEvents::CONTROLLER => [
  37.                 ['onKernelController'0],
  38.             ],
  39.             KernelEvents::EXCEPTION => [
  40.                 ['onKernelException'0],
  41.             ],
  42.             KernelEvents::VIEW => [
  43.                 ['onKernelView'0],
  44.             ],
  45.         ];
  46.     }
  47.     /**
  48.      * @param Security $security
  49.      * @param ContextManager $cm
  50.      */
  51.     public function __construct(
  52.         Security $security,
  53.         ContextManager $cm
  54.     )
  55.     {
  56.         $this->security $security;
  57.         $this->cm $cm;
  58.     }
  59.     /**
  60.      * @param string|null $route
  61.      * @return bool
  62.      */
  63.     protected function isApiRoute(?string $route): bool
  64.     {
  65.         if ( ! $route) {
  66.             return false;
  67.         }
  68.         return str_starts_with($routeAbstractApiController::ROUTING__PREFIX)
  69.             || str_starts_with($routeAbstractAdminApiController::ROUTING__PREFIX);
  70.     }
  71.     /**
  72.      * @param string|null $route
  73.      * @return bool
  74.      */
  75.     protected function isAdminApiRoute(?string $route): bool
  76.     {
  77.         if ( ! $route) {
  78.             return false;
  79.         }
  80.         return str_starts_with($routeAbstractAdminApiController::ROUTING__PREFIX);
  81.     }
  82.     /**
  83.      * @param RequestEvent $event
  84.      * @return void
  85.      */
  86.     public function onKernelRequest(RequestEvent $event): void
  87.     {
  88.         // get controller
  89.         $route $event->getRequest()->attributes->get('_route');
  90.         // make sure it is us
  91.         if ($this->isApiRoute($route) || $this->isAdminApiRoute($route)) {
  92.             // if the user is a profile, set the tenant in the context
  93.             $user $this->security->getUser();
  94.             if ($user instanceof SimpleTenantableInterface && ! $this->cm->getGlobalContext()->getTenant()) {
  95.                 $this->cm->getGlobalContext()->setTenant($user->getTenant());
  96.             }
  97.         }
  98.     }
  99.     /**
  100.      * @param ControllerEvent $event
  101.      * @return void
  102.      */
  103.     public function onKernelController(ControllerEvent $event): void
  104.     {
  105.         // get controller
  106.         $route $event->getRequest()->attributes->get('_route');
  107.         // make sure it is us
  108.         if ($this->isApiRoute($route)) {
  109.             // set timer
  110.             $event->getRequest()->attributes->set('_api_start'hrtime(true));
  111.         }
  112.     }
  113.     /**
  114.      * @param ExceptionEvent $event
  115.      * @return void
  116.      */
  117.     public function onKernelException(ExceptionEvent $event): void
  118.     {
  119.         // get controller
  120.         $route $event->getRequest()->attributes->get('_route');
  121.         // make sure it is us
  122.         if ($this->isApiRoute($route)) {
  123.             // determine the most appropriate http status code
  124.             $status Response::HTTP_INTERNAL_SERVER_ERROR;
  125.             switch (true) {
  126.                 case $event->getThrowable() instanceof AuthenticationException:
  127.                     $status Response::HTTP_UNAUTHORIZED;
  128.                     break;
  129.                 case $event->getThrowable() instanceof AccessDeniedHttpException:
  130.                     $status Response::HTTP_FORBIDDEN;
  131.                     break;
  132.             }
  133.             // assemble json response and attach to event
  134.             $event->setResponse(new JsonResponse(
  135.                 [
  136.                     'error' => [
  137.                         'status' => $status,
  138.                         'code' => $event->getThrowable()->getCode(),
  139.                         'message' => $event->getThrowable()->getMessage(),
  140.                         'file' => $event->getThrowable()->getFile(),
  141.                         'line' => $event->getThrowable()->getLine(),
  142.                         'stack' => $event->getThrowable()->getTraceAsString(),
  143.                         'previous' => $event->getThrowable()->getPrevious() ? [
  144.                             'code' => $event->getThrowable()->getPrevious()->getCode(),
  145.                             'message' => $event->getThrowable()->getPrevious()->getMessage(),
  146.                             'file' => $event->getThrowable()->getPrevious()->getFile(),
  147.                             'line' => $event->getThrowable()->getPrevious()->getLine(),
  148.                             'stack' => $event->getThrowable()->getPrevious()->getTraceAsString(),
  149.                         ] : null,
  150.                     ],
  151.                     'request' => [
  152.                         'uri' => $event->getRequest()->getUri(),
  153.                         'scheme' => $event->getRequest()->getScheme(),
  154.                         'host' => $event->getRequest()->getHost(),
  155.                         'path_info' => $event->getRequest()->getPathInfo(),
  156.                         'query_string' => $event->getRequest()->getQueryString(),
  157.                         'method' => $event->getRequest()->getMethod(),
  158.                         'headers' => $event->getRequest()->headers->all(),
  159.                         'request' => $event->getRequest()->request->all(),
  160.                         'query' => $event->getRequest()->query->all(),
  161.                         'body' => $event->getRequest()->getContent(),
  162.                     ],
  163.                 ],
  164.                 $status,
  165.             ));
  166.         }
  167.     }
  168.     /**
  169.      * @param ViewEvent $event
  170.      * @return void
  171.      */
  172.     public function onKernelView(ViewEvent $event): void
  173.     {
  174.         // get controller
  175.         $route $event->getRequest()->attributes->get('_route');
  176.         // make sure it is us
  177.         if ($this->isApiRoute($route)) {
  178.             // set timer
  179.             $event->getRequest()->attributes->set('_api_stop'$stop hrtime(true));
  180.             // calc span
  181.             $event->getRequest()->attributes->set(
  182.                 '_api_span',
  183.                 $span = ($stop $event->getRequest()->attributes->get('_api_start'))
  184.             );
  185.             // set in the meta
  186.             $view $event->getControllerResult();
  187.             if ($view instanceof JsonView) {
  188.                 // attach metadata for api timing
  189.                 $view->setData(array_merge(
  190.                     $view->getData(),
  191.                     (isset($view->getData()['meta'])) ? [
  192.                         'meta' => array_merge(
  193.                             $view->getData()['meta'],
  194.                             [
  195.                                 'tenant' => $this->cm->getGlobalContext()->getTenant()
  196.                                     ? $this->cm->getGlobalContext()->getTenant()->getSlug()
  197.                                     : null,
  198.                                 'runtime' => ceil($span/1e+6),//nanoseconds to milliseconds
  199.                             ]
  200.                         ),
  201.                     ] : []
  202.                 ));
  203.             }
  204.         }
  205.     }
  206. }