GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Server::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3.1172

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 23
ccs 13
cts 17
cp 0.7647
rs 9.0856
cc 3
eloc 18
nc 3
nop 3
crap 3.1172
1
<?php
2
3
namespace Navarr\Socket;
4
5
class Server
6
{
7
    /**
8
     * A Multi-dimensional array of callable arrays mapped by hook name.
9
     *
10
     * @var array
11
     */
12
    protected $hooks = [];
13
14
    /**
15
     * IP Address.
16
     *
17
     * @var string
18
     */
19
    protected $address;
20
21
    /**
22
     * Port Number.
23
     *
24
     * @var int
25
     */
26
    protected $port;
27
28
    /**
29
     * Seconds to wait on a socket before timing out.
30
     *
31
     * @var int|null
32
     */
33
    protected $timeout = null;
34
35
    /**
36
     * Domain.
37
     *
38
     * @see http://php.net/manual/en/function.socket-create.php
39
     *
40
     * @var int One of AF_INET, AF_INET6, AF_UNIX
41
     */
42
    protected $domain;
43
44
    /**
45
     * The Master Socket.
46
     *
47
     * @var Socket
48
     */
49
    protected $masterSocket;
50
51
    /**
52
     * Maximum Amount of Clients Allowed to Connect.
53
     *
54
     * @var int
55
     */
56
    protected $maxClients = PHP_INT_MAX;
57
58
    /**
59
     * Maximum amount of characters to read in from a socket at once
60
     * This integer is passed directly to socket_read.
61
     *
62
     * @var int
63
     */
64
    protected $maxRead = 1024;
65
66
    /**
67
     * Connected Clients.
68
     *
69
     * @var Socket[]
70
     */
71
    protected $clients = [];
72
73
    /**
74
     * Type of Read to use.  One of PHP_BINARY_READ, PHP_NORMAL_READ.
75
     *
76
     * @var int
77
     */
78
    protected $readType = PHP_BINARY_READ;
79
80
    /**
81
     * Constant String for Generic Connection Hook.
82
     */
83
    const HOOK_CONNECT = '__NAVARR_SOCKET_SERVER_CONNECT__';
84
85
    /**
86
     * Constant String for Generic Input Hook.
87
     */
88
    const HOOK_INPUT = '__NAVARR_SOCKET_SERVER_INPUT__';
89
90
    /**
91
     * Constant String for Generic Disconnect Hook.
92
     */
93
    const HOOK_DISCONNECT = '__NAVARR_SOCKET_SERVER_DISCONNECT__';
94
95
    /**
96
     * Constant String for Server Timeout.
97
     */
98
    const HOOK_TIMEOUT = '__NAVARR_SOCKET_SERVER_TIMEOUT__';
99
100
    /**
101
     * Return value from a hook callable to tell the server not to run the other hooks.
102
     */
103
    const RETURN_HALT_HOOK = false;
104
105
    /**
106
     * Return value from a hook callable to tell the server to halt operations.
107
     */
108
    const RETURN_HALT_SERVER = '__NAVARR_HALT_SERVER__';
109
110
    /**
111
     * Create an Instance of a Server rearing to go.
112
     *
113
     * @param string $address An IPv4, IPv6, or Unix socket address
114
     * @param int    $port
115
     * @param int    $timeout Seconds to wait on a socket before timing it out
116
     */
117 1
    public function __construct($address, $port = 0, $timeout = null)
118
    {
119 1
        set_time_limit(0);
120 1
        $this->address = $address;
121 1
        $this->port = $port;
122 1
        $this->timeout = $timeout;
123
124
        switch (true) {
125 1
            case filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4):
126 1
                $this->domain = AF_INET;
127 1
                break;
128
            case filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6):
129
                $this->domain = AF_INET6;
130
                break;
131
            default:
132
                $this->domain = AF_UNIX;
133
        }
134
135 1
        $this->masterSocket = Socket::create($this->domain, SOCK_STREAM, 0);
136 1
        $this->masterSocket->bind($this->address, $this->port);
137 1
        $this->masterSocket->getSockName($this->address, $this->port);
138 1
        $this->masterSocket->listen();
139 1
    }
140
141
    public function __destruct()
142
    {
143
        $this->masterSocket->close();
144
    }
145
146
    /**
147
     * Run the Server for as long as loopOnce returns true.
148
     *
149
     * @see self.loopOnce
150
     *
151
     * @throws \Navarr\Socket\Exception\SocketException
152
     *
153
     * @return void
154
     */
155
    public function run()
156
    {
157
        do {
158
            $test = $this->loopOnce();
159
        } while ($test);
160
161
        $this->shutDownEverything();
162
    }
163
164
    /**
165
     * This is the main server loop.  This code is responsible for adding connections and triggering hooks.
166
     *
167
     * @throws \Navarr\Socket\Exception\SocketException
168
     *
169
     * @return bool Whether or not to shutdown the server
170
     */
171
    protected function loopOnce()
172
    {
173
        // Get all the Sockets we should be reading from
174
        $read = array_merge([$this->masterSocket], $this->clients);
175
176
        // Set up a block call to socket_select
177
        $write = null;
178
        $except = null;
179
        $ret = Socket::select($read, $write, $except, $this->timeout);
180
        if (!is_null($this->timeout) && $ret == 0) {
181
            if ($this->triggerHooks(self::HOOK_TIMEOUT, $this->masterSocket) === false) {
182
                // This only happens when a hook tells the server to shut itself down.
183
                return false;
184
            }
185
        }
186
187
        // If there is a new connection, add it
188
        if (in_array($this->masterSocket, $read)) {
189
            unset($read[array_search($this->masterSocket, $read)]);
190
            $socket = $this->masterSocket->accept();
191
            $this->clients[] = $socket;
192
193
            if ($this->triggerHooks(self::HOOK_CONNECT, $socket) === false) {
194
                // This only happens when a hook tells the server to shut itself down.
195
                return false;
196
            }
197
            unset($socket);
198
        }
199
200
        // Check for input from each client
201
        foreach ($read as $client) {
202
            $input = $this->read($client);
203
204
            if ($input === '') {
205
                if ($this->disconnect($client) === false) {
206
                    // This only happens when a hook tells the server to shut itself down.
207
                    return false;
208
                }
209
            } else {
210
                if ($this->triggerHooks(self::HOOK_INPUT, $client, $input) === false) {
211
                    // This only happens when a hook tells the server to shut itself down.
212
                    return false;
213
                }
214
            }
215
            unset($input);
216
        }
217
218
        // Unset the variables we were holding on to
219
        unset($read);
220
        unset($write);
221
        unset($except);
222
223
        // Tells self::run to Continue the Loop
224
        return true;
225
    }
226
227
    /**
228
     * Overrideable Read Functionality.
229
     *
230
     * @param Socket $client
231
     *
232
     * @return string
233
     */
234
    protected function read(Socket $client)
235
    {
236
        return $client->read($this->maxRead, $this->readType);
237
    }
238
239
    /**
240
     * Disconnect the supplied Client Socket.
241
     *
242
     * @param Socket $client
243
     * @param string $message Disconnection Message.  Could be used to trigger a disconnect with a status code
244
     *
245
     * @return bool Whether or not to continue running the server (true: continue, false: shutdown)
246
     */
247
    public function disconnect(Socket $client, $message = '')
248
    {
249
        $clientIndex = array_search($client, $this->clients);
250
        $return = $this->triggerHooks(
251
            self::HOOK_DISCONNECT,
252
            $this->clients[$clientIndex],
253
            $message
254
        );
255
256
        $this->clients[$clientIndex]->close();
257
        unset($this->clients[$clientIndex]);
258
        unset($client);
259
260
        if ($return === false) {
261
            return false;
262
        }
263
264
        unset($return);
265
266
        return true;
267
    }
268
269
    /**
270
     * Triggers the hooks for the supplied command.
271
     *
272
     * @param string $command Hook to listen for (e.g. HOOK_CONNECT, HOOK_INPUT, HOOK_DISCONNECT, HOOK_TIMEOUT)
273
     * @param Socket $client
274
     * @param string $input   Message Sent along with the Trigger
275
     *
276
     * @return bool Whether or not to continue running the server (true: continue, false: shutdown)
277
     */
278
    protected function triggerHooks($command, Socket $client, $input = null)
279
    {
280
        if (isset($this->hooks[$command])) {
281
            foreach ($this->hooks[$command] as $callable) {
282
                $continue = call_user_func($callable, $this, $client, $input);
283
284
                if ($continue === self::RETURN_HALT_HOOK) {
285
                    break;
286
                }
287
                if ($continue === self::RETURN_HALT_SERVER) {
288
                    return false;
289
                }
290
                unset($continue);
291
            }
292
        }
293
294
        return true;
295
    }
296
297
    /**
298
     * Attach a Listener to a Hook.
299
     *
300
     * @param string   $command  Hook to listen for
301
     * @param callable $callable A callable with the signature (Server, Socket, string).
302
     *                           Callable should return false if it wishes to stop the server, and true if it wishes to continue.
303
     *
304
     * @return void
305
     */
306 3
    public function addHook($command, $callable)
307
    {
308 3
        if (!isset($this->hooks[$command])) {
309 1
            $this->hooks[$command] = [];
310
        } else {
311 2
            $k = array_search($callable, $this->hooks[$command]);
312 2
            if ($k !== false) {
313 1
                return;
314
            }
315 2
            unset($k);
316
        }
317
318 3
        $this->hooks[$command][] = $callable;
319 3
    }
320
321
    /**
322
     * Remove the provided Callable from the provided Hook.
323
     *
324
     * @param string   $command  Hook to remove callable from
325
     * @param callable $callable The callable to be removed
326
     *
327
     * @return void
328
     */
329
    public function removeHook($command, $callable)
330
    {
331
        if (isset($this->hooks[$command]) &&
332
            array_search($callable, $this->hooks[$command]) !== false
333
        ) {
334
            $hook = array_search($callable, $this->hooks[$command]);
335
            unset($this->hooks[$command][$hook]);
336
            unset($hook);
337
        }
338
    }
339
340
    /**
341
     * Disconnect all the Clients and shut down the server.
342
     *
343
     * @return void
344
     */
345
    private function shutDownEverything()
346
    {
347
        foreach ($this->clients as $client) {
348
            $this->disconnect($client);
349
        }
350
        $this->masterSocket->close();
351
        unset(
352
            $this->hooks,
353
            $this->address,
354
            $this->port,
355
            $this->timeout,
356
            $this->domain,
357
            $this->masterSocket,
358
            $this->maxClients,
359
            $this->maxRead,
360
            $this->clients,
361
            $this->readType
362
        );
363
    }
364
}
365