Nette SafeStream

Nette SafeStream garantit que chaque lecture et chaque écriture de fichier se déroule de façon isolée. Aucun thread ne commencera donc à lire un fichier qui n'a pas fini d'être écrit, et plusieurs threads n'écraseront pas le même fichier.

Installation :

composer require nette/safe-stream

À quoi cela sert-il ?

À quoi servent au juste les opérations isolées ? Commençons par un exemple simple, qui écrit à répétition dans un fichier puis en relit la même chaîne :

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

$counter = 1000;
while ($counter--) {
	file_put_contents('file', $s); // write it
	$readed = file_get_contents('file'); // read it
	if ($s !== $readed) { // check it
		echo 'strings are different!';
	}
}

On pourrait croire que l'appel echo 'strings are different!' ne peut jamais se produire. C'est tout le contraire. Essayez de lancer ce script simultanément dans deux onglets du navigateur. L'erreur surviendra presque aussitôt.

L'un des onglets lira le fichier à un instant où l'autre n'a pas fini de l'écrire, et le contenu sera donc incomplet.

Ce code n'est donc pas sûr s'il s'exécute plusieurs fois en parallèle (c'est-à-dire dans plusieurs threads). Sur Internet, ce n'est pas rare : les serveurs répondent souvent à un grand nombre d'utilisateurs à la fois. Il est capital de s'assurer que votre application fonctionne de manière fiable même exécutée dans plusieurs threads (thread-safe). Sinon, vous vous exposez à des pertes de données et à des erreurs difficiles à détecter.

Or, comme vous le voyez, les fonctions natives de lecture et d'écriture de PHP ne sont ni isolées ni atomiques.

Comment utiliser SafeStream ?

SafeStream crée un protocole sûr grâce auquel les fichiers peuvent être lus et écrits de façon isolée à l'aide des fonctions PHP standard. Il suffit de préfixer le nom du fichier par nette.safe:// :

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

SafeStream veille à ce qu'au plus un thread puisse écrire dans le fichier à la fois. Les autres attendent dans une file. Si aucun thread n'écrit, un nombre quelconque de threads peut lire le fichier en parallèle.

Toutes les fonctions PHP courantes s'utilisent avec ce protocole, par exemple :

// 'r' means open for reading only
$handle = fopen('nette.safe://file.txt', 'r');

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

Limites

SafeStream isole la lecture et l'écriture du contenu des fichiers, mais il ne peut pas rendre atomique n'importe quelle opération. Gardez ces limites à l'esprit :

  • Les informations sur les fichiers ne sont pas isolées. Les fonctions qui ne font qu'interroger les métadonnées, comme file_exists(), filesize() ou is_file(), ne participent pas au verrouillage. Elles peuvent renvoyer des informations sur un fichier qu'un autre thread est justement en train d'écrire.
  • Suppression d'un fichier ouvert sous Windows. Contrairement à Unix, Windows ne permet pas de supprimer un fichier qu'un autre thread a ouvert ; unlink('nette.safe://file') peut donc échouer.
  • Annulation automatique des écritures incomplètes. Si une écriture échoue en cours de route (parce que le disque se remplit, par exemple), SafeStream ramène le fichier, à sa fermeture, à la taille qu'il avait avant le début de l'écriture ; il ne laisse donc jamais derrière lui des données partiellement écrites.

Si vous passez à une version plus récente, consultez la page mise à niveau.

version: 3.x