type === 'tcp' ? $this->tcp((string) $check->target, (int) $check->port, $timeout) : $this->http((string) $check->target, $timeout); } private function http(string $url, int $timeout): array { $start = microtime(true); try { $res = Http::timeout($timeout)->withoutRedirecting()->get($url); $latency = (int) round((microtime(true) - $start) * 1000); $status = $res->status(); // 2xx/3xx = up; a 4xx/5xx means the endpoint answered but is unhealthy. return ['ok' => $status >= 200 && $status < 400, 'latency' => $latency, 'detail' => 'HTTP '.$status]; } catch (Throwable $e) { return ['ok' => false, 'latency' => null, 'detail' => $this->reason($e->getMessage())]; } } private function tcp(string $host, int $port, int $timeout): array { $start = microtime(true); $errno = 0; $errstr = ''; $sock = @stream_socket_client('tcp://'.$host.':'.$port, $errno, $errstr, $timeout); if (! $sock) { return ['ok' => false, 'latency' => null, 'detail' => $errstr !== '' ? $errstr : 'connection failed']; } fclose($sock); $latency = (int) round((microtime(true) - $start) * 1000); return ['ok' => true, 'latency' => $latency, 'detail' => 'TCP '.$port.' open']; } /** Keep the failure reason short + on one line for the compact status row. */ private function reason(string $msg): string { $msg = trim(preg_split('/\R/', $msg)[0] ?? $msg); return mb_strlen($msg) > 80 ? mb_substr($msg, 0, 80).'…' : $msg; } }