Server::process_all_connected_clients()   C
last analyzed

Complexity

Conditions 13
Paths 32

Size

Total Lines 56
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 13

Importance

Changes 0
Metric Value
dl 0
loc 56
ccs 30
cts 30
cp 1
rs 6.6843
c 0
b 0
f 0
cc 13
eloc 28
nc 32
nop 0
crap 13

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
    namespace NokitaKaze\TestHTTPServer;
4
5
    /**
6
     * Class Server
7
     * @package NokitaKaze\TestHTTPServer
8
     *
9
     * @doc https://tools.ietf.org/html/rfc7230
10
     * @doc http://httpwg.org/specs/rfc7230.html
11
     */
12
    class Server {
13
        const CHUNK_SIZE = 4096;
14
15
        /**
16
         * @var ServerSettings
17
         */
18
        protected $_settings = null;
19
20
        /**
21
         * @var resource
22
         */
23
        protected $_server_socket = null;
24
25
        /**
26
         * @var resource
27
         */
28
        protected $_stream_context = null;
29
30
        /**
31
         * @var ClientDatum[]
32
         */
33
        protected $_client_connects = [];
34
35
        /**
36
         * Server constructor
37
         *
38
         * @param ServerSettings|object|array $settings
39
         *
40
         * @throws Exception
41
         */
42 613
        function __construct($settings) {
43 613
            $settings = (object) $settings;
44 613
            self::fix_incoming_settings($settings);
45 613
            $this->_settings = $settings;
46 613
            $this->init_ssl_settings();
47 601
        }
48
49 601
        function __destruct() {
50 601
            $this->shutdown();
51 601
        }
52
53 601
        function shutdown() {
54 601
            foreach ($this->_client_connects as &$connect) {
55 268
                $this->close_connection($connect);
56 301
            }
57 601
            $this->_client_connects = [];
58 601
            $this->close_connection_socket($this->_server_socket);
59 601
            $this->_server_socket = null;
60 601
        }
61
62
        /**
63
         * @param resource $connection
64
         */
65 601
        function close_connection_socket($connection) {
66 601
            if (is_resource($connection)) {
67 597
                stream_socket_shutdown($connection, STREAM_SHUT_RDWR);
68 597
                fclose($connection);
69 299
            }
70 601
        }
71
72
        /**
73
         * @param object|ClientDatum $connection
74
         */
75 573
        function close_connection(&$connection) {
76 573
            if (is_null($connection)) {
77 245
                return;
78
            }
79 573
            $this->close_connection_socket($connection->client);
80 573
            $connection->client = null;
81 573
            $connection = null;
82 573
        }
83
84
        /**
85
         * @return array
86
         */
87 613
        static function get_default_settings() {
88
            return [
89 613
                'interface' => '127.0.0.1',
90 613
                'port' => 58080,
91 613
                'server_connect_waiting_time' => 0.02,
92 307
                'is_ssl' => false,
93
                'filterIncomingConnect' => function () { return true; },
94 307
                'server_maximum_chunk' => 300 * 1024,
95 613
                'server_backlog' => min(255, SOMAXCONN),
96
97 613
                'time_wait_until_first_byte' => 60,
98 307
            ];
99
        }
100
101 613
        function init_ssl_settings() {
102 613
            $this->_stream_context = stream_context_create();
103
            /** @url http://php.net/manual/ru/context.socket.php */
104 613
            stream_context_set_option($this->_stream_context, 'socket', 'backlog', $this->_settings->server_backlog);
105 613
            if (isset($this->_settings->ssl_server_certificate_file)) {
106
                // @hint Здесь специально не задаётся _settings->is_ssl
107 412
                $this->stream_set_ssl_option('local_cert', $this->_settings->ssl_server_certificate_file);
108 412
                $this->stream_set_ssl_option('allow_self_signed', true);
109 412
                $this->stream_set_ssl_option('verify_peer', false);
110 412
                if (isset($this->_settings->ssl_server_key_file)) {
111 412
                    $this->stream_set_ssl_option('local_pk', $this->_settings->ssl_server_key_file);
112 206
                }
113 412
                $this->stream_set_ssl_option('passphrase',
114 412
                    isset($this->_settings->ssl_server_key_password) ? $this->_settings->ssl_server_key_password : '');
115
116 412
                if (isset($this->_settings->ssl_client_certificate_file)) {
117 48
                    $this->stream_set_ssl_option('verify_peer', true);
118 48
                    $this->stream_set_ssl_option('capture_peer_cert', true);
119 48
                    $this->stream_set_ssl_option('capture_peer_cert_chain', true);
120 139
                    $this->stream_set_ssl_option('cafile', $this->_settings->ssl_client_certificate_file);
121 24
                }
122 307
            } elseif (isset($this->_settings->ssl_server_key_file)) {
123 4
                throw new Exception('ssl_server_key_file is set, but ssl_server_certificate_file is missing');
124 99
            } elseif (isset($this->_settings->ssl_server_key_password)) {
125 4
                throw new Exception('ssl_server_key_password is set, but ssl_server_certificate_file is missing');
126 97
            } elseif (isset($this->_settings->ssl_client_certificate_file)) {
127 4
                throw new Exception('ssl_client_certificate_file is set, but ssl_server_certificate_file is missing');
128
            }
129 601
        }
130
131
        /**
132
         * Server constructor.
133
         *
134
         * @param ServerSettings|object $settings
135
         */
136 613
        protected static function fix_incoming_settings($settings) {
137 613
            $default_settings = self::get_default_settings();
138 613
            foreach ($default_settings as $key => $value) {
139 613
                if (!isset($settings->{$key})) {
140 613
                    $settings->{$key} = $value;
141 307
                }
142 307
            }
143 613
            foreach (['ListenStart', 'Connect', 'Request', 'Disconnect', 'ListenStop', 'AnyIncomingData', 'HeadIncomingData',
144 307
                      'BodyIncomingData', 'HeadReceived', 'HeadInvalidReceived'] as $event) {
145 613
                if (!isset($settings->{'on'.$event})) {
146 613
                    $settings->{'on'.$event} = null;
147 307
                }
148 307
            }
149 613
        }
150
151
        /**
152
         * @param callable|double $param
153
         * @param callable|null   $tick
154
         */
155 34
        function listen($param, $tick = null) {
156 34
            $this->init_listening();
157 34
            if (is_callable($param)) {
158 34
                $closure = $param;
159 18
            } else {
160
                $closure = function () use ($param) { return (microtime(true) < $param); };
161
            }
162
            /** @noinspection PhpMethodParametersCountMismatchInspection */
163 34
            while ($closure($this)) {
164 34
                $this->listen_tick();
165 34
                if (!is_null($tick)) {
166
                    $tick($this);
167
                }
168 18
            }
169 34
        }
170
171 597
        function init_listening() {
172 597
            if (!is_null($this->_server_socket)) {
173 34
                return;
174
            }
175 597
            $this->_server_socket = stream_socket_server(
176 597
                sprintf('%s://%s:%d',
177 597
                    $this->_settings->is_ssl ? 'ssl' : 'tcp',
178 597
                    is_null($this->_settings->interface) ? '0.0.0.0' : $this->_settings->interface,
179 597
                    $this->_settings->port
180 299
                ),
181 597
                $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
182 597
                $this->_stream_context);
183 597
            if (!isset($this->_server_socket)) {
184
                // @codeCoverageIgnoreStart
185
                throw new Exception('Can not create socket ['.$errstr.']', 100 + $errno);
186
                // @codeCoverageIgnoreEnd
187
            }
188
189 597
            $this->event_raise('ListenStart');
190 597
        }
191
192 597
        function listen_tick() {
193 597
            $write = null;
194 597
            $except = null;
195 597
            $tv_sec = (int) $this->_settings->server_connect_waiting_time;
0 ignored issues
show
Bug introduced by
Accessing server_connect_waiting_time on the interface NokitaKaze\TestHTTPServer\ServerSettings 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...
196 597
            $tv_usec = ($this->_settings->server_connect_waiting_time - $tv_sec) * 1000000;
0 ignored issues
show
Bug introduced by
Accessing server_connect_waiting_time on the interface NokitaKaze\TestHTTPServer\ServerSettings 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...
197 597
            while (true) {
198 597
                $read = [$this->_server_socket];
199 597
                if (stream_select($read, $write, $except, $tv_sec, $tv_usec) === false) {
200
                    // @codeCoverageIgnoreStart
201
                    throw new Exception('Error on stream select');
202
                    // @codeCoverageIgnoreEnd
203
                }
204 597
                if (empty($read)) {
205 597
                    $this->process_all_connected_clients();
206
207 597
                    return;
208
                }
209
210 589
                $client = @stream_socket_accept($this->_server_socket);
211 589
                if ($client === false) {
212 30
                    $this->event_raise('InvalidConnection');
213 30
                    continue;
214
                }
215
216 573
                if (stream_set_blocking($client, 0) === false) {
217
                    // @codeCoverageIgnoreStart
218
                    throw new Exception('Can not set socket as non blocking');
219
                    // @codeCoverageIgnoreEnd
220
                }
221 573
                $connect_time = microtime(true);
222
                /**
223
                 * @var ClientDatum $datum
224
                 */
225 573
                $datum = new \stdClass();
226 573
                $datum->status = 0;
227 573
                $datum->client = $client;
228 573
                $datum->connection_time = $connect_time;
229 573
                $datum->blob_request = '';
230 573
                $datum->request_head_params = [];
231 573
                $datum->server = $this;
232 573
                $datum->accepted_hosts = null;
233 573
                $this->event_raise('Connect', $datum);
234 573
                $this->_client_connects[] = $datum;
235 287
            }
236
        }
237
238
        /**
239
         * @param string $key
240
         * @param mixed  $value
241
         *
242
         * @url http://php.net/manual/ru/context.ssl.php
243
         */
244 426
        function stream_set_ssl_option($key, $value) {
245 426
            stream_context_set_option($this->_stream_context, 'ssl', $key, $value);
246 426
        }
247
248 17
        function set_option($key, $value) {
249 17
            $this->_settings->{$key} = $value;
250 17
        }
251
252 7
        function get_option($key) {
253 7
            return isset($this->_settings->{$key}) ? $this->_settings->{$key} : null;
254
        }
255
256
        /**
257
         * Первый байт не пришёл
258
         *
259
         * @param object|ClientDatum $connection
260
         *
261
         * @return boolean
262
         */
263 573
        protected function check_timeouts_on_connection_first_byte($connection) {
264 573
            return isset($this->_settings->time_wait_until_first_byte) and
265 573
                   !isset($connection->first_byte_received_time) and
266 573
                   ($connection->connection_time < microtime(true) - $this->_settings->time_wait_until_first_byte);
267
        }
268
269
        /**
270
         * Голова не пришла
271
         *
272
         * @param object|ClientDatum $connection
273
         *
274
         * @return boolean
275
         */
276 572
        protected function check_timeouts_on_connection_head_received($connection) {
277 572
            return isset($this->_settings->time_wait_until_head_received) and
278 431
                   !isset($connection->head_received_time) and
279 572
                   ($connection->connection_time < microtime(true) - $this->_settings->time_wait_until_head_received);
280
        }
281
282
        /**
283
         * Запрос не пришёл
284
         *
285
         * @param object|ClientDatum $connection
286
         *
287
         * @return boolean
288
         */
289 572
        protected function check_timeouts_on_connection_request_received($connection) {
290 572
            return isset($this->_settings->time_wait_until_request_received) and
291 431
                   !isset($connection->full_request_received_time) and
292 572
                   ($connection->connection_time < microtime(true) - $this->_settings->time_wait_until_request_received);
293
        }
294
295
        /**
296
         * Тело не пришло (голова пришла)
297
         *
298
         * @param object|ClientDatum $connection
299
         *
300
         * @return boolean
301
         */
302 572
        protected function check_timeouts_on_connection_body_received_without_head($connection) {
303 572
            return isset($this->_settings->time_between_head_and_body_received, $connection->head_received_time) and
304 431
                   !isset($connection->full_request_received_time) and
305 572
                   ($connection->head_received_time < microtime(true) - $this->_settings->time_between_head_and_body_received);
306
        }
307
308
        /**
309
         * Проверяем слишком старые подключения и убиваем их
310
         *
311
         * @param object|ClientDatum $connection
312
         *
313
         * @return bool
314
         */
315 573
        protected function check_timeouts_on_connection(&$connection) {
316 573
            if ($this->check_timeouts_on_connection_first_byte($connection) or
317 572
                $this->check_timeouts_on_connection_head_received($connection) or
318 572
                $this->check_timeouts_on_connection_request_received($connection) or
319 572
                $this->check_timeouts_on_connection_body_received_without_head($connection)
320 287
            ) {
321 25
                $this->close_connection($connection);
322
323 25
                return false;
324
            }
325
326
            // @hint No Slowloris ( https://en.wikipedia.org/wiki/Slowloris_%28computer_security%29 ) test.
327
            // This is not a real server
328
329 572
            return true;
330
        }
331
332
        /**
333
         * Processing all connected clients
334
         */
335 597
        protected function process_all_connected_clients() {
336 597
            if (empty($this->_client_connects)) {
337 369
                return;
338
            }
339
340
            /**
341
             * @var resource[] $read
342
             * @var resource[] $write
343
             * @var resource[] $except
344
             */
345 573
            $read = [];
346 573
            foreach ($this->_client_connects as $connected) {
347 573
                if (is_null($connected)) {
348 220
                    continue;
349
                }
350 573
                if (is_null($connected->client)) {
351 60
                    $connected = null;
352 60
                    continue;
353
                }
354
355 573
                if (!is_resource($connected->client)) {
356
                    // @codeCoverageIgnoreStart
357
                    throw new Exception(sprintf('Connection became non resource: %s', (string) $connected->client));
358
                    // @codeCoverageIgnoreEnd
359
                }
360
361
                // Проверяем слишком старые подключения и убиваем их
362 573
                if (!$this->check_timeouts_on_connection($connected)) {
363 25
                    continue;
364
                }
365
366 572
                $read[] = $connected->client;
367 287
            }
368 573
            if (empty($read)) {
369 305
                $this->_client_connects = [];
370
371 305
                return;
372
            }
373 572
            $write = null;
374 572
            $except = null;
375 572
            if (stream_select($read, $write, $except, 0) === false) {
376
                // @codeCoverageIgnoreStart
377
                throw new Exception('Error on stream select');
378
                // @codeCoverageIgnoreEnd
379
            }
380 572
            unset($connected);
381
382 572
            foreach ($read as &$socket_resource) {
383 556
                foreach ($this->_client_connects as &$connected) {
384 556
                    if (!is_null($connected) and ($connected->client == $socket_resource)) {
385 556
                        $this->process_connected_client($connected);
386 556
                        break;
387
                    }
388 278
                }
389 286
            }
390 572
        }
391
392
        /**
393
         * @param ClientDatum $connect
394
         * @param double      $time
395
         * @param string      $buf
396
         *
397
         * @return boolean
398
         */
399 556
        protected function receive_data_from_connected_client($connect, $time, &$buf) {
400 556
            $buf = @fread($connect->client, self::CHUNK_SIZE);
401 556
            if (empty($buf)) {
402 130
                $this->close_connection($connect);
403
404 130
                return false;
405
            }
406 448
            $connect->last_byte_received_time = $time;
407 448
            if (!isset($connect->first_byte_received_time)) {
408 448
                $connect->first_byte_received_time = $time;
409 224
            }
410 448
            if (strlen($buf) >= self::CHUNK_SIZE) {
411
                do {
412 32
                    $sub_buf = @fread($connect->client, self::CHUNK_SIZE);
413 32
                    $buf .= $sub_buf;
414 32
                    if (isset($this->_settings->server_maximum_chunk) and
415 32
                        (strlen($buf) >= $this->_settings->server_maximum_chunk)
416 16
                    ) {
417
                        // Слишком много пришло за один раз
418 8
                        break;
419
                    }
420 24
                } while (strlen($sub_buf) >= self::CHUNK_SIZE);
421 16
            }
422
423 448
            return true;
424
        }
425
426
        /**
427
         * Processing all connected clients
428
         *
429
         * @param ClientDatum $connect
430
         */
431 556
        protected function process_connected_client(&$connect) {
432 556
            $time = microtime(true);
433 556
            $connect->context_options = stream_context_get_options($connect->client);
434 556
            $connect->context_params = stream_context_get_params($connect->client);
435 556
            if (!$this->receive_data_from_connected_client($connect, $time, $buf)) {
436 130
                $this->close_connection($connect);
437
438 130
                return;
439
            }
440 448
            $this->event_raise('AnyIncomingData', $connect, $buf);
441 448
            if ($connect->status == 0) {
442 448
                $this->event_raise('HeadIncomingData', $connect, $buf);
443 258
            } elseif ($connect->status == 1) {
444 68
                $this->event_raise('BodyIncomingData', $connect, $buf);
445 34
            }
446 448
            $connect->blob_request .= $buf;
447 448
            if (strpos($connect->blob_request, "\r\n\r\n") === false) {
448
                // Head не дошёл
449 8
                return;
450
            }
451
452
            // Проверяем на голову
453
            // Голова только-только дошла
454 440
            if (($connect->status == 0) and !$this->process_connected_client_head($connect, $time)) {
455 56
                return;
456
            }
457
458
            // Проверяем на body
459 384
            if ($connect->status == 1) {
460 96
                $this->process_connected_client_body($connect, $buf, $time);
461 48
            }
462 384
            if (is_null($connect) or ($connect->status != 2)) {
463 92
                return;
464
            }
465
466
            // Проверяем, что Host в списке обрабатываемых
467 360
            if (!$this->check_requested_host_in_accepted_list($connect)) {
468 72
                return;
469
            }
470
471
            // filterIncomingConnect
472 288
            $closure = $this->_settings->filterIncomingConnect;
473 288
            if (!$closure($this, $connect)) {
474 8
                $this->close_connection($connect);
475
476 8
                return;
477
            }
478 280
            unset($closure, $buf);
479
480
            // Request
481 280
            $this->event_raise('Request', $connect);
482
483 280
            if ($connect->status == 3) {
484 277
                $this->close_connection($connect);
485 139
            }
486 280
        }
487
488 360
        function check_requested_host_in_accepted_list($connect) {
489 360
            if (!isset($this->_settings->accepted_hosts)) {
490 220
                return true;
491
            }
492
493 140
            list($host) = explode(':', $connect->request_head_params['Host']);
494 140
            if (!in_array(strtolower($host), $this->_settings->accepted_hosts)) {
495 72
                if ($this->event_raise('HostNotFound', $connect) === false) {
496 12
                    $this->close_connection($connect);
497
498 12
                    return false;
499
                }
500
501 60
                $this->answer($connect, 404, 'Not Found', 'Host not found');
502 60
                $this->close_connection($connect);
503
504 60
                return false;
505
            }
506
507 68
            return true;
508
        }
509
510
        /**
511
         * If returns "false" connection closed
512
         *
513
         * @param ClientDatum $connect
514
         * @param double      $time
515
         *
516
         * @return boolean
517
         */
518 440
        protected function process_connected_client_head(&$connect, $time) {
519 440
            $connect->head_received_time = $time;
520 440
            list($connect->blob_head) = explode("\r\n\r\n", $connect->blob_request, 2);
521 440
            $a = explode("\r\n", $connect->blob_head, 2);
522 440
            list($first_line, $other_lines) = (count($a) == 2) ? $a : [$a[0], ''];
523 440
            if (!preg_match('_^([A-Z]+)\\s+(.+)\\s+HTTP/([0-9.]+)$_', $first_line, $a)) {
524 16
                if ($this->event_raise('HeadInvalidReceived', $connect, 0) === false) {
525
                    // Подключение сдохло
526 8
                    $this->close_connection($connect);
527
528 8
                    return false;
529
                }
530
531 8
                $this->answer($connect, 400, 'Bad Request', "This is not a HTTP request");
532 8
                $this->close_connection($connect);
533
534 8
                return false;
535
            }
536
537 424
            $connect->request_type = $a[1];
538 424
            $connect->request_url = $a[2];
539 424
            $connect->request_http_version = $a[3];
540 424
            if ($connect->request_type == 'POST') {
541 96
                $connect->status = 1;
542 376
            } elseif ($connect->request_type == 'GET') {
543 312
                $connect->status = 2;
544 312
                $connect->full_request_received_time = $time;
545 156
            } else {
546 16
                if ($this->event_raise('HeadInvalidReceived', $connect, 1) === false) {
547
                    // Подключение сдохло
548 8
                    $this->close_connection($connect);
549
550 8
                    return false;
551
                }
552
553 8
                $this->answer($connect, 400, 'Bad Request', "Can not process this request type");
554 8
                $this->close_connection($connect);
555
556 8
                return false;
557
            }
558
559 408
            $connect->request_head_params = [];
560 408
            foreach (explode("\r\n", $other_lines) as $other_line) {
561 408
                if (empty($other_line) or !preg_match('_^([A-Za-z0-9-]+):\\s*(.*)$_', $other_line, $a)) {
562 16
                    if ($this->event_raise('HeadInvalidReceived', $connect, 2) === false) {
563
                        // Подключение сдохло
564 8
                        $this->close_connection($connect);
565
566 8
                        return false;
567
                    }
568
569 8
                    $this->answer($connect, 400, 'Bad Request', "Malformed head");
570 8
                    $this->close_connection($connect);
571
572 8
                    return false;
573
                }
574
575 408
                $connect->request_head_params[$a[1]] = $a[2];
576 204
            }
577
578 392
            if (!isset($connect->request_head_params['Host'])) {
579 8
                $this->answer($connect, 400, 'Bad Request', "Field 'host' missed");
580 8
                $this->close_connection($connect);
581
582 8
                return false;
583
            }
584
585
            // event
586 384
            $this->event_raise('HeadReceived', $connect);
587
588 384
            return true;
589
        }
590
591
        /**
592
         * If returns "false" connection closed
593
         *
594
         * @param ClientDatum $connect
595
         * @param string      $buf
596
         * @param double      $time
597
         */
598 96
        protected function process_connected_client_body(&$connect, $buf, $time) {
599 96
            $head_end = strpos($connect->blob_request, "\r\n\r\n");
600 96
            if (!isset($connect->request_head_params['Content-Length'])) {
601 16
                if ($this->event_raise('HeadInvalidReceived', $connect, 3) === false) {
602 8
                    $this->close_connection($connect);
603
604 8
                    return;
605
                }
606
607 8
                $this->answer($connect, 400, 'malformed request', 'Malformed request');
608 8
                $this->close_connection($connect);
609
610 8
                return;
611
            }
612 80
            $requested_body_length = (int) $connect->request_head_params['Content-Length'];
613 80
            if (strlen($connect->blob_request) >= $head_end + 4 + $requested_body_length) {
614 72
                $connect->blob_body = substr($connect->blob_request, $head_end + 4, $requested_body_length);
615 72
                $connect->status = 2;
616 72
                $connect->body_received_time = $time;
617 72
                $connect->full_request_received_time = $time;
618
                // @hint Request raised in process_connected_client
619 36
            }
620
621 80
            $this->event_raise('BodyIncomingData', $connect, $buf);
622 80
        }
623
624
        /**
625
         * @param string $event
626
         *
627
         * @return mixed|null
628
         */
629 597
        function event_raise($event) {
630 597
            $method_name = 'on'.$event;
631 597
            if (!isset($this->_settings->{$method_name}) or !is_callable($this->_settings->{$method_name})) {
632 597
                return null;
633
            }
634
635 349
            $args = func_get_args();
636 349
            $args[0] = $this;
637
638 349
            return call_user_func_array($this->_settings->{$method_name}, $args);
639
        }
640
641
        /**
642
         * @param ClientDatum $connect
643
         * @param integer     $code
644
         * @param string      $code_text
645
         * @param string      $body
646
         * @param array       $headers
647
         */
648 429
        function answer($connect, $code, $code_text, $body, array $headers = []) {
649 429
            $buf = sprintf("HTTP/1.0 %d %s\r\n", $code, $code_text);
650 429
            $headers['Content-Length'] = strlen($body);
651 429
            foreach ($headers as $key => &$value) {
652 429
                $buf .= sprintf("%s: %s\r\n", $key, $value);
653 215
            }
654 429
            @fwrite($connect->client, "{$buf}\r\n{$body}");
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
655 429
        }
656
    }
657
658
?>