src/EventListener/ActivityAnnotationListener.php line 29

Open in your IDE?
  1. <?php declare(strict_types=1);
  2.      
  3. namespace App\EventListener;
  4.  
  5. use App\Annotation\Activity as ActivityAnnotation;
  6. use App\Component\HttpFoundation\Payload;
  7. use Doctrine\Common\Annotations\Reader;
  8. use ReflectionClass;
  9. use ReflectionException;
  10. use RuntimeException;
  11. use App\Tartarus;
  12. use Symfony\Component\HttpKernel\Event\{
  13.     ControllerEvent
  14.     ResponseEvent
  15.     ControllerArgumentsEvent
  16. };
  17.  
  18. class ActivityAnnotationListener {
  19.     
  20.     private $annotationReader;
  21.     protected $annotation null;
  22.     protected $payload null;
  23.     protected $auth null;
  24.  
  25.     public function __construct(Reader $annotationReader) {
  26.         $this->annotationReader $annotationReader;
  27.     }
  28.  
  29.     public function onKernelController(ControllerEvent $event): void {
  30.         if (!$event->isMainRequest())
  31.             return;
  32.         $controllers $event->getController();
  33.         if (!is_array($controllers))
  34.             return;
  35.         $this->handleAnnotation($controllers);
  36.     }
  37.     
  38.     public function onKernelControllerArguments(ControllerArgumentsEvent $event): void {
  39.         if (!$event->isMainRequest())
  40.             return;
  41.         foreach ($event->getArguments() as $argument) {
  42.             if ($argument instanceof Payload)
  43.                 $this->payload $argument;
  44.             elseif ($argument instanceof Tartarus\AuthInterface)
  45.                 $this->auth $argument;
  46.         }
  47.     }
  48.     
  49.     /**
  50.      * @todo    Improve/fix for numeric indexes and wildcard match
  51.      */
  52.     protected function unsetKey(\stdClass &$victim, array $path) : void {
  53.         if (count($path) == 1) {
  54.             $key current($path);
  55.             if (isset($victim->$key))
  56.                 unset($victim->$key);
  57.         } else {
  58.             $key array_shift($path);
  59.             if (isset($victim->$key))
  60.                 $this->unsetKey($victim->$key$path);
  61.         }
  62.     }
  63.     
  64.     public function onKernelResponse(ResponseEvent $event) : void {
  65.         if (!$event->isMainRequest())
  66.             return;
  67.         $response $event->getResponse();
  68.         /* Decide whether to log the activity depending on response code and annotation params */
  69.         if ($this->annotation) {
  70.             if (!$this->payload)
  71.                 throw new RuntimeException('Activity requires Payload argument');
  72.             if ($this->annotation->status) {
  73.                 if (!in_array($response->getStatusCode(), $this->annotation->status))
  74.                     return;
  75.             }
  76.             /* Exclude data from logged payload, if applicable */
  77.             $data $this->payload->getData();
  78.             if ($this->annotation->exclude && $data) {
  79.                 foreach ($this->annotation->exclude as $path)
  80.                     $this->unsetKey($data$path);
  81.             }
  82.             /* Go ahead with logging */
  83.             $payload json_encode($data);
  84.             $clientIp $event->getRequest()->getClientIp() ?? '';
  85.             $userAgent $event->getRequest()->headers->get('User-Agent') ?? '';
  86.             $action $this->payload->getRouteName();
  87.             $created time();
  88.             try {
  89.                 $userId = ($this->auth) ? $this->auth->getUserId(['allow_locked' => true]) : 0;
  90.             } catch (Tartarus\StateError $e) {
  91.                 $userId 0;
  92.             }
  93.             /**
  94.              * TODO: Print CSV:
  95.              * user_id | created uint32 | action (routeName) | user_agent | ip_address | payload 
  96.              */
  97.         }
  98.     }
  99.      
  100.     private function handleAnnotation(iterable $controllers): void {
  101.         list($controller$method) = $controllers;
  102.         try {
  103.             $controller = new ReflectionClass($controller);
  104.         } catch (ReflectionException $e) {
  105.             throw new RuntimeException('Failed to read annotation');
  106.         }
  107.         $this->handleMethodAnnotation($controller$method);
  108.     }
  109.  
  110.     private function handleMethodAnnotation(ReflectionClass $controllerstring $method): void {
  111.         $method $controller->getMethod($method);
  112.         /* Get activity annotation */
  113.         $annotation $this->annotationReader->getMethodAnnotation($methodActivityAnnotation::class);
  114.         if ($annotation instanceof ActivityAnnotation) {
  115.             $this->annotation $annotation;
  116.         } 
  117.     }
  118. }