Skip to content

Commit 91944ba

Browse files
committed
Added room system
Tweaking system to become standalone
1 parent 2ab6295 commit 91944ba

11 files changed

+219
-29
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/.idea
22
/vendor
33

4-
/composer.lock
4+
/composer.lock
5+
/tester
6+
/test.php

composer.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"php": ">=8.1",
1414
"phprtc/contracts": "dev-master",
1515
"phprtc/utils": "dev-master",
16-
"phprtc/server": "dev-master"
16+
"phprtc/server": "dev-master",
17+
"phprtc/console": "dev-main"
1718
},
1819
"autoload": {
1920
"psr-4": {
@@ -34,6 +35,7 @@
3435
"require-dev": {
3536
"phpstan/phpstan": "^1.4",
3637
"phpunit/phpunit": "^9.5",
37-
"openswoole/ide-helper": "^4.10"
38+
"openswoole/ide-helper": "^4.10",
39+
"jetbrains/phpstorm-attributes": "^1.0"
3840
}
3941
}

phpstan.neon

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
parameters:
22
level: 8
33
paths: ['src', 'tests']
4-
checkMissingIterableValueType: false
4+
checkMissingIterableValueType: false
5+
ignoreErrors:
6+
- '#Unsafe usage of new static\(\)\.#'

src/Connection.php

+13-10
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
namespace RTC\Websocket;
44

5+
use RTC\Contracts\Server\ServerInterface;
56
use RTC\Contracts\Websocket\ConnectionInterface;
6-
use RTC\Server\Server;
77

88
class Connection implements ConnectionInterface
99
{
1010
public function __construct(
11-
protected Server $server,
12-
protected int $fd
11+
protected ServerInterface $server,
12+
protected int $fd
1313
)
1414
{
1515
}
@@ -25,13 +25,16 @@ public function send(
2525
): void
2626
{
2727
if ($this->server->exists($this->getIdentifier())) {
28-
$data = [
29-
'command' => $command,
30-
'data' => $data,
31-
'time' => time()
32-
];
33-
34-
$this->server->push($this->fd, (string)json_encode($data), $opcode, $flags);
28+
$this->server->push(
29+
fd: $this->fd,
30+
data: (string)json_encode([
31+
'command' => $command,
32+
'data' => $data,
33+
'time' => microtime(true)
34+
]),
35+
opcode: $opcode,
36+
flags: $flags,
37+
);
3538
}
3639
}
3740

src/Enums/RoomEnum.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace RTC\Websocket\Enums;
4+
5+
enum RoomEnum: int
6+
{
7+
case EVENT_ON_ADD = 2000;
8+
case EVENT_ON_LEAVE = 2001;
9+
case EVENT_ON_REMOVE = 2002;
10+
case EVENT_ON_REMOVE_ALL = 2003;
11+
case EVENT_ON_MESSAGE = 2004;
12+
case EVENT_ON_MESSAGE_ALL = 2005;
13+
}

src/Exceptions/Exception.php

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace RTC\Websocket\Exceptions;
4+
5+
class Exception extends \RTC\Contracts\Exception
6+
{
7+
8+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace RTC\Websocket\Exceptions;
4+
5+
class RoomOverflowException extends Exception
6+
{
7+
8+
}

src/Frame.php

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@
77

88
class Frame implements FrameInterface
99
{
10-
protected array $decodedMessage;
10+
protected array|string $decodedMessage;
1111

1212

1313
public function __construct(protected \Swoole\WebSocket\Frame $frame)
1414
{
15-
$this->decodedMessage = json_decode($this->frame->data, true);
15+
$decodedMessage = json_decode($this->frame->data, true);
16+
17+
if (is_array($decodedMessage)) {
18+
$this->decodedMessage = $decodedMessage;
19+
} else {
20+
$this->decodedMessage = (string)$decodedMessage;
21+
}
1622
}
1723

1824
/**

src/Payload.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77

88
class Payload implements PayloadInterface
99
{
10-
protected array $decodedMessage;
10+
protected array $decodedMessage = [];
1111
protected float $serverTime;
1212

1313
public function __construct(protected Frame $frame)
1414
{
15-
$this->decodedMessage = json_decode($this->frame->data, true);
15+
$decodedMessage = json_decode($this->frame->data, true);
16+
17+
if (is_array($decodedMessage)) {
18+
$this->decodedMessage = $decodedMessage;
19+
}
20+
1621
$this->serverTime = microtime(true);
1722
}
1823

src/Room.php

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace RTC\Websocket;
5+
6+
use JetBrains\PhpStorm\Pure;
7+
use RTC\Contracts\Server\ServerInterface;
8+
use RTC\Contracts\Websocket\ConnectionInterface;
9+
use RTC\Server\Event;
10+
use RTC\Websocket\Enums\RoomEnum;
11+
use RTC\Websocket\Exceptions\RoomOverflowException;
12+
use Swoole\Table;
13+
14+
class Room extends Event
15+
{
16+
private Table $connections;
17+
18+
19+
public static function create(ServerInterface $server, string $name, int $size = -1): static
20+
{
21+
return new static($server, $name, $size);
22+
}
23+
24+
25+
public function __construct(
26+
protected ServerInterface $server,
27+
protected string $name,
28+
protected int $size = 1024
29+
)
30+
{
31+
$this->connections = new Table($this->size);
32+
$this->connections->column('conn', Table::TYPE_INT, 100);
33+
$this->connections->create();
34+
}
35+
36+
/**
37+
* @throws RoomOverflowException
38+
*/
39+
public function add(int|ConnectionInterface $connection): static
40+
{
41+
if (-1 != $this->size && $this->connections->count() == $this->size) {
42+
throw new RoomOverflowException("The maximum size of $this->size for room $this->name has been reached.");
43+
}
44+
45+
$connectionId = $this->getClientId($connection);
46+
$this->connections->set(key: $connectionId, value: ['conn' => $connectionId]);
47+
48+
// Fire client add event
49+
$this->emit(RoomEnum::EVENT_ON_ADD->value, [$connection]);
50+
51+
return $this;
52+
}
53+
54+
public function has(int|ConnectionInterface $connection): bool
55+
{
56+
return $this->connections->exist($this->getClientId($connection));
57+
}
58+
59+
public function count(): int
60+
{
61+
return $this->connections->count();
62+
}
63+
64+
public function remove(int|ConnectionInterface $connection): void
65+
{
66+
$connectionId = $this->getClientId($connection);
67+
68+
if ($this->connections->exist($connectionId)) {
69+
$this->connections->del($connectionId);
70+
}
71+
72+
// Fire client remove event
73+
$this->emit(RoomEnum::EVENT_ON_REMOVE->value, [$connection]);
74+
}
75+
76+
public function removeAll(): void
77+
{
78+
$connections = clone $this->connections;
79+
$this->connections->destroy();
80+
81+
$this->emit(RoomEnum::EVENT_ON_REMOVE_ALL->value, [$connections]);
82+
}
83+
84+
public function getClients(): Table
85+
{
86+
return $this->connections;
87+
}
88+
89+
/**
90+
* @param string $command
91+
* @param mixed $message
92+
* @return int Number of successful recipients
93+
*/
94+
public function send(string $command, mixed $message): int
95+
{
96+
// Fire message event
97+
$this->emit(RoomEnum::EVENT_ON_MESSAGE->value, [$command, $message]);
98+
99+
foreach ($this->connections as $connectionData) {
100+
$this->server->push(
101+
fd: $connectionData['conn'],
102+
data: (string)json_encode([
103+
'command' => $command,
104+
'data' => ['message' => $message],
105+
'time' => microtime(true)
106+
]),
107+
);
108+
}
109+
110+
return $this->connections->count();
111+
}
112+
113+
public function sendAsClient(ConnectionInterface $connection, string $command, mixed $message): int
114+
{
115+
// Fire message event
116+
$this->emit(RoomEnum::EVENT_ON_MESSAGE_ALL->value, [$command, $message, clone $this->connections]);
117+
118+
foreach ($this->connections as $connectionData) {
119+
$this->server->push(
120+
fd: $connectionData['conn'],
121+
data: (string)json_encode([
122+
'command' => $command,
123+
'data' => [
124+
'sender' => $connection,
125+
'message' => $message
126+
],
127+
'time' => microtime(true)
128+
]),
129+
);
130+
}
131+
132+
return $this->connections->count();
133+
}
134+
135+
#[Pure] protected function getClientId(int|ConnectionInterface $connection): string
136+
{
137+
return (string)(is_int($connection) ? $connection : $connection->getIdentifier());
138+
}
139+
}

src/WebsocketHandler.php

+13-11
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,39 @@
22

33
namespace RTC\Websocket;
44

5-
use JetBrains\PhpStorm\Pure;
5+
use RTC\Contracts\Server\ServerInterface;
66
use RTC\Contracts\Websocket\ConnectionInterface;
77
use RTC\Contracts\Websocket\WebsocketHandlerInterface;
8-
use RTC\Server\Server;
98
use Swoole\Table;
109

1110
abstract class WebsocketHandler implements WebsocketHandlerInterface
1211
{
13-
protected Table $connections;
12+
private Table $connections;
13+
1414

1515
public function __construct(
16-
protected readonly Server $server,
17-
int $size = 2048
16+
protected readonly ServerInterface $server,
17+
protected readonly int $size = 2048
1818
)
1919
{
20-
$this->connections = new Table($size);
20+
$this->connections = new Table($this->size);
2121
$this->connections->column('conn', Table::TYPE_INT, 100);
2222
$this->connections->create();
2323
}
2424

25+
public function onReady(): void
26+
{
27+
}
28+
2529
public function addConnection(ConnectionInterface $connection): void
2630
{
2731
$this->connections->set((string)$connection->getIdentifier(), ['conn' => $connection->getIdentifier()]);
2832
}
2933

30-
#[Pure] public function getConnection(int $fd): ?ConnectionInterface
34+
public function getConnection(int $fd): ?ConnectionInterface
3135
{
32-
foreach ($this->connections as $connectionData) {
33-
if ($fd == $connectionData['conn']) {
34-
return new Connection($this->server, $fd);
35-
}
36+
if ($this->connections->exist((string)$fd)) {
37+
return new Connection($this->server, $fd);
3638
}
3739

3840
return null;

0 commit comments

Comments
 (0)