SafeStream: Atomické operace‎

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.

Řešení nabízí bezpečný protokol, pomocí něhož můžeme atomicky manipulovat se soubory prostřednictvím standardních PHP funkcí. Stačí jen uvést nette.safe:// před jménem souboru:

$handle = fopen('nette.safe://test.txt', 'x'); // před jméno souboru dáme nette.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('nette.safe://test.txt', $content);

$ini = parse_ini_file('nette.safe://autoload.ini');

Protokol se zpřístupní automaticky při instalaci přes Composeru. SafeStream lze použít i bez Composeru, je ale nutné ho manualně registrovat:

Nette\Utils\SafeStream::register();

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.