EventPusher::onMessageServerEvent()   B
last analyzed

Complexity

Conditions 5
Paths 9

Size

Total Lines 40
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 40
rs 8.439
cc 5
eloc 20
nc 9
nop 1
1
<?php
2
3
namespace BZIon\Socket;
4
5
use BZIon\Event\EventSubscriber;
6
use Player;
7
use Ratchet\ConnectionInterface;
8
use Ratchet\MessageComponentInterface;
9
use React\EventLoop\LoopInterface;
10
use Symfony\Component\Console\Output\OutputInterface;
11
12
class EventPusher implements MessageComponentInterface
13
{
14
    /**
15
     * The connected clients
16
     * @var \SplObjectStorage
17
     */
18
    protected $clients;
19
20
    /**
21
     * The event subscriber
22
     * @var EventSubscriber
23
     */
24
    protected $subscriber;
25
26
    /**
27
     * The event loop
28
     * @var LoopInterface
29
     */
30
    protected $loop;
31
32
    /**
33
     * The console output
34
     * @var OutputInterface|null
35
     */
36
    protected $output;
37
38
    /**
39
     * Max pong time and interval between pings in seconds
40
     * @var int
41
     */
42
    const KEEP_ALIVE = 300;
43
44
    /**
45
     * Create a new event pusher handler
46
     */
47
    public function __construct(LoopInterface $loop, OutputInterface $output = null)
48
    {
49
        $this->loop = $loop;
50
        $this->output = $output;
51
52
        $this->clients = new \SplObjectStorage();
53
        $this->subscriber = \Service::getContainer()->get('kernel.subscriber.bzion_subscriber');
54
55
        // Ping timer
56
        $loop->addPeriodicTimer(self::KEEP_ALIVE, array($this, 'ping'));
57
    }
58
59
    /**
60
     * Open the connection
61
     * @param ConnectionInterface $conn
62
     */
63
    public function onOpen(ConnectionInterface $conn)
64
    {
65
        // Find which player opened the connection
66
        $conn->Player = Player::get($conn->Session->get('playerId'));
0 ignored issues
show
Bug introduced by
Accessing Player 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...
Bug introduced by
Accessing Session 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...
67
68
        $conn->pong = true;
0 ignored issues
show
Bug introduced by
Accessing pong 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...
69
70
        // Store the new connection to send messages to later
71
        $this->clients->attach($conn);
72
73
        $this->log(
74
            sprintf(
75
                "<fg=cyan>Client #{$conn->resourceId} connected from {$conn->remoteAddress}\t ({$conn->Player->getUsername()})</>",
0 ignored issues
show
Bug introduced by
Accessing resourceId 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...
Bug introduced by
Accessing remoteAddress 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...
Bug introduced by
Accessing Player 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...
76
                $conn->resourceId,
0 ignored issues
show
Bug introduced by
Accessing resourceId 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...
77
                $conn->remoteAddress,
0 ignored issues
show
Bug introduced by
Accessing remoteAddress 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...
78
                $conn->Player->getUsername()
0 ignored issues
show
Bug introduced by
Accessing Player 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...
79
            ), OutputInterface::VERBOSITY_VERBOSE
80
        );
81
    }
82
83
    /**
84
     * Send a message as a client
85
     * @param ConnectionInterface $from
86
     * @param mixed               $msg
87
     */
88
    public function onMessage(ConnectionInterface $from, $msg)
89
    {
90
        $this->log("Received message from #{$from->resourceId}");
0 ignored issues
show
Bug introduced by
Accessing resourceId 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
        // Record the reception of the message to prevent a ping timeout
93
        $from->pong = true;
0 ignored issues
show
Bug introduced by
Accessing pong 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...
94
    }
95
96
    /**
97
     * Close a connection
98
     * @param ConnectionInterface $conn
99
     */
100
    public function onClose(ConnectionInterface $conn)
101
    {
102
        // The connection is closed, remove it, as we can no longer send it messages
103
        $this->clients->detach($conn);
104
105
        $this->log(
106
            sprintf(
107
                "<fg=yellow>Client #{$conn->resourceId} disconnected from {$conn->remoteAddress}\t ({$conn->Player->getUsername()})</>",
0 ignored issues
show
Bug introduced by
Accessing resourceId 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...
Bug introduced by
Accessing remoteAddress 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...
Bug introduced by
Accessing Player 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...
108
                $conn->resourceId,
0 ignored issues
show
Bug introduced by
Accessing resourceId 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...
109
                $conn->remoteAddress,
0 ignored issues
show
Bug introduced by
Accessing remoteAddress 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...
110
                $conn->Player->getUsername()
0 ignored issues
show
Bug introduced by
Accessing Player 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...
111
            ), OutputInterface::VERBOSITY_VERBOSE
112
        );
113
    }
114
115
    /**
116
     * Action to call on an error
117
     */
118
    public function onError(ConnectionInterface $conn, \Exception $e)
119
    {
120
        echo "An error has occurred: {$e->getMessage()}\n";
121
122
        $conn->close();
123
    }
124
125
    /**
126
     * Pushes or emails a new private message to the user
127
     *
128
     * @param array $event The event data we received from the web server
129
     */
130
    private function onMessageServerEvent($event)
131
    {
132
        // A list of players who received a message so that we can e-mail the
133
        // ones who didn't
134
        $received = array();
135
136
        $conversation = \Conversation::get($event->data->conversation);
137
138
        $conversationMembers = $conversation->getPlayerIds();
139
140
        foreach ($this->clients as $client) {
141
            $player = $client->Player;
142
143
            if (!in_array($player->getId(), $conversationMembers)) {
144
                // Don't notify that player, he doesn't belong in the conversation
145
                continue;
146
            }
147
148
            $event->notification_count = $player->countUnreadNotifications();
149
            $event->message_count      = $player->countUnreadMessages();
150
151
            $this->send($client, $event);
152
            $received[] = $player->getId();
153
        }
154
155
        // Send e-mails
156
        foreach ($event->data->recipients as $recipient) {
157
            // Only send an email to users who aren't currently logged in
158
            if (!in_array($recipient, $received)) {
159
                $this->log("<fg=green>E-mailing player {$recipient->getId()} ({$recipient->getUsername()})</>");
160
161
                $this->subscriber->sendEmails(
162
                    'New message received',
163
                    array($recipient),
164
                    'message',
165
                    array('message' => \Message::get($event->data->message))
166
                );
167
            }
168
        }
169
    }
170
171
    /**
172
     * Pushes or emails a new notification to the user
173
     *
174
     * @param array $event The event data we received from the web server
175
     */
176
    private function onNotificationServerEvent($event)
177
    {
178
        $notification = \Notification::get($event->data->notification);
179
180
        // Whether we've notified that player in real time - if he isn't online
181
        // at the moment, we'll send an e-mail to him
182
        $active = false;
183
184
        foreach ($this->clients as $client) {
185
            if ($client->Player->getId() == $event->data->receiver) {
186
                $this->send($client, $event);
187
                $active = true;
188
            }
189
        }
190
191
        if (!$active) {
192
            $player = $notification->getReceiver();
193
            $this->log("<fg=green>E-mailing player {$player->getId()} ({$player->getUsername()})</>");
194
195
            $this->subscriber->emailNotification($notification);
196
        }
197
    }
198
199
    /**
200
     * Send some data to the client
201
     *
202
     * @param ConnectionInterface $client The client that will receive the data
203
     * @param array               $data   The data to send
204
     */
205
    protected function send(ConnectionInterface $client, $data)
206
    {
207
        $this->log("<fg=green>Notifying #{$client->resourceId} ({$client->Player->getUsername()})</>");
0 ignored issues
show
Bug introduced by
Accessing resourceId 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...
Bug introduced by
Accessing Player 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...
208
209
        $data->notification_count = $client->Player->countUnreadNotifications();
0 ignored issues
show
Bug introduced by
Accessing Player 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...
210
        $data->message_count      = $client->Player->countUnreadMessages();
0 ignored issues
show
Bug introduced by
Accessing Player 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...
211
212
        $client->send(json_encode(array('event' => $data)));
213
    }
214
215
    /**
216
     * Action to call when the server notifies us about something
217
     * @param string $event JSON'ified string we'll receive from the webserver
218
     */
219
    public function onServerEvent($event)
220
    {
221
        $event = $event->event;
222
223
        switch ($event->type) {
224
            case 'message':
225
                $this->log("New message received", OutputInterface::VERBOSITY_VERY_VERBOSE);
226
                $this->onMessageServerEvent($event);
227
                break;
228
            case 'notification':
229
                $this->log("New notification received", OutputInterface::VERBOSITY_VERY_VERBOSE);
230
                $this->onNotificationServerEvent($event);
231
                break;
232
            default:
233
                $this->log("Generic message received", OutputInterface::VERBOSITY_VERY_VERBOSE);
234
                foreach ($this->clients as $client) {
235
                    $this->send($client, $event);
236
                }
237
        }
238
    }
239
240
    /**
241
     * Log a debugging message to the console
242
     *
243
     * @param string $message The message to log
244
     * @param int    $level   The output verbosity level of the message
245
     */
246
    private function log($message, $level = OutputInterface::VERBOSITY_DEBUG)
247
    {
248
        if (!$this->output) {
249
            return;
250
        }
251
252
        if ($level <= $this->output->getVerbosity()) {
253
            $this->output->writeln($message);
254
        }
255
    }
256
257
    /**
258
     * Send a ping message to all clients and kick those who didn't respond
259
     */
260
    public function ping()
261
    {
262
        $this->log("Sending pings");
263
264
        foreach ($this->clients as $client) {
265
            if (!$client->pong) {
266
                $this->log("Dropping #{$client->resourceId}");
267
268
                $client->close();
269
                continue;
270
            }
271
272
            $this->log("Pinging #{$client->resourceId}");
273
            $client->send('ping');
274
            $client->pong = false;
275
        }
276
    }
277
}
278