flock()
La función flock() de PHP permite implementar un mecanismo sencillo de bloqueo de archivos para evitar condiciones de carrera entre procesos.
¿Qué es la función flock()?
La función flock() realiza el bloqueo de archivos en PHP. Un bloqueo permite que un proceso le indique a los demás: "Estoy trabajando con este archivo — espera tu turno". Sin él, dos scripts que se ejecuten al mismo tiempo pueden entrelazar sus escrituras y corromper un archivo. Esto es una clásica condición de carrera, y flock() es la herramienta más sencilla que PHP ofrece para prevenirla.
Esta página explica qué hace la función, sus tipos de bloqueo, un ejemplo completo y funcional que puedes ejecutar, los errores comunes que suelen cometerse, y dónde encaja entre las demás funciones de archivo de PHP.
Una nota sobre el bloqueo "consultivo"
En sistemas tipo Unix, flock() es consultivo (advisory): el bloqueo solo se respeta entre procesos que también llaman a flock() sobre el mismo archivo. Un programa que ignora el bloqueo puede seguir leyendo o sobreescribiendo el archivo libremente. Por tanto, el bloqueo solo te protege si todos los scripts que acceden al archivo cooperan. En Windows, el bloqueo es obligatorio (lo impone el sistema operativo), por lo que el comportamiento difiere ligeramente entre plataformas — no confíes en el cumplimiento obligatorio en código portable.
Sintaxis
flock($stream, $operation, &$would_block = null): bool| Parámetro | Descripción |
|---|---|
$stream | Un puntero de archivo devuelto por fopen(). |
$operation | Una de las constantes de bloqueo que se muestran a continuación, opcionalmente combinada con LOCK_NB mediante OR. |
$would_block | Opcional. Se establece en 1 si el bloqueo habría quedado a la espera (solo tiene sentido con LOCK_NB). Se pasa por referencia. |
La función devuelve true en caso de éxito o false en caso de error.
Tipos de bloqueo
| Constante | Significado |
|---|---|
LOCK_SH | Bloqueo compartido (lectura). Varios procesos pueden mantener un bloqueo compartido al mismo tiempo, pero ninguno puede tener uno exclusivo mientras tanto. Úsalo al leer. |
LOCK_EX | Bloqueo exclusivo (escritura). Solo un proceso puede mantenerlo; todos los demás — lectores y escritores — esperan. Úsalo al escribir. |
LOCK_UN | Libera el bloqueo que se tiene actualmente sobre el stream. |
LOCK_NB | Modificador no bloqueante. Combínalo con LOCK_SH o LOCK_EX (p. ej., `LOCK_EX |
Por defecto, flock() bloquea: si otro proceso tiene un bloqueo exclusivo, tu llamada espera hasta que el bloqueo quede libre. Añade LOCK_NB cuando prefieras fallar rápido.
Cómo usar la función flock()
El patrón siempre consta de los mismos cuatro pasos:
- Abre el archivo con
fopen()usando un modo adecuado para lo que vayas a hacer ('r+','c','a', …). - Adquiere el bloqueo con
flock($file, LOCK_EX)(oLOCK_SHpara leer). - Lee o escribe el archivo.
- Libera con
flock($file, LOCK_UN)y cierra confclose().
Un ejemplo completo y ejecutable
Este script abre un archivo de contador, lo bloquea en modo exclusivo, incrementa el número almacenado y lo vuelve a escribir — el tipo de lectura-modificación-escritura que debe estar bloqueado para ser seguro bajo concurrencia:
<?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);Si se ejecuta tres veces, imprime Counter is now: 1, luego 2, luego 3. Dado que la lectura-incremento-escritura ocurre bajo LOCK_EX, dos procesos nunca pueden leer el mismo valor y ambos escribir 2.
Fallar rápido con un bloqueo no bloqueante
Cuando no quieres esperar — por ejemplo, un cron job que debería saltarse la ejecución si el anterior aún está en marcha — 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);Errores comunes
- Los bloqueos se asocian al identificador de archivo abierto, no a la ruta. Llamar a
fopen()dos veces sobre el mismo archivo da lugar a dos identificadores independientes, y un bloqueo en uno no bloquea al otro dentro del mismo proceso. - Usa
'c'/'c+', no'w', al bloquear. El modo'w'trunca el archivo en el momento en que lo abres — antes de adquirir el bloqueo —, lo que anula el propósito. Trunca explícitamente conftruncate()después de obtener el bloqueo. flock()no funciona de forma fiable sobre NFS ni sobre algunos sistemas de archivos en red o FAT. Para la coordinación entre servidores, usa un servicio de bloqueo real (un bloqueo de fila en base de datos, Redis, etc.).fclose()libera cualquier bloqueo pendiente, pero libera explícitamente conLOCK_UNpara que el archivo vuelva a estar disponible tan pronto como hayas terminado.
Conclusión
flock() es la herramienta integrada de PHP para coordinar el acceso concurrente a un archivo. Usa LOCK_EX alrededor de las escrituras, LOCK_SH alrededor de las lecturas, y LOCK_NB cuando prefieras fallar antes que esperar. Recuerda que el bloqueo en Unix es consultivo — solo te protege cuando todos los scripts que acceden al archivo participan. Para trabajar con archivos a un nivel más alto, consulta fwrite(), fread() y file_put_contents().