W3docs

flock()

Функция flock() — встроенная функция PHP для блокировки файлов. Предотвращает одновременный доступ нескольких процессов и состояния гонки.

Что такое функция flock()?

Функция flock() выполняет блокировку файлов в PHP. Блокировка позволяет одному процессу сообщить остальным: «Я работаю с этим файлом — подождите своей очереди». Без неё два скрипта, выполняющихся одновременно, могут перемежать свои операции записи и повредить файл. Это классическое состояние гонки, и flock() — простейший инструмент PHP для его предотвращения.

На этой странице описаны назначение функции, типы блокировок, полный рабочий пример, распространённые подводные камни и место flock() среди других файловых функций PHP.

О «рекомендательной» блокировке

В Unix-подобных системах flock() является рекомендательной: блокировка соблюдается только теми процессами, которые также вызывают flock() для того же файла. Программа, игнорирующая блокировки, по-прежнему может свободно читать файл или перезаписывать его. Таким образом, блокировка защищает только в том случае, если все скрипты, работающие с файлом, её соблюдают. В Windows блокировка является обязательной (принудительно применяется ОС), поэтому поведение немного различается на разных платформах — не полагайтесь на принудительное применение в переносимом коде.

Синтаксис

flock($stream, $operation, &$would_block = null): bool
ПараметрОписание
$streamУказатель на файл, возвращённый функцией fopen().
$operationОдна из констант блокировки ниже, при необходимости объединённая с LOCK_NB через OR.
$would_blockНеобязательный. Устанавливается в 1, если блокировка вызвала бы ожидание (имеет смысл только с LOCK_NB). Передаётся по ссылке.

Функция возвращает true при успехе или false при ошибке.

Типы блокировок

КонстантаЗначение
LOCK_SHРазделяемая блокировка (читатель). Несколько процессов могут удерживать разделяемую блокировку одновременно, но ни один из них не может удерживать эксклюзивную. Используйте при чтении.
LOCK_EXЭксклюзивная блокировка (писатель). Только один процесс может её удерживать; все остальные — читатели и писатели — ждут. Используйте при записи.
LOCK_UNСнять блокировку, удерживаемую на потоке.
LOCK_NBМодификатор неблокирующего режима. Объедините его с LOCK_SH или LOCK_EX (например, `LOCK_EX

По умолчанию flock() блокирует: если другой процесс удерживает эксклюзивную блокировку, ваш вызов ждёт, пока блокировка не освободится. Добавьте LOCK_NB, если предпочитаете немедленный отказ вместо ожидания.

Как использовать функцию flock()

Паттерн всегда состоит из одних и тех же четырёх шагов:

  1. Откройте файл с помощью fopen(), используя режим, соответствующий выполняемой операции ('r+', 'c', 'a', …).
  2. Установите блокировку с помощью flock($file, LOCK_EX) (или LOCK_SH для чтения).
  3. Прочитайте или запишите файл.
  4. Снимите блокировку с помощью flock($file, LOCK_UN) и закройте файл с помощью fclose().

Полный рабочий пример

Этот скрипт открывает файл-счётчик, устанавливает эксклюзивную блокировку, увеличивает хранящееся число и записывает его обратно — это классическая операция чтение-изменение-запись, которая обязательно должна выполняться под блокировкой для безопасности при конкурентном доступе:

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

При трёхкратном запуске выводится Counter is now: 1, затем 2, затем 3. Поскольку операция чтение-увеличение-запись выполняется под LOCK_EX, два процесса никогда не смогут прочитать одно и то же значение и оба записать 2.

Немедленный отказ с неблокирующей блокировкой

Когда ожидание нежелательно — например, для задания cron, которое должно пропустить выполнение, если предыдущее ещё не завершилось — объедините LOCK_EX с 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);

Распространённые подводные камни

  • Блокировки привязаны к открытому дескриптору файла, а не к пути. Двукратный вызов fopen() для одного и того же файла даёт два независимых дескриптора, и блокировка одного не блокирует другой в том же процессе.
  • Используйте 'c'/'c+', а не 'w', при блокировке. Режим 'w' усекает файл в момент открытия — до получения блокировки — что сводит на нет всю цель. Выполняйте усечение явно с помощью ftruncate() уже после получения блокировки.
  • flock() ненадёжно работает через NFS и некоторые сетевые файловые системы или FAT. Для координации между несколькими серверами используйте настоящий сервис блокировок (блокировка строки в базе данных, Redis и т. д.).
  • fclose() снимает все оставшиеся блокировки, но лучше явно снять блокировку с помощью LOCK_UN, чтобы файл стал доступен сразу после завершения работы с ним.

Заключение

flock() — встроенный инструмент PHP для управления конкурентным доступом к файлу. Используйте LOCK_EX при записи, LOCK_SH при чтении и LOCK_NB, если предпочитаете немедленный отказ вместо ожидания. Помните, что блокировка в Unix носит рекомендательный характер — она защищает только тогда, когда все скрипты, работающие с файлом, её соблюдают. Для более широких файловых операций смотрите fwrite(), fread() и file_put_contents().

Практика

Практика
Какова функция flock() в PHP?
Какова функция flock() в PHP?
Was this page helpful?