flock()
Die Funktion flock() ist eine eingebaute PHP-Funktion für einfaches Dateisperren, die verhindert, dass mehrere Prozesse eine Datei gleichzeitig beschädigen.
Was ist die Funktion flock()?
Die Funktion flock() führt Dateisperrung in PHP durch. Eine Sperre ermöglicht es einem Prozess, anderen mitzuteilen: „Ich arbeite gerade mit dieser Datei — wartet euren Zug ab." Ohne sie können zwei gleichzeitig laufende Skripte ihre Schreibvorgänge verschränken und eine Datei beschädigen. Dies ist eine klassische Race Condition, und flock() ist das einfachste Werkzeug, das PHP zur Verfügung stellt, um sie zu verhindern.
Diese Seite erklärt, was die Funktion tut, welche Sperrtypen es gibt, ein vollständiges, lauffähiges Beispiel, häufige Fallstricke und wo sie sich unter PHPs anderen Dateifunktionen einordnet.
Ein Wort zu „beratender" Sperrung
Auf Unix-ähnlichen Systemen ist flock() beratend (advisory): Die Sperre wird nur von Prozessen respektiert, die ebenfalls flock() auf derselben Datei aufrufen. Ein Programm, das Sperren ignoriert, kann die Datei weiterhin frei lesen oder überschreiben. Sperrung schützt also nur, wenn alle Skripte, die auf die Datei zugreifen, kooperieren. Unter Windows ist die Sperrung obligatorisch (vom Betriebssystem erzwungen), sodass das Verhalten plattformübergreifend leicht abweicht — verlassen Sie sich in portablem Code nicht auf obligatorische Erzwingung.
Syntax
flock($stream, $operation, &$would_block = null): bool| Parameter | Beschreibung |
|---|---|
$stream | Ein Datei-Zeiger, der von fopen() zurückgegeben wird. |
$operation | Eine der unten stehenden Sperrkonstanten, optional mit LOCK_NB per OR verknüpft. |
$would_block | Optional. Wird auf 1 gesetzt, wenn die Sperre blockiert hätte (nur mit LOCK_NB sinnvoll). Wird als Referenz übergeben. |
Die Funktion gibt bei Erfolg true oder bei Misserfolg false zurück.
Sperrtypen
| Konstante | Bedeutung |
|---|---|
LOCK_SH | Gemeinsame Sperre (Leser). Viele Prozesse können gleichzeitig eine gemeinsame Sperre halten, aber keine exklusive Sperre in der Zwischenzeit. Verwenden Sie sie beim Lesen. |
LOCK_EX | Exklusive Sperre (Schreiber). Nur ein Prozess kann sie halten; alle anderen — Leser und Schreiber — warten. Verwenden Sie sie beim Schreiben. |
LOCK_UN | Gibt die aktuell gehaltene Sperre auf dem Stream frei. |
LOCK_NB | Nicht-blockierender Modifikator. Verknüpfen Sie ihn per OR mit LOCK_SH oder LOCK_EX (z. B. `LOCK_EX |
Standardmäßig blockiert flock(): Hält ein anderer Prozess eine exklusive Sperre, wartet Ihr Aufruf, bis die Sperre frei ist. Fügen Sie LOCK_NB hinzu, wenn Sie lieber sofort scheitern möchten.
Verwendung der Funktion flock()
Das Muster besteht immer aus denselben vier Schritten:
- Öffnen Sie die Datei mit
fopen()in einem Modus, der zu Ihrem Vorhaben passt ('r+','c','a', …). - Erwerben Sie die Sperre mit
flock($file, LOCK_EX)(oderLOCK_SHzum Lesen). - Lesen oder schreiben Sie in die Datei.
- Geben Sie die Sperre frei mit
flock($file, LOCK_UN)und schließen Sie mitfclose().
Ein vollständiges, lauffähiges Beispiel
Dieses Skript öffnet eine Zählerdatei, sperrt sie exklusiv, erhöht die gespeicherte Zahl und schreibt sie zurück — die Art von Lesen-Ändern-Schreiben-Vorgang, der zwingend gesperrt werden muss, um unter gleichzeitigem Zugriff sicher zu sein:
<?php
$filename = 'counter.txt';
// 'c' opens for read/write, creating the file if missing,
// and does NOT truncate it (unlike 'w').
$file = fopen($filename, 'c+');
if ($file === false) {
exit("Could not open file.\n");
}
if (flock($file, LOCK_EX)) { // block until we hold the lock
$current = (int) stream_get_contents($file);
$current++;
rewind($file); // back to the start
ftruncate($file, 0); // clear old contents
fwrite($file, (string) $current);
fflush($file); // push to disk before unlocking
flock($file, LOCK_UN); // release the lock
echo "Counter is now: $current\n";
} else {
echo "Could not acquire lock.\n";
}
fclose($file);Dreimaliges Ausführen gibt Counter is now: 1, dann 2, dann 3 aus. Da das Lesen-Inkrementieren-Schreiben unter LOCK_EX stattfindet, können zwei Prozesse nie denselben Wert lesen und beide 2 schreiben.
Sofortiges Scheitern mit nicht-blockierender Sperre
Wenn Sie nicht warten möchten — etwa bei einem Cron-Job, der den Lauf überspringen soll, wenn ein vorheriger noch läuft — kombinieren Sie LOCK_EX mit LOCK_NB:
<?php
$file = fopen('job.lock', 'c');
if (flock($file, LOCK_EX | LOCK_NB)) {
echo "Got the lock, doing work...\n";
// ... long-running task ...
flock($file, LOCK_UN);
} else {
echo "Another instance is already running. Exiting.\n";
}
fclose($file);Häufige Fallstricke
- Sperren hängen am offenen Datei-Handle, nicht am Pfad. Zweimaliges Aufrufen von
fopen()auf derselben Datei liefert zwei unabhängige Handles, und eine Sperre auf einem blockiert das andere nicht im selben Prozess. - Verwenden Sie
'c'/'c+', nicht'w', beim Sperren. Der Modus'w'kürzt die Datei in dem Moment, in dem Sie sie öffnen — bevor Sie die Sperre erworben haben — und untergräbt damit den Zweck. Kürzen Sie mitftruncate()explizit, nachdem Sie die Sperre halten. flock()funktioniert über NFS oder bestimmten vernetzten/FAT-Dateisystemen nicht zuverlässig. Für die serverübergreifende Koordination verwenden Sie stattdessen einen echten Sperrdienst (eine Datenbankzeilensperre, Redis usw.).fclose()gibt alle verbleibenden Sperren frei, aber entsperren Sie explizit mitLOCK_UN, damit die Datei sofort wieder verfügbar ist, sobald Sie fertig sind.
Fazit
flock() ist PHPs eingebautes Werkzeug zur Koordination des gleichzeitigen Zugriffs auf eine Datei. Verwenden Sie LOCK_EX rund um Schreibvorgänge, LOCK_SH rund um Lesevorgänge und LOCK_NB, wenn Sie lieber scheitern als warten möchten. Beachten Sie, dass das Sperren unter Unix beratend ist — es schützt Sie nur, wenn alle Skripte, die auf die Datei zugreifen, mitmachen. Für übergeordnete Dateiarbeiten siehe fwrite(), fread() und file_put_contents().