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
|
|||
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 |
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: