It is possible that flock returns false with LOCK_EX?
But is there any case where the script would actually return «Couldn’t get the lock!» ? I thought it waits until the file lock.txt gets unlocked. If the file never gets unlocked, then the script waits forever, right? Further, I found this answer explaining the difference between exclusive and shared lock on unix: https://stackoverflow.com/a/11837714/2311074 Do these 4 rules also apply to flock in PHP (for example «If one or more shared locks already exist, exclusive locks cannot be obtained»)?
I could imagine that there are other situations where the function might return FALSE, for example if the handle is invalid or the file system does not support locking or similar.
3 Answers 3
Yes, blocking flock can return false .
It will happen if provided resource don’t support locking
Samples are even provided in documentation
May only be used on file pointers returned by fopen() for local files, or file pointers pointing to userspace streams that implement the streamWrapper::stream_lock() method.
So if you try to lock ftp or http resource you will get false , same will go for some wrappers e.g. zlib or phar .
flock() is not supported on antiquated filesystems like FAT and its derivates and will therefore always return FALSE under these environments.
Yes I found that when building flintstone it will not flock a compression streamed file.
$file = 'compress.zlib://path/to/file.txt'; $fp = fopen($file, 'w'); var_dump(flock($fp, LOCK_EX)); // false
On SunOS, if you opened the file in read mode, flock with LOCK_EX will always return false, even if the file is not locked. There is a comment to this effect on the PHP Manual flock page. The comment is down-voted to -2, but it is correct and bears repeating here:
flock on Solaris is slightly strange: it will fail if you try to get an exclusive lock on a file not opened for writing. That is, for reading files, you MUST use a shared lock. From the Solaris man page for flock: «Read permission is required on a file to obtain a shared lock, and write permission is required to obtain an exclusive lock.» In contrast, this is from the Linux man page for flock: «The mode used to open the file doesn’t matter to flock.» So, beware.
Many flock examples show the file being opened in read mode, but I can confirm the above comment. If you use flock with LOCK_EX, it will return return false on SunOS if you opened the file in read mode. Since Linux doesn’t care which mode you use, it seems to me that one should always open the file in write mode when using flock with LOCK_EX.
Php flock lock ex
Если наш веб-сайт посещает множество людей, которые одновременно обращаются к одному и тому же файлу, то мы можем столкнуться с некоторыми проблемами. В частности, при одновременной попытке записи несколькими пользователями файл может быть поврежден, либо выдать неожиданные результаты, если один человек считывает файл, а другой одновременно записывает его. Возникает проблема синхронизации доступа пользователей.
Чтобы ограничить доступ к файлу, в PHP используется функция flock() . Эта функция блокирует доступ к файлу, когда он уже занят одним пользователем, а все остальные запросы ставит в очередь. При освобождении файла он разблокируется, передается первому в очереди пользователю и снова блокируется.
Функция имеет следующее определение:
bool flock (resource $handle , int $operation [, int &$wouldblock ])
Первый параметр — дескриптор файла, возвращаемые функцией fopen() .
Второй параметр указывает на тип блокировки. Он может принимать следующие значения:
- LOCK_SH (или число 1): разделяемая блокировка (чтение файла)
- LOCK_EX (или число 2): исключительная блокировка (запись файла)
- LOCK_UN (или число 3): для снятия блокировки
- LOCK_NB (или число 4): эта константа используется только вместе с одной из предыдущих в битовой маске (LOCK_EX | LOCK_NB), если не надо ждать пока flock() получит блокировку
Третий необязательный параметр $wouldblock будет содержать true , если блокировка будет блокирующей.
При успешном выполнении функция flock возвратит значение true , а в случае ошибки — false .
Используем flock для ограничения доступа к файлу:
При изменении файла блокировка ставится непосредственно перед внесением изменений, а снимается сразу после их внесения. Иначе программа может замедлить свою работу. Поэтому вызов, блокирующий файл: flock($fd, LOCK_EX) поставлен непосредственно перед вызовом функции fwrite($fd, «$str») . А снятие блокировки с помощью константы LOCK_UN идет сразу после записи.
Обратите внимание, что при открытии файла здесь использовался режим ‘r+’, а не ‘w’ или ‘w+’, так как ‘w’ и ‘w+’ уже подразумевают изменение файла. Поэтому при блокировке, даже если надо делать запись, не рекомендуется использование ‘w’ и ‘w+’.
Если нам надо стереть все содержимое файла и перезаписать файл по-новому, то мы можем воспользоваться функцией ftruncate :
flock
flock() позволяет осуществить простую модель чтения/записи, которая может быть использована практически на любой платформе (включая большинство вариантов Unix и даже Windows).
В версиях PHP до 5.3.2 блокировка освобождалась также вызовом функции fclose() (которая также вызывается автоматически по завершении скрипта).
PHP поддерживает портируемый способ консультативной блокировки (adviosory locking) полностью всего файла (что означает, что все программы, осуществляющие доступ к файлу, должны использовать один и тот же способ блокировки, иначе блокировка не будет работать). По умолчанию, данная функция будет ждать получения блокировки; это поведение можно изменить с помощью описанного ниже параметра LOCK_NB .
Список параметров
Указатель ( resource ) на файл, обычно создаваемый с помощью функции fopen() .
- LOCK_SH для получения разделяемой блокировки (чтение).
- LOCK_EX для получения эксклюзивной блокировки (запись).
- LOCK_UN для снятия блокировки (разделяемой или эксклюзивной).
Также возможно добавить константу LOCK_NB в качестве битовой маски к любой из вышеуказанных операций, если вы не хотите ждать пока flock() получит блокировку.
Необязательный третий параметр будет установлен в 1, если блокировка будет блокирующей (код ошибки EWOULDBLOCK). (не поддерживается на Windows)
Возвращаемые значения
Возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.
Список изменений
Версия | Описание |
---|---|
5.3.2 | Автоматическое снятие блокировки при закрытии было удалено. Снятие блокировки теперь всегда должно осуществляться вручную. |
Примеры
Пример #1 Пример использования функции flock()
if ( flock ( $fp , LOCK_EX )) < // выполняем эксклюзивную блокировку
ftruncate ( $fp , 0 ); // очищаем файл
fwrite ( $fp , «Что-нибудь пишем сюда\n» );
fflush ( $fp ); // очищаем вывод перед отменой блокировки
flock ( $fp , LOCK_UN ); // отпираем файл
> else echo «Не удалось получить блокировку !» ;
>
Пример #2 Использование flock() с параметром LOCK_NB
/* Включаем параметр LOCK_NB в операции LOCK_EX */
if(! flock ( $fp , LOCK_EX | LOCK_NB )) echo ‘Не удалось получить блокировку’ ;
exit(- 1 );
>
Примечания
Замечание:
В Windows flock() использует обязательную (mandatory) блокировку вместо консультативной. Обязательная блокировка также поддерживается на Linux и операционных системах, основанных на System V с помощью стандартного механизма, который предоставляет системный вызов fcntl(): т.е. искомый файл должен иметь установленный бит доступа setgid и неустановленный бит группового выполнения. Для корректной работы этой схемы в Linux, файловая система также должна быть смонтирована с опцией mand.
Замечание:
Из-за того, что функции flock() необходим указатель на файл, вам может понадобиться воспользоваться специальным запирающим файлом для того, чтобы ограничить доступ к файлу, который вы намерены очищать, путём его открытия в режиме записи (используя «w» или «w+» в качестве аргумента функции fopen() ).
Замечание:
Может быть использована только на дескрипторах локальных файлов, возвращенных функцией fopen() , или файловых дескрипторах пользовательских потоков, реализующих метод streamWrapper::stream_lock() .
Присвоение другого значения аргументу handle в последующем коде отменит существующую блокировку.
В некоторых операционных системах flock() реализован на уровне процессов. При использовании многопоточных серверных API, таких как ISAPI, вы не можете полагаться на flock() для защиты ваших файлов от других PHP-скриптов, которые работают в параллельном потоке на том же сервере!
flock() не поддерживается на старых файловых системах вроде FAT и ее производных, так что всегда будет возвращать FALSE в этом окружении (это особенно касается пользователей Windows 98).
SplFileObject::flock
Блокирует или разблокирует файл тем же портируемым способом, что и flock() .
Список параметров
- LOCK_SH для получения разделяемой блокировки (чтение).
- LOCK_EX для получения эксклюзивной блокировки (запись).
- LOCK_UN для снятия блокировки (разделяемой или эксклюзивной).
Также возможно добавить LOCK_NB в качестве битовой маски к одной из вышеуказанных операций, если flock() не должен блокироваться во время попытки блокировки.
Будет установлен true , если блокировка будет блокирующей (код ошибки EWOULDBLOCK).
Возвращаемые значения
Возвращает true в случае успешного выполнения или false в случае возникновения ошибки.
Примеры
Пример #1 Пример использования SplFileObject::flock()
$file = new SplFileObject ( «/tmp/lock.txt» , «w» );
if ( $file -> flock ( LOCK_EX )) < // выполняем эксклюзивную блокировку
$file -> ftruncate ( 0 ); // очищаем файл
$file -> fwrite ( «Что-нибудь пишем сюда\n» );
$file -> flock ( LOCK_UN ); // снимаем блокировку
> else echo «Не удалось получить блокировку!» ;
>
?>?php
Смотрите также
User Contributed Notes 2 notes
For the record, the example given here has an explicit command to truncate the file, however with a ‘write mode’ of ‘w’, it will do this for you automatically, so the truncate call is not needed.
@digitalprecision What you said is not completely true, ftruncate(0); is needed if there was a write to the file before the lock is acquired. You also may need fseek(0); to move back the file pointer to the beginning of the file
$file = new SplFileObject ( «/tmp/lock.txt» , «w» );
$file -> fwrite ( «xxxxx» ); // write something before the lock is acquired
sleep ( 5 ); // wait for 5 seconds
if ( $file -> flock ( LOCK_EX )) < // do an exclusive lock
$file -> fwrite ( «Write something here\n» );
$file -> flock ( LOCK_UN ); // release the lock
> else echo «Couldn’t get the lock!» ;
>
?>
«lock.txt» content:
xxxxxWrite something here