Как мы побороли flock
// 23 января 2004 годаДано: файл, в который часто пишется информация и часто из него считывается. Например, простой текстовый счетчик на страницу.
Проблема: рано или поздно, но обязательно случится ситуация, когда одновременно к файлу будет несколько обращений и на запись и на чтение. И вся информация, натурально, потеряется.
Чтобы этого не случилось, существует специальный оператор в PHP flock. Который, по идее, должен с этим бороться сначала мы получаем эксклюзивные права доступа к файлу, пишем туда, что хотим, а потом эти права теряем.
В идеале это работает. Но цитирую документацию по PHP «flock() will not work on NFS and many other networked file systems. Check your operating system documentation for more details». Если почитать все ту же документацию, то в комментариях пользователей приводится множество способов побороть flock, вплоть до самых экзотичных.
Стоит ли говорить, что у меня ни сам flock, ни другие способы просто не работали?
В конце концов, я набрел в одном англоязычном блоге на интересную фразу как раз по этому поводу: «A file rename operation is guaranteed to be atomic by the operating system, and is very efficient as file functions are always highly optimized».
То есть, rename это атомарная, сиречь неделимая функция операционной системы. К тому же она очень эффективная, потому что файловые функции всегда хорошо оптимизированы.
Так что я попробовал побороть flock следующим образом: сначала пишем все, что нам нужно, в временный файл (не забудьте выставить правильный chmod на директорию, в которую он будет писаться), а потом просто переименовываем этот файл в тот файл, который нужно. Причем в
Я написал две функции. Первая функция это функция записи в файл, вторая чтения из файла. Эти две функции интенсивно работают у меня вот уже больше месяца, и пока не было ни одного сбоя.
Запись в файл:
function S_fw ($COUNT_FILE, $text)
{
$z=rand (0,100000);
$fp = @fopen($COUNT_FILE.".".$z, "wb+");
@fwrite($fp,$text);
@fclose($fp);
if (@rename ($COUNT_FILE.".".$z, $COUNT_FILE)==false)
{
@unlink ($COUNT_FILE);
@rename ($COUNT_FILE.".".$z, $COUNT_FILE);
}
}
Чтение из файла (flock здесь стоит «на всякий случай»):
function S_fr ($COUNT_FILE)
{
clearstatcache();
$fp = @fopen($COUNT_FILE, "rb");
@flock ($fp,LOCK_SH);
$conts=@fread ($fp, filesize ($COUNT_FILE));
@fclose ($fp);
@flock($fp, LOCK_UN);
return $conts;
}
Пользуйтесь на здоровье.
PS. Мне тут подсказывают про функцию tempnam. Использовать ее будет более правильно, чем rand.
PSS. Прислали ссылку на еще один способ, как это можно сделать. Я не проверял.