SafeStream: bezpečně na soubory
Nette SafeStream garantuje, že každé čtení a zápis do souboru proběhne izolovaně. To znamená, že žádné vlákno nezačne číst soubor, který ještě není celý zapsán, nebo více vláken nebude přepisovat tentýž soubor.
Instalace:
composer require nette/safe-stream
K čemu je to dobré?
K čemu jsou izolované operace vlastně dobré? Začněme jednoduchým příkladem, který opakovaně zapisuje do souboru a následně z něj čte tentýž řetězec:
$s = str_repeat('Long String', 10000);
$counter = 1000;
while ($counter--) {
file_put_contents('soubor', $s); // write it
$readed = file_get_contents('soubor'); // read it
if ($s !== $readed) { // check it
echo 'řetězce se liší!';
}
}
Může se zdát, že volání echo 'řetězce se liší!'
nikdy nemůže nastat. Opak je pravdou. Schválně si
zkuste tento skript spustit ve dvou tabech prohlížeče zároveň. Chyba se dostaví prakticky okamžitě.
Jedna ze záložek totiž přečte soubor ve chvíli, kde jej druhá ještě nestihla celý zapsat, takže obsah nebude kompletní.
Uvedený kód tedy není bezpečný, pokud se v jednu chvíli provádí vícekrát (tedy ve více vláknech). Což na internetu není nic neobvyklého, často v jednu chvíli odpovídá server velkému počtu uživatelů. Takže zajistit, aby vaše aplikace fungovala spolehlivě i při provádění ve více vláknech (thread-safe), je velmi důležité. Jinak dojde ke ztrátě dat a vzniku těžko odhalitelných chyb.
Jak ale vidíte, nativní PHP funkce pro čtení a zápis souborů izolované a atomické nejsou.
Jak používat SafeStream?
SafeStream vytváří bezpečný protokol, pomocí něhož lze izolovaně číst a zapisovat soubory prostřednictvím
standardních PHP funkcí. Stačí jen uvést nette.safe://
před jménem souboru:
file_put_contents('nette.safe://soubor', $s);
$s = file_get_contents('nette.safe://soubor');
SafeStream zajišťuje, že v jednu chvíli může do souboru zapisovat maximálně jedno vlákno. Ostatní vlákna čekají ve frontě. Pokud žádné vlákno nezapisuje, může soubor číst paralelně libovolný počet vláken.
S protokolem lze používat všechny běžné PHP funkce, například:
// 'r' znamená otevřít pouze pro čtení
$handle = fopen('nette.safe://file.txt', 'r');
$ini = parse_ini_file('nette.safe://translations.neon');