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');