Completed
Push — master ( 00ae50...77d591 )
by Narcotic
27:26 queued 18:13
created

RestEventSubscriber::getEventObject()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 0
cts 12
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 12
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * handle graviton.rest.event events
4
 */
5
namespace Graviton\RestBundle\Subscriber;
6
7
use Graviton\RestBundle\Event\RestEvent;
8
use Symfony\Component\HttpFoundation\Response;
9
use Symfony\Component\EventDispatcher\Event;
10
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
11
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
13
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
14
use Symfony\Component\DependencyInjection\ContainerInterface;
15
16
/**
17
 * Subscriber for kernel.request and kernel.response events.
18
 * This class dispatches the graviton.rest.event event
19
 *
20
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
21
 * @license  https://opensource.org/licenses/MIT MIT License
22
 * @link     http://swisscom.ch
23
 */
24
final class RestEventSubscriber implements EventSubscriberInterface
25
{
26
    /**
27
     * @var Response
28
     */
29
    private $response;
30
31
    /**
32
     * @var RestEvent
33
     */
34
    private $restEvent;
35
36
    /**
37
     * @var ContainerInterface
38
     */
39
    private $container;
40
41
    /**
42
     * @param Response           $response  Response
43
     * @param RestEvent          $restEvent Rest event
44
     * @param ContainerInterface $container Container
45
     */
46
    public function __construct(Response $response, RestEvent $restEvent, ContainerInterface $container)
47
    {
48
        $this->response = $response;
49
        $this->restEvent = $restEvent;
50
        $this->container = $container;
51
    }
52
53
    /**
54
     * Returns the subscribed events
55
     *
56
     * @return array $ret Events to subscribe
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array<string|integer>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
57
     */
58 2
    public static function getSubscribedEvents()
59
    {
60
        /**
61
         * There is a priority flag to manage execution order.
62
         * If we need more controll over this, we can use this flag or add
63
         * pre/post Methods like described here:
64
         * http://symfony.com/doc/current/components/event_dispatcher/introduction.html (StoreSubscriber)
65
         */
66
67
        return array(
68 2
            'kernel.request'  => array("onKernelRequest", 0),
69 1
            'kernel.response' => array("onKernelResponse", 0)
70 1
        );
71
    }
72
73
    /**
74
     * Handler for kernel.request events
75
     *
76
     * @param GetResponseEvent         $event      Event
77
     * @param string                   $name       Event name
78
     * @param EventDispatcherInterface $dispatcher Event dispatcher
79
     *
80
     * @return void
81
     */
82
    public function onKernelRequest(GetResponseEvent $event, $name, EventDispatcherInterface $dispatcher)
83
    {
84
        $restEvent = $this->getEventObject($event);
85
        if ($restEvent instanceof RestEvent) {
86
            $dispatcher->dispatch("graviton.rest.request", $restEvent);
87
        }
88
    }
89
90
    /**
91
     * Get a RestEvent object -> put this in a factory
92
     *
93
     * @param Event $event Original event (kernel.request / kernel.response)
94
     *
95
     * @return RestEvent $restEvent
96
     */
97
    private function getEventObject(Event $event)
98
    {
99
        // get the service name
100
        list ($serviceName) = explode(":", $event->getRequest()->get('_controller'));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getRequest() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: Graviton\RestBundle\Event\RestEvent, Symfony\Component\HttpKe...ontrollerArgumentsEvent, Symfony\Component\HttpKe...t\FilterControllerEvent, Symfony\Component\HttpKe...ent\FilterResponseEvent, Symfony\Component\HttpKe...vent\FinishRequestEvent, Symfony\Component\HttpKe...\Event\GetResponseEvent, Symfony\Component\HttpKe...orControllerResultEvent, Symfony\Component\HttpKe...sponseForExceptionEvent, Symfony\Component\HttpKernel\Event\KernelEvent, Symfony\Component\HttpKe...Event\PostResponseEvent, Symfony\Component\Securi...t\InteractiveLoginEvent, Symfony\Component\Securi...p\Event\SwitchUserEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
101
102
        // get the controller which handles this request
103
        if ($this->container->has($serviceName)) {
104
            $controller = $this->container->get($serviceName);
105
            $restEvent = $this->restEvent;
106
            $restEvent->setRequest($event->getRequest());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getRequest() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: Graviton\RestBundle\Event\RestEvent, Symfony\Component\HttpKe...ontrollerArgumentsEvent, Symfony\Component\HttpKe...t\FilterControllerEvent, Symfony\Component\HttpKe...ent\FilterResponseEvent, Symfony\Component\HttpKe...vent\FinishRequestEvent, Symfony\Component\HttpKe...\Event\GetResponseEvent, Symfony\Component\HttpKe...orControllerResultEvent, Symfony\Component\HttpKe...sponseForExceptionEvent, Symfony\Component\HttpKernel\Event\KernelEvent, Symfony\Component\HttpKe...Event\PostResponseEvent, Symfony\Component\Securi...t\InteractiveLoginEvent, Symfony\Component\Securi...p\Event\SwitchUserEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
107
            $restEvent->setResponse($this->response);
108
            $restEvent->setController($controller);
109
110
            $returnEvent = $restEvent;
111
        } else {
112
            $returnEvent = $event;
113
        }
114
115
        return $returnEvent;
116
    }
117
118
    /**
119
     * Handler for kernel.response events
120
     *
121
     * @param FilterResponseEvent      $event      Event
122
     * @param string                   $name       Event name
123
     * @param EventDispatcherInterface $dispatcher Event dispatcher
124
     *
125
     * @return void
126
     */
127
    public function onKernelResponse(FilterResponseEvent $event, $name, EventDispatcherInterface $dispatcher)
128
    {
129
        $dispatcher->dispatch("graviton.rest.response", $event);
130
    }
131
}
132