SafeStream: Безопасность для файлов

Nette\Utils\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) { // check it
		echo 'strings are different!';
	}
}

Может показаться, что echo 'strings differ!' никогда не может возникнуть. Верно и обратное. Попробуйте запустить этот сценарий в двух вкладках браузера одновременно. Ошибка возникнет почти сразу.

Одна из вкладок будет читать файл в то время, когда другая ещё не успела всё записать, поэтому содержимое будет неполным.

Поэтому код небезопасен, если он выполняется несколько раз в одно и то же время (т. е. в нескольких потоках). Что не редкость в интернете, часто сервер отвечает большому количеству пользователей одновременно. Поэтому очень важно обеспечить надежную работу приложения даже при выполнении в несколько потоков (thread-safe). В противном случае данные будут потеряны и возникнут труднообнаруживаемые ошибки.

Но, как вы видите, встроенные функции 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');