04_FUNCTION_API.md 7.2 KB

Function API

Overview

All functions become available after a single require:

require_once __DIR__ . "/manage-client/lib/client.php";

The command line (bin/manage-client.php) and the UI (ui/panel.php) contain no logic of their own — they call exactly these functions. A function therefore behaves identically no matter how it's triggered.

Errors are thrown as RuntimeException. Exceptions to this rule are noted explicitly.

Status

manageClientStatus(): array

Collective call for UIs. Never throws: every error ends up in the return value, so a settings page still renders even when the server is unreachable.

[
    "instance"           => "my-project-prod",
    "server_url"         => "https://manage.example.org",
    "configured"         => true,
    "version"            => "v1.2.3",   // "" if it can't be determined
    "php_version"        => "8.3.6",
    "update"             => [...],      // return value of manageUpdateCheck(), or null
    "update_error"       => null,       // error message if the check failed
    "backups"            => [...],      // return value of manageBackupList()
    "last_backup_at"     => "2026-08-20T09:21:04+00:00",
    "pending_migrations" => [...],
    "errors"             => [],         // non-fatal warnings
]

manageClientVersion(): string

Installed version, for example "v1.2.3". Returns "" if it can't be determined; that's not an error, just "unknown".

manageClientConfigured(): bool

Whether server URL, instance and token are set.

Update

manageUpdateCheck(): array

Fetches the manifest and compares versions.

[
    "current"   => "v1.2.3",
    "latest"    => "v1.3.0",
    "available" => true,
    "manifest"  => [
        "version"      => "v1.3.0",
        "package_url"  => "https://…/api/v1/package.php?version=v1.3.0",
        "sha256"       => "…",
        "size"         => 421337,
        "published_at" => "2026-08-20T09:20:43+00:00",
    ],
]

If the installed version is unknown, available => true holds, so an installation without a readable version file isn't permanently blocked.

manageUpdateApply(array $options = []): array

Downloads the package, checks size and SHA-256, extracts it, deploys it, and then runs the post-update step.

Options:

Option Meaning
force deploy even when no newer version is available
skip_hook only deploy files, run neither migrations nor the callback

Return value:

[
    "deployed"        => true,
    "from_version"    => "v1.2.3",
    "to_version"      => "v1.3.0",
    "copied"          => 128,
    "backed_up"       => 126,
    "skipped"         => 2,      // protected paths
    "removed_backups" => 1,
    "backup_dir"      => "/…/data/manage/updates/20260820-092114-v1.3.0",
    "hook"            => [...],  // see below
]

Throws if deployment fails. If only the post-update step fails, the function returns normally and hook["success"] is false — the files are then already deployed. Callers must distinguish between the two; see 07_POST_UPDATE_HOOKS.

Important: deployment overwrites files while the application is live. There is no maintenance page and no rollback. The overwritten files sit as copies in backup_dir, but exclusively for manual restoration.

manageUpdatePendingMigrations(): array

Migrations not yet run, in execution order:

[["id" => "2026-08-20-01-add-index", "path" => "/…/migrations/2026-08-20-01-add-index.php"]]

manageUpdateRunMigrations(array $context = []): array

Runs the pending migrations. Never throws, reports instead:

[
    "success" => false,
    "applied" => ["2026-08-20-01-add-index"],
    "failed"  => "2026-08-20-02-backfill",
    "error"   => "SQLSTATE[42S22]: …",
    "pending" => 2,   // including the failed one
]

Backup

manageBackupCreate(string $trigger = "manual"): array

Creates a local archive and uploads it, provided MANAGE_BACKUP_UPLOAD is active.

$trigger is free-form; common values are manual, automatic, cron, update. Only automatic and cron count toward the interval check.

[
    "filename"       => "backup-20260820-092104.zip",
    "created_at"     => "2026-08-20T09:21:04+00:00",
    "trigger"        => "cron",
    "size"           => 427,
    "file_count"     => 3,
    "source_bytes"   => 63,
    "sha256"         => "…",
    "app_version"    => "v1.2.3",
    "database"       => null,   // or ["tables" => 12, "rows" => 4711, …]
    "remote_uploads" => [
        ["target" => "Manage Server", "type" => "manage", "success" => true, …],
    ],
]

A failed upload does not invalidate the local archive: the error sits in remote_uploads[].error and in the log; the function doesn't throw. If it does throw, the archive itself never came into being.

Concurrent runs are prevented by a lock file; a second run throws immediately with Es läuft bereits ein Backup. ("a backup is already running" — the literal, still German, message text).

manageBackupCreateAutomaticIfDue(): ?array

Creates a backup once MANAGE_BACKUP_AUTO_INTERVAL_SECONDS has passed since the last automatic backup, otherwise null. Meant for hosting without cron; belongs on a rarely loaded admin page.

manageBackupList(): array

Local archives, newest first. Self-healing: entries without a file are removed, sizes are refreshed from disk.

manageBackupUpload(string $archivePath, array $meta = []): array

Uploads an existing archive to the Manage server. Called automatically by manageBackupCreate(); only needed on its own for special cases — for example, resending an archive after a server outage.

manageBackupPath(string $filename): string

Absolute path of a local archive. Validates the filename strictly, so a download form can't be made to serve an arbitrary path.

Heartbeat

manageHeartbeatSend(): array

Reports version, PHP version, free disk space, pending migrations and the time of the last backup. The response includes the update information as a side effect:

["success" => true, "latest" => "v1.3.0", "update_available" => true, "server_time" => "…"]

manageHeartbeatSendQuietly(): ?array

Same as above, but never throws and returns null on error. For calls inside the project where an unreachable server should have no consequence.

Helper functions

Function Purpose
manageFormatBytes(int $bytes): string 1234567 → 1.18 MB
manageClientLog(string $level, string $message, array $context = []): void one line into the client log. Never throws
manageRemoteCapabilities(): array which configured target types this system supports

Example

require_once __DIR__ . "/manage-client/lib/client.php";

$check = manageUpdateCheck();
if ($check["available"]) {
    manageBackupCreate("update");          // back up before the update
    $result = manageUpdateApply();

    if (!$result["hook"]["success"]) {
        // Files are deployed, the post-update step is not.
        error_log("Migration failed: " . $result["hook"]["error"]);
    }
}

A backup before the update is deliberately not built in, but a line in the project — so it stays visible that it happens.