W3docs

flock()

La funzione flock() di PHP implementa un meccanismo di blocco file per prevenire la corruzione dei dati in caso di accesso concorrente.

Cos'è la funzione flock()?

La funzione flock() esegue il blocco dei file in PHP. Un blocco consente a un processo di comunicare agli altri: "Sto lavorando con questo file — aspetta il tuo turno." Senza di esso, due script in esecuzione simultanea possono intrecciare le proprie scritture e corrompere un file. Questa è una classica race condition, e flock() è lo strumento più semplice che PHP offre per prevenirla.

Questa pagina spiega cosa fa la funzione, i tipi di blocco disponibili, un esempio completo e funzionante che puoi eseguire, le insidie più comuni e dove si colloca tra le altre funzioni per file di PHP.

Una nota sul blocco "advisory"

Sui sistemi Unix-like, flock() è advisory: il blocco viene rispettato solo dai processi che anch'essi chiamano flock() sullo stesso file. Un programma che ignora il blocco può comunque leggere o sovrascrivere il file liberamente. Il blocco ti protegge quindi solo se ogni script che accede al file collabora. Su Windows, il blocco è obbligatorio (applicato dal sistema operativo), quindi il comportamento differisce leggermente tra le piattaforme — non fare affidamento sul blocco obbligatorio nel codice portabile.

Sintassi

flock($stream, $operation, &$would_block = null): bool
ParametroDescrizione
$streamUn puntatore al file restituito da fopen().
$operationUna delle costanti di blocco elencate di seguito, con l'opzione di combinare LOCK_NB tramite OR.
$would_blockOpzionale. Impostato a 1 se il blocco avrebbe causato un'attesa (significativo solo con LOCK_NB). Passato per riferimento.

La funzione restituisce true in caso di successo o false in caso di errore.

Tipi di blocco

CostanteSignificato
LOCK_SHBlocco condiviso (lettore). Molti processi possono detenere contemporaneamente un blocco condiviso, ma nessuno può detenere un blocco esclusivo nel frattempo. Usalo in lettura.
LOCK_EXBlocco esclusivo (scrittore). Solo un processo può detenerlo; tutti gli altri — lettori e scrittori — attendono. Usalo in scrittura.
LOCK_UNRilascia il blocco attualmente detenuto sullo stream.
LOCK_NBModificatore non bloccante. Combinalo con LOCK_SH o LOCK_EX tramite OR (ad esempio `LOCK_EX

Per impostazione predefinita, flock() blocca: se un altro processo detiene un blocco esclusivo, la tua chiamata rimane in attesa finché il blocco non è libero. Aggiungi LOCK_NB quando preferisci fallire rapidamente.

Come usare la funzione flock()

Lo schema è sempre lo stesso, con quattro passaggi:

  1. Apri il file con fopen() usando una modalità adatta all'operazione da eseguire ('r+', 'c', 'a', …).
  2. Acquisisci il blocco con flock($file, LOCK_EX) (oppure LOCK_SH per leggere).
  3. Leggi o scrivi il file.
  4. Rilascia con flock($file, LOCK_UN) e chiudi con fclose().

Un esempio completo e funzionante

Questo script apre un file contatore, lo blocca in modo esclusivo, incrementa il numero memorizzato e lo riscrive — il classico pattern leggi-modifica-scrivi che deve essere bloccato per essere sicuro in condizioni di concorrenza:

<?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);

Eseguendolo tre volte, verranno stampati Counter is now: 1, poi 2, poi 3. Poiché la sequenza leggi-incrementa-scrivi avviene sotto LOCK_EX, due processi non potranno mai leggere lo stesso valore e scrivere entrambi 2.

Fallire velocemente con un blocco non bloccante

Quando non vuoi attendere — ad esempio in un cron job che deve saltare l'esecuzione se una precedente è ancora in corso — combina LOCK_EX con 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);

Insidie comuni

  • I blocchi sono associati all'handle del file aperto, non al percorso. Chiamare fopen() due volte sullo stesso file restituisce due handle indipendenti, e un blocco su uno non blocca l'altro nello stesso processo.
  • Usa 'c'/'c+', non 'w', quando usi i blocchi. La modalità 'w' tronca il file nel momento in cui lo apri — prima di acquisire il blocco — vanificandone lo scopo. Tronca esplicitamente con ftruncate() dopo aver acquisito il blocco.
  • flock() non funziona in modo affidabile su NFS o alcuni filesystem di rete/FAT. Per il coordinamento tra più server, usa un servizio di blocco dedicato (un lock su riga di database, Redis, ecc.).
  • fclose() rilascia qualsiasi blocco rimanente, ma rilascia esplicitamente con LOCK_UN in modo che il file sia disponibile non appena hai finito.

Conclusione

flock() è lo strumento integrato di PHP per coordinare l'accesso concorrente a un file. Usa LOCK_EX attorno alle scritture, LOCK_SH attorno alle letture e LOCK_NB quando preferisci fallire piuttosto che attendere. Ricorda che il blocco su Unix è advisory — ti protegge solo quando ogni script che accede al file partecipa. Per operazioni sui file di livello superiore, vedi fwrite(), fread() e file_put_contents().

Esercizio

Pratica
Qual è la funzione di flock() in PHP?
Qual è la funzione di flock() in PHP?
Was this page helpful?