$exception->getMessage(), ]); } try { $pendingMigrations = count(manageUpdatePendingMigrations()); } catch (Throwable $exception) { manageClientLog("WARNING", "Heartbeat could not read migrations", [ "error" => $exception->getMessage(), ]); } $diskFree = 0; try { $free = @disk_free_space(manageClientAppRoot()); if (is_float($free) || is_int($free)) { $diskFree = (int) $free; } } catch (Throwable $exception) { $diskFree = 0; } return [ "version" => manageClientVersion(), "php_version" => PHP_VERSION, "disk_free" => $diskFree, "pending_migrations" => $pendingMigrations, "last_backup_at" => $lastBackupAt, ]; } /** * Sends the heartbeat. * * @return array{success: bool, latest: string, update_available: bool, server_time: string} */ function manageHeartbeatSend(): array { $decoded = manageClientRequestJson( "POST", "heartbeat.php", manageHeartbeatPayload(), (int) MANAGE_HTTP_TIMEOUT, ); return [ "success" => !empty($decoded["success"]), "latest" => (string) ($decoded["latest"] ?? ""), "update_available" => !empty($decoded["update_available"]), "server_time" => (string) ($decoded["server_time"] ?? ""), ]; } /** * Heartbeat that never throws. For call sites inside a host application where * an unreachable server must not surface as an error. */ function manageHeartbeatSendQuietly(): ?array { try { return manageHeartbeatSend(); } catch (Throwable $exception) { manageClientLog("WARNING", "Heartbeat failed", ["error" => $exception->getMessage()]); return null; } }