SafeStream: bezpieczny dostęp do plików

Nette SafeStream gwarantuje, że każdy odczyt i zapis do pliku odbędzie się w izolacji. Oznacza to, że żaden wątek nie zacznie czytać pliku, który jeszcze nie został cały zapisany, lub wiele wątków nie będzie nadpisywać tego samego pliku.

Instalacja:

composer require nette/safe-stream

Do czego to służy?

Do czego są właściwie dobre operacje izolowane? Zacznijmy od prostego przykładu, który wielokrotnie zapisuje do pliku, a następnie odczytuje z niego ten sam ciąg:

$s = str_repeat('Long String', 10000);

$counter = 1000;
while ($counter--) {
	file_put_contents('soubor', $s); // zapisz go
	$readed = file_get_contents('soubor'); // odczytaj go
	if ($s !== $readed) { // sprawdź go
		echo 'ciągi się różnią!';
	}
}

Może się wydawać, że wywołanie echo 'ciągi się różnią!' nigdy nie może nastąpić. Wręcz przeciwnie. Spróbuj uruchomić ten skrypt w dwóch kartach przeglądarki jednocześnie. Błąd pojawi się praktycznie natychmiast.

Jedna z zakładek bowiem odczyta plik w momencie, gdy druga jeszcze nie zdążyła go całego zapisać, więc zawartość nie będzie kompletna.

Podany kod nie jest więc bezpieczny, jeśli w jednej chwili wykonuje się wielokrotnie (czyli w wielu wątkach). Co w internecie nie jest niczym niezwykłym, często w jednej chwili serwer odpowiada dużej liczbie użytkowników. Zapewnienie, aby Twoja aplikacja działała niezawodnie nawet przy wykonywaniu w wielu wątkach (thread-safe), jest bardzo ważne. W przeciwnym razie dojdzie do utraty danych i powstania trudnych do wykrycia błędów.

Jak jednak widzisz, natywne funkcje PHP do odczytu i zapisu plików nie są izolowane ani atomowe.

Jak używać SafeStream?

SafeStream tworzy bezpieczny protokół, za pomocą którego można izolowanie czytać i zapisywać pliki za pośrednictwem standardowych funkcji PHP. Wystarczy tylko podać nette.safe:// przed nazwą pliku:

file_put_contents('nette.safe://soubor', $s);
$s = file_get_contents('nette.safe://soubor');

SafeStream zapewnia, że w jednej chwili do pliku może zapisywać maksymalnie jeden wątek. Pozostałe wątki czekają w kolejce. Jeśli żaden wątek nie zapisuje, plik może czytać równolegle dowolna liczba wątków.

Z protokołem można używać wszystkich powszechnych funkcji PHP, na przykład:

// 'r' oznacza otwarcie tylko do odczytu
$handle = fopen('nette.safe://file.txt', 'r');

$ini = parse_ini_file('nette.safe://translations.neon');
wersja: 3.0