Dateien kopieren und verschieben gehört zu den alltäglichen Aufgaben in fast jedem PHP-Projekt. PHP stellt dafür zwei schlanke Funktionen bereit, die sich auf den ersten Blick ähneln, im Detail aber unterschiedlich arbeiten. Dieses Tutorial zeigt, wann copy() die richtige Wahl ist, wann rename() besser passt und wie du beide in robusten Backup- und Deployment-Skripten einsetzt.
Was leistet PHP copy() im Vergleich zu rename()?
Wer in PHP mit Dateien arbeitet, kommt früher oder später an den Punkt, an dem eine Datei dupliziert oder in ein anderes Verzeichnis bewegt werden soll. Genau dafür gibt es PHP copy und rename(). Die Funktion copy() legt eine Kopie an, der Originalname bleibt erhalten. Die Funktion rename() benennt um oder verschiebt, am Ende existiert nur noch ein Treffer am neuen Pfad.

Bevor Syntax und Backup-Pattern folgen, lohnt sich ein kurzer Blick darauf, in welchen Situationen man beide Funktionen im Alltag überhaupt braucht.
Im Alltag braucht man beide. Backup-Skripte erstellen Kopien, Deployments verschieben fertige Artefakte in ihr Zielverzeichnis, ein Bild-Upload landet zuerst im temporären Bereich und wandert später in den dauerhaften Speicher. Dieses Tutorial zeigt die Syntax, die Stolperfallen und ein praxisnahes Backup-Pattern, das in vielen kleinen PHP-Projekten direkt einsetzbar ist.
Syntax und Rückgabewert von PHP copy()
Die Signatur ist erfreulich kurz. copy() erwartet einen Quell- und einen Zielpfad und gibt einen Boolean zurück. Anders als beim Duplizieren eines Arrays im Speicher werden hier echte Bytes auf das Dateisystem geschrieben.
<?php
copy(string $from, string $to, ?resource $context = null): bool
Bei Erfolg liefert die Funktion true, im Fehlerfall false. Eine Exception wirft copy() nicht. Stattdessen wird ein Warning ausgegeben, das in produktiven Setups oft per error_reporting() ausgeblendet ist. Wer mit der @-Unterdrückung arbeitet, verliert auch diesen Hinweis und tappt im Dunkeln, sobald etwas schiefgeht. Der saubere Weg ist die explizite Prüfung des Rückgabewerts und ein Eintrag im Log.
<?php
$quelle = 'config.example.php';
$ziel = 'config.php';
if (copy($quelle, $ziel)) {
echo 'Datei erfolgreich kopiert.';
} else {
error_log("Konnte $quelle nicht nach $ziel kopieren");
}
Eine Datei mit PHP copy sicher duplizieren
Im Alltag passiert oft genau dieser Fall: Eine Vorlagedatei soll als Arbeitsdatei dupliziert werden. Wichtig ist, dass copy() ein vorhandenes Ziel ohne Rückfrage überschreibt. Wer das verhindern will, prüft zuerst mit file_exists() ob die Datei existiert.
<?php
$ziel = 'config.php';
if (file_exists($ziel)) {
echo 'Achtung, die Zieldatei existiert bereits.';
/* Hier koennte ein Bestaetigen, Skip oder Backup folgen */
} else {
copy('config.example.php', $ziel);
}
Ein zweiter Punkt sind Berechtigungen. Das Zielverzeichnis muss schreibbar sein, sonst gibt copy() false zurück. Eine kurze Vorab-Prüfung mit is_writable() spart spätere Fehlersuche.
Mit rename() Dateien verschieben oder umbenennen statt PHP copy()
rename() arbeitet anders als PHP copy(). Auf derselben Partition aktualisiert das Dateisystem nur den Inode-Eintrag, die Bytes werden nicht angefasst. Das macht die Funktion auch für mehrere GB große Dateien praktisch instant. Der Aufruf sieht ähnlich aus.
<?php
/* Im selben Ordner umbenennen */
rename('alt.txt', 'neu.txt');
/* In einen Unterordner verschieben */
rename('uploads/temp.jpg', 'uploads/processed/temp.jpg');
Auch rename() überschreibt das Ziel ohne Warnung, wenn es schon existiert. In Skripten, die über Mount-Grenzen hinweg verschieben (etwa zwischen /var/www und /mnt/storage), kann der Aufruf scheitern. Das ist kein PHP-Fehler, sondern eine Eigenheit des Betriebssystems. Für diesen Fall lohnt sich ein kleiner Helper.
<?php
function verschiebeDatei(string $von, string $nach): bool
{
if (@rename($von, $nach)) {
return true;
}
/* Fallback fuer Mount-Grenzen */
if (copy($von, $nach)) {
return unlink($von);
}
return false;
}
Ein Backup-Skript mit PHP copy und glob
Der Klassiker unter den Anwendungsfällen ist ein einfaches Backup. Alle JPG-Bilder eines Galerie-Ordners werden in ein datiertes Backup-Verzeichnis kopiert. Mit glob() lassen sich Dateien nach Muster auflisten, die eigentliche Sicherung übernimmt copy() in einer Schleife.
<?php
$quellOrdner = __DIR__ . '/galerie';
$backupRoot = __DIR__ . '/backups/' . date('Y-m-d');
if (!is_dir($backupRoot)) {
mkdir($backupRoot, 0755, true);
}
$kopiert = 0;
foreach (glob($quellOrdner . '/*.jpg') as $datei) {
$ziel = $backupRoot . '/' . basename($datei);
if (copy($datei, $ziel)) {
$kopiert++;
} else {
error_log('Backup fehlgeschlagen: ' . $datei);
}
}
echo $kopiert . ' Dateien gesichert.';
Dieses kurze Skript reicht für Vereinsseiten, kleine Portfolios oder Wartungsskripte. Wer ganze Verzeichnisbäume sichern will, ersetzt glob() durch einen RecursiveDirectoryIterator und legt Zwischen-Verzeichnisse rekursiv mit mkdir() an.
Atomares Verschieben mit rename() statt PHP copy()
In Deployment-Skripten oder Cache-Updates gibt es ein bewährtes Pattern, das ohne PHP copy() auskommt: Erst die neue Datei vollständig in eine .tmp-Datei schreiben, dann mit rename() das alte Ziel überschreiben. Auf POSIX-Systemen ist rename() auf gleicher Partition garantiert atomar. Es gibt keinen Moment, in dem die Zieldatei halb beschrieben sichtbar wäre.
<?php
$ziel = 'cache/data.json';
$tmp = $ziel . '.tmp';
file_put_contents($tmp, json_encode($daten));
/* Erst hier wird das Ziel komplett ersetzt */
rename($tmp, $ziel);
Wer die Datei direkt überschreibt, riskiert, dass ein paralleler Webserver-Request währenddessen zugreift und einen halben Inhalt liest. Mit dem temp-plus-rename-Pattern ist das Problem ohne weitere Locks gelöst. Genau dieser Mechanismus steckt unter der Haube vieler Cache-Bibliotheken und Skripte, die Daten in JSON oder PHP-Format ablegen.
flowchart TD
A[Quelldatei vorhanden] --> B{copy oder rename?}
B -->|copy| C[Bytes neu schreiben]
B -->|rename| D{gleiche Partition?}
D -->|Ja| E[Inode-Update, atomar]
D -->|Nein| F[Fehler, Fallback noetig]
C --> G[Quelle bleibt erhalten]
E --> H[Quelle ist weg]
PHP copy und rename vs move_uploaded_file()
Im Code größerer Projekte tauchen drei Wege auf, eine Datei zu bewegen. copy() legt ein Duplikat an. rename() verschiebt oder benennt um. move_uploaded_file() ist eine Spezialvariante für Upload-Workflows. Der entscheidende Unterschied: move_uploaded_file() prüft über is_uploaded_file(), ob die Quelle wirklich aus einem HTTP-Upload stammt. Damit verhindert die Funktion, dass Angreifer beliebige Pfade aus dem Server-Filesystem als Quelle übergeben.
Eine grobe Faustregel: Solange die Datei aus $_FILES stammt, sollte immer move_uploaded_file() zum Einsatz kommen. Für alle anderen Verschiebe- und Kopier-Operationen reichen copy() und rename(). Wer in einem CMS Vorlagen-Dateien dupliziert, in einem CLI-Skript Build-Artefakte verschiebt oder in einem Backup-Skript Bilder sichert, ist mit den klassischen Funktionen besser bedient, weil sie keine Upload-Prüfung erwarten.
Stolperfallen und Best Practices für PHP copy()
Ein paar Punkte tauchen in Code-Reviews zu PHP copy() immer wieder auf. Erstens: @-Unterdrückung versteckt Hinweise, die im Fehlerfall wertvoll wären. Stattdessen den Rückgabewert prüfen und im Log notieren, was schief lief. Zweitens: Beide Funktionen überschreiben das Ziel ohne Warnung. Wer das nicht will, prüft zuvor mit file_exists() und entscheidet bewusst.
Drittens: Bei sehr großen Dateien blockiert copy() den Skriptlauf merklich, weil alle Bytes wandern. Wer Performance braucht und ohnehin innerhalb derselben Partition arbeitet, sollte rename() bevorzugen. Viertens: Berechtigungen sind die häufigste Ursache für stille Fehler. Vor dem Aufruf einmal mit is_writable() prüfen, ob das Zielverzeichnis überhaupt beschreibbar ist, und im Fehlerfall einen klaren Log-Eintrag erzeugen. Fünftens: Auf Windows verhält sich rename() etwas restriktiver als auf Linux. Eine Datei, die noch von einem Prozess geöffnet ist, lässt sich dort nicht umbenennen, was unter Linux problemlos funktioniert.
Wer all diese Punkte beachtet, hat mit PHP copy und rename() ein robustes Werkzeug für alle gängigen Datei-Operationen. Beim Kopieren einer Datei wird jedes Byte vollständig in das Ziel geschrieben, ein implizites Sharing wie bei Arrays gibt es nicht. Die Funktionen sind seit PHP 4 stabil, brauchen keine Erweiterung und funktionieren auf jedem Hosting-Setup ohne zusätzliche Konfiguration.
Fazit zu PHP copy() und rename()
PHP copy() und rename() gehören zum Pflichtwerkzeug jedes PHP-Entwicklers. PHP copy legt eine Kopie an und lässt die Quelle in Ruhe, rename() verschiebt oder benennt um und ist auf gleicher Partition praktisch instant. Wer den Rückgabewert prüft, vorhandene Ziele bewusst behandelt und das atomare temp-plus-rename-Pattern nutzt, baut auch produktive Backup- und Deployment-Skripte zuverlässig. Für Upload-Workflows bleibt move_uploaded_file() die richtige Wahl, für alles andere reichen die beiden Klassiker copy() und rename() vollkommen aus.