Completed
Push — master ( 4e4356...ccb503 )
by Dawid
02:52
created

src/Server.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php declare(strict_types=1);
2
3
namespace Igni\Network;
4
5
use Igni\Exception\RuntimeException;
6
use Igni\Network\Exception\ServerException;
7
use Igni\Network\Server\Client;
8
use Igni\Network\Server\Configuration;
9
use Igni\Network\Server\HandlerFactory;
10
use Igni\Network\Server\Listener;
11
use Igni\Network\Server\LogWriter;
12
use Igni\Network\Server\OnCloseListener;
13
use Igni\Network\Server\OnConnectListener;
14
use Igni\Network\Server\OnReceiveListener;
15
use Igni\Network\Server\OnShutdownListener;
16
use Igni\Network\Server\OnStartListener;
17
use Igni\Network\Server\ServerStats;
18
use Psr\Log\LoggerInterface;
19
use SplQueue;
20
use Swoole\Server as SwooleServer;
21
22
use function extension_loaded;
23
24
/**
25
 * Http server implementation based on swoole extension.
26
 *
27
 * @package Igni\Http
28
 */
29
class Server implements HandlerFactory
30
{
31
    private const SWOOLE_EXT_NAME = 'swoole';
32
33
    /**
34
     * @var SwooleServer|null
35
     */
36
    protected $handler;
37
38
    /**
39
     * @var Configuration
40
     */
41
    protected $configuration;
42
43
    /**
44
     * @var SplQueue[]
45
     */
46
    protected $listeners = [];
47
48
    /**
49
     * @var LoggerInterface
50
     */
51
    protected $logger;
52
53
    /**
54
     * @var HandlerFactory
55
     */
56
    protected $handlerFactory;
57
58
    /**
59
     * @var Client[]
60
     */
61
    private $clients = [];
62
63
    /**
64
     * @var bool
65
     */
66
    private $started = false;
67
68 15
    public function __construct(
69
        Configuration $settings = null,
70
        LoggerInterface $logger = null,
71
        HandlerFactory $handlerFactory = null
72
    ) {
73 15
        if (!extension_loaded(self::SWOOLE_EXT_NAME)) {
74
            throw new RuntimeException('Swoole extenstion is missing, please install it and try again.');
75
        }
76
77 15
        $this->handlerFactory = $handlerFactory ?? $this;
78 15
        $this->configuration = $settings ?? new Configuration();
79 15
        $this->logger = new LogWriter($logger);
80 15
    }
81
82
    /**
83
     * Return server configuration.
84
     *
85
     * @return Configuration
86
     */
87 2
    public function getConfiguration(): Configuration
88
    {
89 2
        return $this->configuration;
90
    }
91
92 6
    public function getClient(int $id): Client
93
    {
94 6
        return $this->clients[$id];
95
    }
96
97
    /**
98
     * Adds listener that is attached to server once it is run.
99
     *
100
     * @param Listener $listener
101
     */
102 11
    public function addListener(Listener $listener): void
103
    {
104 11
        $this->addListenerByType($listener, OnStartListener::class);
105 11
        $this->addListenerByType($listener, OnCloseListener::class);
106 11
        $this->addListenerByType($listener, OnConnectListener::class);
107 11
        $this->addListenerByType($listener, OnShutdownListener::class);
108 11
        $this->addListenerByType($listener, OnReceiveListener::class);
109 11
    }
110
111 11
    protected function addListenerByType(Listener $listener, string $type): void
112
    {
113 11
        if ($listener instanceof $type) {
114 11
            if (!isset($this->listeners[$type])) {
115 11
                $this->listeners[$type] = new SplQueue();
116
            }
117 11
            $this->listeners[$type]->push($listener);
118
        }
119 11
    }
120
121
    /**
122
     * Checks if listener exists.
123
     *
124
     * @param Listener $listener
125
     * @return bool
126
     */
127 1
    public function hasListener(Listener $listener): bool
128
    {
129
        /** @var SplQueue $listenerCollection */
130 1
        foreach ($this->listeners as $listenerCollection) {
131 1
            foreach ($listenerCollection as $current) {
132 1
                if ($current === $listener) {
133 1
                    return true;
134
                }
135
            }
136
        }
137
138 1
        return false;
139
    }
140
141
    /**
142
     * Returns information about server.
143
     *
144
     * @return ServerStats
145
     */
146 1
    public function getServerStats(): ServerStats
147
    {
148 1
        if (!$this->started) {
149 1
            throw ServerException::forMethodCallOnIdleServer(__METHOD__);
150
        }
151
        return new ServerStats($this->handler->stats());
152
    }
153
154
    public function createHandler(Configuration $configuration)
155
    {
156
        $flags = SWOOLE_TCP;
157
        if ($configuration->isSslEnabled()) {
158
            $flags |= SWOOLE_SSL;
159
        }
160
        $settings = $configuration->toArray();
161
        $handler = new SwooleServer($settings['address'], $settings['port'], SWOOLE_PROCESS, $flags);
162
        $handler->set($settings);
163
164
        return $handler;
165
    }
166
167 10
    public function start(): void
168
    {
169 10
        $this->addListener($this->logger);
0 ignored issues
show
$this->logger is of type object<Psr\Log\LoggerInterface>, but the function expects a object<Igni\Network\Server\Listener>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
170 10
        $this->handler = $this->handlerFactory->createHandler($this->configuration);
171 10
        $this->createListeners();
172 10
        $this->handler->start();
173 10
        $this->started = true;
174 10
    }
175
176 1
    public function stop(): void
177
    {
178 1
        if ($this->handler !== null) {
179 1
            $this->handler->shutdown();
180 1
            $this->handler = null;
181
        }
182 1
        $this->started = false;
183 1
    }
184
185 10
    protected function createListeners(): void
186
    {
187 10
        $this->createOnConnectListener();
188 10
        $this->createOnCloseListener();
189 10
        $this->createOnShutdownListener();
190 10
        $this->createOnStartListener();
191 10
        $this->createOnReceiveListener();
192 10
    }
193
194 6
    private function createClient($handler, int $clientId): Client
195
    {
196 6
        return $this->clients[$clientId] = new Client($handler, $clientId);
197
    }
198
199 4
    private function destroyClient(int $clientId): void
200
    {
201 4
        unset($this->clients[$clientId]);
202 4
    }
203
204 10
    protected function createOnConnectListener(): void
205
    {
206
        $this->handler->on('Connect', function($handler, int $clientId) {
207 6
            $this->createClient($handler, $clientId);
208
209 6
            if (!isset($this->listeners[OnConnectListener::class])) {
210
                return;
211
            }
212
213 6
            $queue = clone $this->listeners[OnConnectListener::class];
214
            /** @var OnConnectListener $listener */
215 6
            while (!$queue->isEmpty() && $listener = $queue->pop()) {
216 6
                $listener->onConnect($this, $this->getClient($clientId));
217
            }
218 10
        });
219 10
    }
220
221 10
    protected function createOnCloseListener(): void
222
    {
223
        $this->handler->on('Close', function($handler, int $clientId) {
224 4
            if (isset($this->listeners[OnCloseListener::class])) {
225
226 4
                $queue = clone $this->listeners[OnCloseListener::class];
227
                /** @var OnCloseListener $listener */
228 4
                while (!$queue->isEmpty() && $listener = $queue->pop()) {
229 4
                    $listener->onClose($this, $this->getClient($clientId));
230
                }
231
            }
232
233 4
            $this->destroyClient($clientId);
234 10
        });
235 10
    }
236
237 10
    protected function createOnShutdownListener(): void
238
    {
239
        $this->handler->on('Shutdown', function() {
240 1
            if (!isset($this->listeners[OnShutdownListener::class])) {
241
                return;
242
            }
243
244 1
            $queue = clone $this->listeners[OnShutdownListener::class];
245
246
            /** @var OnShutdownListener $listener */
247 1
            while (!$queue->isEmpty() && $listener = $queue->pop()) {
248 1
                $listener->onShutdown($this);
249
            }
250 10
        });
251 10
    }
252
253 10
    protected function createOnStartListener(): void
254
    {
255
        $this->handler->on('Start', function() {
256 1
            if (!isset($this->listeners[OnStartListener::class])) {
257
                return;
258
            }
259
260 1
            $queue = clone $this->listeners[OnStartListener::class];
261
            /** @var OnStartListener $listener */
262 1
            while (!$queue->isEmpty() && $listener = $queue->pop()) {
263 1
                $listener->onStart($this);
264
            }
265 10
        });
266 10
    }
267
268 10
    protected function createOnReceiveListener(): void
269
    {
270
        $this->handler->on('Receive', function ($handler, int $clientId, int $fromId, string $data) {
271 1
            if (!isset($this->listeners[OnReceiveListener::class])) {
272
                return;
273
            }
274
275 1
            $queue = clone $this->listeners[OnReceiveListener::class];
276
277
            /** @var OnReceiveListener $listener */
278 1
            while (!$queue->isEmpty() && $listener = $queue->pop()) {
279 1
                $listener->onReceive($this, $this->getClient($clientId), $data);
280
            }
281 10
        });
282 10
    }
283
}
284