Nette SafeStream
Nette SafeStream гарантирует, что каждая операция чтения и записи файла происходит изолированно. Это значит, что ни один поток не начнёт читать файл, который ещё не дописан до конца, и что несколько потоков не перезапишут один и тот же файл.
Установка:
composer require nette/safe-stream
Для чего это нужно?
Для чего вообще нужны изолированные операции? Начнём с простого примера, который многократно записывает в файл, а затем читает из него ту же строку:
$s = str_repeat('Long String', 10000);
$counter = 1000;
while ($counter--) {
file_put_contents('file', $s); // записываем
$readed = file_get_contents('file'); // читаем
if ($s !== $readed) { // проверяем
echo 'strings are different!';
}
}
Может показаться, что вызов echo 'strings are different!' никогда не
произойдёт. Всё наоборот. Попробуйте запустить этот скрипт
одновременно в двух вкладках браузера. Ошибка появится почти сразу.
Одна из вкладок прочитает файл в момент, когда другая ещё не дописала его целиком, так что содержимое будет неполным.
Поэтому код небезопасен, если выполняется несколько раз одновременно (то есть в нескольких потоках). В интернете это не редкость, потому что серверы часто отвечают большому количеству пользователей одновременно. Обеспечить, чтобы ваше приложение надёжно работало и при выполнении в нескольких потоках (было потокобезопасным), принципиально важно. Иначе может произойти потеря данных и возникнут труднообнаружимые ошибки.
Однако, как видите, нативные функции PHP для чтения и записи файлов не изолированы и не атомарны.
Как использовать SafeStream?
SafeStream создаёт безопасный протокол, через который файлы можно читать
и записывать изолированно обычными функциями PHP. Достаточно снабдить
имя файла приставкой nette.safe://:
file_put_contents('nette.safe://file', $s);
$s = file_get_contents('nette.safe://file');
SafeStream обеспечивает, что записывать в файл одновременно может не более одного потока. Остальные потоки ждут в очереди. Если ни один поток не пишет, читать файл параллельно может сколько угодно потоков.
С протоколом можно использовать все обычные функции PHP, например:
// 'r' означает открыть только для чтения
$handle = fopen('nette.safe://file.txt', 'r');
$ini = parse_ini_file('nette.safe://config.ini');
Ограничения
SafeStream изолирует чтение и запись содержимого файлов, но не может сделать атомарной каждую операцию. Помните об этих границах:
- Сведения о файле не изолированы. Функции, которые только
запрашивают метаданные, например
file_exists(),filesize()илиis_file(), в блокировке не участвуют. Они могут вернуть сведения о файле, который другой поток как раз записывает. - Удаление открытого файла в Windows. В отличие от Unix, Windows не позволяет
удалить файл, который другой поток в данный момент держит открытым, так
что
unlink('nette.safe://file')может дать сбой. - Автоматический откат незавершённой записи. Если запись прерывается на середине (например, кончается место на диске), то при закрытии файла SafeStream обрезает его обратно до размера, который был до начала записи, так что частично записанных данных он никогда не оставляет.
Если вы переходите на более новую версию, посмотрите страницу обновления.