Files
kiri-coroutine-server/core/Transport.php
T
2026-05-09 22:17:19 +08:00

214 lines
3.4 KiB
PHP

<?php
namespace Coroutine\Server;
class Transport
{
/**
* @var array<int, Struct>
*/
private array $clients = [];
/**
* @var array<int, int>
*/
private array $userIdByFd = [];
/**
* @param int $userId
* @param Struct $data
* @return Struct|null
*/
public function add(int $userId, Struct $data): ?Struct
{
$previous = $this->clients[$userId] ?? null;
$this->clients[$userId] = $data;
if ($data->fd > 0) {
$this->userIdByFd[$data->fd] = $userId;
}
if ($previous && $previous->fd !== $data->fd) {
unset($this->userIdByFd[$previous->fd]);
@$previous->ws->close();
}
return $previous;
}
/**
* @param int $userId
* @param mixed $data
* @return void
*/
public function sendUserId(int $userId, mixed $data): void
{
$struct = $this->clients[$userId] ?? null;
if ($struct === null) {
return;
}
$struct->touch();
@$struct->ws->push($this->normalizePayload($data));
}
/**
* @param int $fd
* @param mixed $data
* @return void
*/
public function sendFd(int $fd, mixed $data): void
{
$struct = $this->getClientId($fd);
if ($struct === null) {
return;
}
$struct->touch();
@$struct->ws->push($this->normalizePayload($data));
}
/**
* @param int $userId
* @return void
*/
public function close(int $userId): void
{
$struct = $this->clients[$userId] ?? null;
if ($struct === null) {
return;
}
@$struct->ws->close();
}
/**
* @param int $userId
* @param int|null $fd
* @return void
*/
public function remove(int $userId, ?int $fd = null): void
{
$struct = $this->clients[$userId] ?? null;
if ($struct === null) {
return;
}
if ($fd !== null && $struct->fd !== $fd) {
return;
}
unset($this->clients[$userId]);
unset($this->userIdByFd[$struct->fd]);
@$struct->ws->close();
}
/**
* @param int $fd
* @return Struct|null
*/
public function getClientId(int $fd): ?Struct
{
$userId = $this->userIdByFd[$fd] ?? null;
if ($userId === null) {
return null;
}
return $this->clients[$userId] ?? null;
}
/**
* @param int $userId
* @return Struct|null
*/
public function getUserId(int $userId): ?Struct
{
return $this->clients[$userId] ?? null;
}
/**
* @param int $userId
* @return bool
*/
public function has(int $userId): bool
{
return isset($this->clients[$userId]);
}
/**
* @param int $userId
* @param int|null $fd
* @return void
*/
public function touch(int $userId, ?int $fd = null): void
{
$struct = $this->clients[$userId] ?? null;
if ($struct === null) {
return;
}
if ($fd !== null && $struct->fd !== $fd) {
return;
}
$struct->touch();
}
/**
* @return array
*/
public function getLists(): array
{
$array = [];
foreach ($this->clients as $userId => $client) {
$array[] = [
'userId' => $userId,
'fd' => $client->fd,
'nickname' => $client->user->getNickname(),
'connectedAt' => $client->connectedAt,
'lastSeenAt' => $client->lastSeenAt,
];
}
return $array;
}
/**
* @return int
*/
public function size(): int
{
return count($this->clients);
}
/**
* @param mixed $data
* @return string
*/
private function normalizePayload(mixed $data): string
{
if (is_string($data)) {
return $data;
}
if (is_scalar($data) || $data === null) {
return (string)$data;
}
$payload = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return $payload === false ? '' : $payload;
}
}