<?phpnamespace App\Net;use App\Chat\Frame;use App\Chat\Session;/** One TCP client: what it sent and is not a whole line yet, what waits to be written. */final class TcpSession implements Session{ public const MAX_OUTPUT = 1_048_576; public string $buffer = ''; private string $output = ''; private bool $closed = false; /** @param resource $stream */ public function __construct(public $stream, private readonly string $peer) { } public function send(string $code, array $fields = []): void { if ($this->closed) { return; } $this->output .= Frame::encode($code, $fields); if (strlen($this->output) > self::MAX_OUTPUT) { $this->close(); // a client that does not read any more return; } $this->flush(); } public function hasOutput(): bool { return $this->output !== '' && !$this->closed; } public function flush(): void { if ($this->closed || $this->output === '') { return; } $written = @fwrite($this->stream, $this->output); if ($written === false) { $this->close(); return; } $this->output = (string)substr($this->output, $written); } public function close(): void { if ($this->closed) { return; } $this->flush(); $this->closed = true; if (is_resource($this->stream)) { @fclose($this->stream); } } public function isClosed(): bool { return $this->closed; } public function address(): string { return $this->peer; } public function ip(): string { return ClientIp::host($this->peer); }}