Was gesichert wird, steht in MANAGE_BACKUP_SOURCES. Optional kommt ein
MySQL-Dump dazu. Wohin gesichert wird, steuern MANAGE_BACKUP_UPLOAD und
MANAGE_BACKUP_REMOTE_TARGETS.
Relevante Dateien:
manage-client/lib/backup.php – Sammeln, Archivieren, Aufbewahrung, Uploadmanage-client/lib/zip.php – ZIP-Erzeugung ohne externe Bibliothekmanage-client/lib/mysql.php – optionaler Datenbank-Dumpmanage-client/lib/remote.php – zusätzliche ZieleAlle Pfade sind relativ zu MANAGE_APP_ROOT. Jeder Eintrag verwendet genau eine
der drei Formen:
define("MANAGE_BACKUP_SOURCES", [
// Nicht-rekursives Glob-Muster
["as" => "data", "glob" => "data/*.json"],
// Verzeichnis, rekursiv
["as" => "data/uploads", "dir" => "data/uploads"],
// Einzelne Datei
["as" => "config", "file" => "config.dist.php"],
]);
as ist das Präfix im Archiv. Ohne as landet der Eintrag auf oberster Ebene –
bei dir wird der Verzeichnisname als Präfix verwendet.
Das obige Beispiel erzeugt:
data/orders.json
data/settings.json
data/uploads/2026/bild.jpg
config/config.dist.php
.tmp und .partErgeben zwei Quellen denselben Archivpfad, gewinnt die erste. Ein Archiv enthält nie zwei Einträge mit gleichem Namen.
config.php mit echten Zugangsdaten – ein Backup wird an den Server übertragen
und dort heruntergeladen; siehe 10_SECURITYEin Backup sichert Betriebsdaten, kein vollständiges Systemabbild.
Nur aktiv, wenn MANAGE_BACKUP_DATABASE gesetzt ist:
define("MANAGE_BACKUP_DATABASE", [
"dsn" => "mysql:host=localhost;dbname=meinprojekt;charset=utf8mb4",
"user" => "meinprojekt",
"password" => "…",
// Optional: von diesen Tabellen nur die Struktur sichern, nicht die Zeilen.
"skip_data_tables" => ["sessions", "cache"],
// Optional: Name im Archiv, falls SELECT DATABASE() nichts liefert.
"name" => "meinprojekt",
]);
Der Dump landet im Archiv als database/<name>.sql und enthält für jede Tabelle
DROP TABLE IF EXISTS, das CREATE TABLE aus SHOW CREATE TABLE und die Zeilen
als einzelne INSERT-Anweisungen. SET FOREIGN_KEY_CHECKS=0 steht am Anfang, am
Ende wird wieder auf 1 gesetzt.
Eigenschaften und Grenzen:
mysqldump-Aufruf – das ist auf vielen Shared-Hostern die einzige
Möglichkeit, funktioniert aber nur für MySQL und MariaDB.0x…-Literal geschrieben, damit der Dump gültiges ASCII bleibt.Wiederherstellung von Hand:
unzip -p backup-20260820-092104.zip database/meinprojekt.sql | mysql meinprojekt
MANAGE_BACKUP_COMPRESS (Standard true) schaltet Deflate ein. Der Unterschied
ist bei SQL-Dumps und JSON groß, bei bereits komprimierten Bildern praktisch null.
Fehlt zlib, schreibt der Client unkomprimiert weiter statt abzubrechen.
Grenzen des ZIP-Formats ohne Zip64: 4 GB pro Datei, 4 GB pro Archiv, 65535 Einträge. Wird eine Grenze überschritten, bricht das Backup mit einer klaren Meldung ab.
Standardziel, aktiv über MANAGE_BACKUP_UPLOAD (Standard true). Konfiguriert
wird nichts weiter: Es gelten MANAGE_SERVER_URL, MANAGE_INSTANCE und
MANAGE_TOKEN. Der Server prüft die mitgesendete Prüfsumme erneut und lehnt
abweichende Uploads ab.
Für rein lokale Backups:
define("MANAGE_BACKUP_UPLOAD", false);
Jedes Ziel wird unabhängig versucht. Ein Fehlschlag macht weder das lokale Archiv noch die anderen Ziele ungültig.
S3-kompatibler Speicher:
define("MANAGE_BACKUP_REMOTE_TARGETS", [
[
"name" => "S3 Archiv",
"type" => "s3",
"bucket" => "example-bucket",
"region" => "eu-central-1",
"prefix" => "meinprojekt",
"access_key" => "AKIA…",
"secret_key" => "…",
// Für S3-kompatible Anbieter:
// "endpoint" => "https://fsn1.your-objectstorage.com",
],
]);
Signiert wird mit AWS Signature V4 über normale PHP-HTTPS-Streams; eine Bibliothek
wird nicht benötigt. Das Archiv wird für die Signatur vollständig in den Speicher
geladen – ein Backup größer als memory_limit kann dieses Ziel nicht nutzen.
SFTP (benötigt die PHP-Erweiterung ssh2):
[
"name" => "SFTP Backup",
"type" => "sftp",
"host" => "backup.example.org",
"port" => 22,
"username" => "backup-user",
"password" => "…",
"path" => "/backups/meinprojekt",
]
Mit Schlüssel statt Passwort:
[
"type" => "sftp",
"host" => "backup.example.org",
"username" => "backup-user",
"public_key" => "/pfad/zu/backup.pub",
"private_key" => "/pfad/zu/backup",
"password" => "", // Passphrase des Schlüssels, sonst leer
"path" => "/backups/meinprojekt",
]
Das Zielverzeichnis muss existieren und beschreibbar sein; es wird nicht angelegt.
Eigener Uploader:
[
"name" => "Eigenes Ziel",
"type" => "custom",
"file" => MANAGE_APP_ROOT . "/includes/backup-uploader.php",
"callback" => "myProjectUploadBackup",
]
function myProjectUploadBackup(string $archivePath, array $metadata, array $target)
{
// $metadata: filename, created_at, trigger, sha256, file_count, source_bytes
// Erfolg: true oder ein Array zurückgeben.
// Fehler: Exception werfen oder ["success" => false, "error" => "…"].
return ["remote_path" => "…"];
}
Fehlgeschlagene Uploads werden mit HTTP-Status und Antwortauszug protokolliert.
Zugangsdaten sind davon ausgenommen: Aus der Zielkonfiguration wird nur eine
Positivliste unkritischer Schlüssel übernommen (name, type, url, bucket,
region, prefix, endpoint, host, port, username, path, file,
callback, timeout). access_key, secret_key und password erscheinen nie
im Protokoll.
manageBackupCreate() und Rückgabewerte