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 'строки отличаются!';
}
}
Может показаться, что вызов echo 'строки отличаются!'
никогда не
может произойти. На самом деле это не так. Попробуйте запустить этот
скрипт в двух вкладках браузера одновременно. Ошибка возникнет
практически немедленно.
Одна из вкладок прочитает файл в тот момент, когда вторая еще не успела его полностью записать, поэтому содержимое будет неполным.
Приведенный код, таким образом, не является безопасным, если он выполняется одновременно несколько раз (то есть в нескольких потоках). Что в интернете не является чем-то необычным, часто в один момент сервер отвечает большому количеству пользователей. Поэтому обеспечить, чтобы ваше приложение работало надежно даже при выполнении в нескольких потоках (потокобезопасность), очень важно. Иначе произойдет потеря данных и возникновение трудно обнаруживаемых ошибок.
Но, как вы видите, встроенные функции 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://translations.neon');