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');
версия: 3.0