SafeStream: varno delo z datotekami

Nette SafeStream zagotavlja, da vsako branje in pisanje v datoteko poteka izolirano. To pomeni, da nobena nit ne začne brati datoteke, ki še ni v celoti zapisana, ali več niti ne bo prepisovalo iste datoteke.

Namestitev:

composer require nette/safe-stream

Čemu je to dobro?

Čemu so izolirane operacije pravzaprav dobre? Začnimo s preprostim primerom, ki ponavljajoče zapisuje v datoteko in nato iz nje bere isti niz:

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

$counter = 1000;
while ($counter--) {
	file_put_contents('datoteka', $s); // zapiši
	$readed = file_get_contents('datoteka'); // preberi
	if ($s !== $readed) { // preveri
		echo 'niza se razlikujeta!';
	}
}

Morda se zdi, da se klic echo 'niza se razlikujeta!' nikoli ne more zgoditi. Nasprotno je res. Namenoma poskusite ta skript zagnati v dveh zavihkih brskalnika hkrati. Napaka se bo pojavila praktično takoj.

Ena od zavihkov namreč prebere datoteko v trenutku, ko je druga še ni uspela v celoti zapisati, zato vsebina ne bo popolna.

Navedena koda torej ni varna, če se izvaja večkrat hkrati (torej v več nitih). Kar na internetu ni nič nenavadnega, pogosto strežnik hkrati odgovarja velikemu številu uporabnikov. Zato je zagotavljanje, da vaša aplikacija deluje zanesljivo tudi pri izvajanju v več nitih (thread-safe), zelo pomembno. Sicer pride do izgube podatkov in nastanka težko odkritih napak.

Kot pa vidite, nativne PHP funkcije za branje in pisanje datotek niso izolirane in atomične.

Kako uporabljati SafeStream?

SafeStream ustvarja varen protokol, s katerim je mogoče izolirano brati in pisati datoteke prek standardnih PHP funkcij. Dovolj je le navesti nette.safe:// pred imenom datoteke:

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

SafeStream zagotavlja, da lahko v eno datoteko hkrati piše največ ena nit. Ostale niti čakajo v vrsti. Če nobena nit ne piše, lahko datoteko bere vzporedno poljubno število niti.

S protokolom lahko uporabljate vse običajne PHP funkcije, na primer:

// 'r' pomeni odpri samo za branje
$handle = fopen('nette.safe://file.txt', 'r');

$ini = parse_ini_file('nette.safe://translations.neon');
različica: 3.0