*/ private array $clients = []; /** * @var array */ private array $userIdByFd = []; 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; } 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)); } public function sendFd(int $fd, mixed $data): void { $struct = $this->getClientId($fd); if ($struct === null) { return; } $struct->touch(); @$struct->ws->push($this->normalizePayload($data)); } public function close(int $userId): void { $struct = $this->clients[$userId] ?? null; if ($struct === null) { return; } @$struct->ws->close(); } 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(); } public function getClientId(int $fd): ?Struct { $userId = $this->userIdByFd[$fd] ?? null; if ($userId === null) { return null; } return $this->clients[$userId] ?? null; } public function getUserId(int $userId): ?Struct { return $this->clients[$userId] ?? null; } public function has(int $userId): bool { return isset($this->clients[$userId]); } 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(); } 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; } public function size(): int { return count($this->clients); } 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; } }