Completed
Push — master ( 6435a2...ebbf46 )
by Julien
01:57
created

Application::loadTopic()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.6845
c 0
b 0
f 0
cc 4
eloc 12
nc 5
nop 1
1
<?php
2
3
namespace Eole\Sandstone\Websocket;
4
5
use Psr\Log\LoggerAwareTrait;
6
use JMS\Serializer\EventDispatcher\EventSubscriberInterface as WrongEventSubscriberInterface;
7
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
8
use Symfony\Component\Security\Core\User\UserInterface;
9
use Ratchet\ConnectionInterface;
10
use Ratchet\Wamp\WampServerInterface;
11
use Eole\Sandstone\Logger\EchoLogger;
12
use Eole\Sandstone\OAuth2\Security\Authentication\Token\OAuth2Token;
13
use Eole\Sandstone\Application as SandstoneApplication;
14
15
final class Application implements WampServerInterface
16
{
17
    use LoggerAwareTrait;
18
19
    /**
20
     * @var SandstoneApplication
21
     */
22
    private $sandstoneApplication;
23
24
    /**
25
     * @var Topic[]
26
     */
27
    private $topics;
28
29
    /**
30
     * @param SandstoneApplication $sandstoneApplication
31
     */
32
    public function __construct(SandstoneApplication $sandstoneApplication)
33
    {
34
        $this->sandstoneApplication = $sandstoneApplication;
35
        $this->topics = array();
36
        $this->logger = new EchoLogger();
37
    }
38
39
    /**
40
     * @param ConnectionInterface $conn
41
     *
42
     * @return UserInterface|null
43
     *
44
     * @throws \Exception
45
     */
46
    private function authenticateUser(ConnectionInterface $conn)
47
    {
48
        $accessToken = $conn->WebSocket->request->getQuery()->get('access_token');
0 ignored issues
show
Bug introduced by
Accessing WebSocket on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
49
50
        if (null === $accessToken) {
51
            return null;
52
        }
53
54
        $authenticationManager = $this->sandstoneApplication['security.authentication_manager'];
55
56
        $authenticatedToken = $authenticationManager->authenticate(new OAuth2Token($accessToken));
57
        $user = $authenticatedToken->getUser();
58
        $isUser = $user instanceof UserInterface;
59
60
        if (!$isUser) {
61
            throw new \Exception('User not found.');
62
        }
63
64
        return $user;
65
    }
66
67
    /**
68
     * {@InheritDoc}
69
     */
70
    public function onOpen(ConnectionInterface $conn)
71
    {
72
        $this->logger->info('Connection event', ['event' => 'open']);
73
        $this->logger->info('Authentication...');
74
75
        try {
76
            $user = $this->authenticateUser($conn);
77
            if (null === $user) {
78
                $this->logger->info('Anonymous connection');
79
            } else {
80
                $this->logger->info('User logged.', ['username' => $user->getUsername()]);
81
            }
82
        } catch (\Exception $e) {
83
            $this->logger->notice('Failed authentication', ['error_message' => $e->getMessage()]);
84
            $conn->send(json_encode('Could not authenticate client, closing connection.'));
85
            $conn->close();
86
87
            return;
88
        }
89
90
        $conn->user = $user;
0 ignored issues
show
Bug introduced by
Accessing user on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
91
    }
92
93
    /**
94
     * {@InheritDoc}
95
     */
96
    private function getTopic($topicPath)
97
    {
98
        if (!isset($this->topics[$topicPath])) {
99
            $this->topics[$topicPath] = $this->loadTopic($topicPath);
100
        }
101
102
        return $this->topics[$topicPath];
103
    }
104
105
    /**
106
     * @param string $topicPath
107
     *
108
     * @return Topic
109
     */
110
    private function loadTopic($topicPath)
111
    {
112
        $topic = $this->sandstoneApplication['sandstone.websocket.router']->loadTopic($topicPath);
113
114
        if (!$this->sandstoneApplication->offsetExists('serializer')) {
115
            throw new \RuntimeException('A serializer must be registered');
116
        }
117
118
        $topic->setNormalizer($this->sandstoneApplication['serializer']);
119
120
        if ($topic instanceof EventSubscriberInterface) {
121
            $this->sandstoneApplication['dispatcher']->addSubscriber($topic);
122
        }
123
124
        // debug purpose only. Sometimes I use the wrong namespace (JMS one), and it's hard to debug.
125
        if ($topic instanceof WrongEventSubscriberInterface) {
126
            throw new \LogicException(
127
                get_class($topic).' seems to implements the wrong EventSubscriberInterface. '.
128
                'Use the Symfony one, not the JMS one.'
129
            );
130
        }
131
132
        return $topic;
133
    }
134
135
    /**
136
     * {@InheritDoc}
137
     */
138
    public function onSubscribe(ConnectionInterface $conn, $topic)
139
    {
140
        $this->logger->info('Topic event', ['event' => 'subscribe', 'topic' => $topic]);
141
142
        $this->getTopic($topic)->onSubscribe($conn, $topic);
0 ignored issues
show
Compatibility introduced by
$conn of type object<Ratchet\ConnectionInterface> is not a sub-type of object<Ratchet\Wamp\WampConnection>. It seems like you assume a concrete implementation of the interface Ratchet\ConnectionInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like $topic defined by parameter $topic on line 138 can also be of type object<Ratchet\Wamp\Topic>; however, Eole\Sandstone\Websocket\Topic::onSubscribe() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
143
    }
144
145
    /**
146
     * {@InheritDoc}
147
     */
148
    public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible)
149
    {
150
        $this->logger->info('Topic event', ['event' => 'publish', 'topic' => $topic]);
151
152
        $this->getTopic($topic)->onPublish($conn, $topic, $event);
0 ignored issues
show
Compatibility introduced by
$conn of type object<Ratchet\ConnectionInterface> is not a sub-type of object<Ratchet\Wamp\WampConnection>. It seems like you assume a concrete implementation of the interface Ratchet\ConnectionInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like $topic defined by parameter $topic on line 148 can also be of type object<Ratchet\Wamp\Topic>; however, Eole\Sandstone\Websocket\Topic::onPublish() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
153
    }
154
155
    /**
156
     * {@InheritDoc}
157
     */
158
    public function onUnSubscribe(ConnectionInterface $conn, $topic)
159
    {
160
        $this->logger->info('Topic event', ['event' => 'unsubscribe', 'topic' => $topic]);
161
162
        $this->getTopic($topic)->onUnSubscribe($conn, $topic);
0 ignored issues
show
Compatibility introduced by
$conn of type object<Ratchet\ConnectionInterface> is not a sub-type of object<Ratchet\Wamp\WampConnection>. It seems like you assume a concrete implementation of the interface Ratchet\ConnectionInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like $topic defined by parameter $topic on line 158 can also be of type object<Ratchet\Wamp\Topic>; however, Eole\Sandstone\Websocket\Topic::onUnSubscribe() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
163
    }
164
165
    /**
166
     * {@InheritDoc}
167
     */
168
    public function onClose(ConnectionInterface $conn)
169
    {
170
        $this->logger->info('Connection event', ['event' => 'close']);
171
172
        foreach ($this->topics as $topic) {
173
            if ($topic->has($conn)) {
174
                $topic->onUnSubscribe($conn, $topic);
0 ignored issues
show
Compatibility introduced by
$conn of type object<Ratchet\ConnectionInterface> is not a sub-type of object<Ratchet\Wamp\WampConnection>. It seems like you assume a concrete implementation of the interface Ratchet\ConnectionInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
175
            }
176
        }
177
    }
178
179
    /**
180
     * {@InheritDoc}
181
     */
182
    public function onCall(ConnectionInterface $conn, $id, $topic, array $params)
183
    {
184
        $this->logger->info('Topic event', ['event' => 'call', 'topic' => $topic]);
185
    }
186
187
    /**
188
     * {@InheritDoc}
189
     */
190
    public function onError(ConnectionInterface $conn, \Exception $e)
191
    {
192
        $this->logger->info('Connection event', ['event' => 'error', 'message' => $e->getMessage()]);
193
    }
194
}
195