<?phpnamespace App\Net;use Evenement\EventEmitter;use PhpWasmNetwork\Connection;use PhpWasmNetwork\Event;use PhpWasmNetwork\Network;/** * A Link over a WebSocket (wss://…, port 443: it passes every firewall and proxy), with php-wasm-network, JumPHP's * network library: the browser's WebSocket in the page, a non-blocking socket on a desktop. The chat server * (caramail-server, its WebSocket port) speaks the little of the relay protocol needed: the client joins the room * "CHAT", where the server is peer 0; each binary message carries the applet's TCP bytes unchanged. * * poll() each frame (the window does it). */final class WsLink extends EventEmitter implements Link{ public const ROOM = 'CHAT'; private ?Connection $net = null; private bool $open = false; private bool $closed = false; public function __construct(private readonly string $url) { } public static function isUrl(string $server): bool { return (bool)preg_match('#^wss?://#i', $server); } public function poll(): void { if ($this->closed) { return; } if ($this->net === null) { // opened at the first poll: the window is drawn before a desktop connection's TLS handshake $this->net = Network::connect($this->url, ['transport' => 'ws']); } foreach ($this->net->poll() as $event) { switch ($event->type) { case Event::OPEN: $this->net->joinRoom(self::ROOM); break; case Event::ROOM: if (!$this->open) { $this->open = true; $this->emit('open'); } break; case Event::MESSAGE: $this->emit('data', [$event->data]); break; case Event::ERROR: $info = $event->info(); $this->emit('error', [(string)($info['message'] ?? $info['code'] ?? 'network error')]); break; case Event::CLOSE: $this->finish('closed'); return; } } if ($this->net->state() === Connection::CLOSED) { $this->finish('closed'); } } public function write(string $bytes): void { if ($this->open && $this->net !== null) { $this->net->send($bytes, 0); } } public function close(): void { $this->net?->close(); $this->finish('closed'); } private function finish(string $why): void { if ($this->closed) { return; } $this->closed = true; $this->open = false; $this->emit('close', [$why]); }}