Atomické operace
Protokol Nette\Utils\SafeStream pro práci se soubory garantuje, že každá operace proběhne atomicky a izolovaně.
K čemu je to 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 oknech zároveň. Chyba se dostaví prakticky okamžitě.
Uvedený kód totiž není bezpečný, pokud se zároveň provádí vícekrát (tedy ve více vláknech). Což na internetu není nic neobvyklého, často se v tentýž okamžik připojuje více lidí k jednomu webu. Takže starat se o to, aby vaše aplikace fungovala spolehlivě i při provádění ve více vláknech (thread-safe), je velmi důležité. Jak totiž vidíte, nativní PHP funkce atomické nejsou. Jinak musíme počítat se ztrátou dat a vznikem těžko odhalitelných chyb.
Jak zajistit, aby se funkce file_put_contents nebo
file_get_contents chovaly atomicky? Řešení nabízí bezpečný
protokol SafeStream, pomocí něhož můžeme atomicky manipulovat se soubory
prostřednictvím standardních PHP funkcí. Protokol se zpřístupní
automaticky při načtení Nette Framework. A pak stačí jen uvést
safe:// před jménem souboru:
$handle = fopen('safe://test.txt', 'x'); // před jméno souboru dáme safe://
fwrite($handle, 'Nette Framework'); // zatím se píše do pomocného souboru
fclose($handle); // a teprve teď se přejmenuje na test.txt
Lze samozřejmě používat všechny známé funkce, například:
file_put_contents('safe://test.txt', $content);
$ini = parse_ini_file('safe://autoload.ini');
SafeStream vám garantuje:
- atomicitu: soubor se zapíše buď celý, nebo vůbec
- isolaci: nikdo nezačne číst soubor, který ještě není celý zapsán
Pokud zapisujete do existujícího souboru v režimu
a (append), SafeStream vytvoří jeho kopii a teprve po
úspěšném zápisu jí přejmenuje na původní název. Zapisovat v tomto
režimu má tedy vyšší režii, než zápis v ostatních módech.
