Nette SafeStream

Nette SafeStream gwarantuje, że każda operacja odczytu i zapisu pliku odbywa się w izolacji. Oznacza to, że żaden wątek nie zacznie czytać pliku, który nie został jeszcze w pełni zapisany, ani wiele wątków nie nadpisze tego samego pliku.

Instalacja:

composer require nette/safe-stream

Do czego to służy?

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

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

$counter = 1000;
while ($counter--) {
	file_put_contents('file', $s); // zapisujemy
	$readed = file_get_contents('file'); // odczytujemy
	if ($s !== $readed) { // sprawdzamy
		echo 'strings are different!';
	}
}

Mogłoby się wydawać, że wywołanie echo 'strings are different!' nigdy nie może nastąpić. Prawda jest odwrotna. Spróbuj uruchomić ten skrypt jednocześnie w dwóch kartach przeglądarki. Błąd wystąpi niemal natychmiast.

Jedna z kart odczyta plik w momencie, gdy druga nie skończyła jeszcze zapisywać go w całości, więc treść będzie niekompletna.

Kod nie jest więc bezpieczny, jeśli wykonywany jest wielokrotnie równolegle (czyli w wielu wątkach). W internecie nie jest to rzadkość, bo serwery często odpowiadają jednocześnie dużej liczbie użytkowników. Zapewnienie, że Twoja aplikacja działa niezawodnie także przy wykonywaniu w wielu wątkach (thread-safe), jest kluczowe. Inaczej może dojść do utraty danych i 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ół, przez który można w izolacji odczytywać i zapisywać pliki standardowymi funkcjami PHP. Wystarczy poprzedzić nazwę pliku przedrostkiem nette.safe://:

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

SafeStream zapewnia, że w danym momencie do pliku może pisać najwyżej jeden wątek. Pozostałe wątki czekają w kolejce. Jeśli żaden wątek nie pisze, plik może równolegle czytać 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://config.ini');

Ograniczenia

SafeStream izoluje odczyt i zapis zawartości plików, ale nie potrafi uczynić atomową każdej operacji. Miej te granice na uwadze:

  • Informacje o pliku nie są izolowane. Funkcje, które tylko odpytują metadane, jak file_exists(), filesize() czy is_file(), nie uczestniczą w blokowaniu. Mogą zwrócić informacje o pliku, który inny wątek właśnie zapisuje.
  • Usuwanie otwartego pliku w Windows. W przeciwieństwie do Uniksa Windows nie pozwala usunąć pliku, który inny wątek ma właśnie otwarty, więc unlink('nette.safe://file') może zawieść.
  • Automatyczne wycofanie niedokończonych zapisów. Jeśli zapis zawiedzie w połowie (na przykład zapełni się dysk), SafeStream przy zamykaniu pliku przycina go z powrotem do rozmiaru, jaki miał przed rozpoczęciem zapisu, więc nigdy nie zostawia częściowo zapisanych danych.

Jeśli aktualizujesz do nowszej wersji, zajrzyj na stronę aktualizacji.

wersja: 3.x