Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
SocketClient
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
8 / 8
15
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 connect
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 disconnect
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 receive
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
4
 send
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 isConnected
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 ensureConnected
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 __destruct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace AqwSocketClient\Clients;
6
7use AqwSocketClient\Interfaces\MessageInterface;
8use AqwSocketClient\Interfaces\SocketInterface;
9use AqwSocketClient\Messages\DelimitedMessage;
10use AqwSocketClient\Messages\JsonMessage;
11use AqwSocketClient\Messages\XmlMessage;
12use AqwSocketClient\Packet;
13use AqwSocketClient\Server;
14use AqwSocketClient\Sockets\NativeSocket;
15use Override;
16use RuntimeException;
17
18/**
19 * @mago-ignore analyzer:unhandled-thrown-type
20 */
21final class SocketClient extends AbstractClient
22{
23    private bool $connected = false;
24
25    public function __construct(
26        public readonly Server $server,
27        private readonly SocketInterface $socket = new NativeSocket(),
28    ) {
29        $this->socket->create();
30    }
31
32    #[Override]
33    public function connect(): void
34    {
35        if ($this->isConnected()) {
36            throw new RuntimeException('Already connected.');
37        }
38
39        $this->socket->connect($this->server->hostname, $this->server->port);
40
41        $this->connected = true;
42    }
43
44    #[Override]
45    public function disconnect(): void
46    {
47        if (!$this->isConnected()) {
48            throw new RuntimeException('Not connected.');
49        }
50
51        $this->socket->close();
52
53        $this->connected = false;
54    }
55
56    /**
57     * @throws RuntimeException When fail to receive data
58     * @return MessageInterface[]
59     */
60    #[Override]
61    public function receive(): array
62    {
63        $this->ensureConnected();
64
65        $buffer = '';
66
67        while (true) {
68            ['bytes' => $bytes, 'chunk' => $chunk] = $this->socket->read(1);
69
70            if ($bytes === 0) {
71                break;
72            }
73
74            if ($chunk === "\0") {
75                break;
76            }
77
78            $buffer .= $chunk;
79        }
80
81        return array_values(array_filter([
82            DelimitedMessage::from($buffer),
83            JsonMessage::from($buffer),
84            XmlMessage::from($buffer),
85        ]));
86    }
87
88    /**
89     * @throws RuntimeException When not connected or fail to send data
90     */
91    #[Override]
92    public function send(Packet $packet): void
93    {
94        $this->ensureConnected();
95
96        $data = $packet->unpacketify();
97        $this->socket->send($data);
98    }
99
100    #[Override]
101    public function isConnected(): bool
102    {
103        return $this->connected;
104    }
105
106    private function ensureConnected(): void
107    {
108        if (!$this->isConnected()) {
109            throw new RuntimeException('Not connected.');
110        }
111    }
112
113    public function __destruct()
114    {
115        if ($this->isConnected()) {
116            $this->socket->close();
117        }
118    }
119}