Manipulation sécurisée de fichier + Fichiers de données php
Posté : 18 juin 2005, 23:19
Ce sujet est tiré d'une discussion interne, qui a dérivé sur une série de bench pour comparer deux méthodes de stockage de données in-file. Il semble que les résultats puissent être utiles à tous, et le débat est ouvert 
1. Manipulation sécurisée de fichiers
La manipulation de fichier sous windows nécessite l'emploi des verrous, pour gérer les accès concurrents en écriture.
En utilisant cette classe on simplifie les choses:
script1.php
2. Fichier variable
L'utilisation de var_export permet une application très sympa : le stockage de donnée directement dans un fichier PHP, sans passer par la serialisation (ce qui offre de bien meilleures performances).
Cela ne fonctionne pas correctement avec les objets, donc dans le cas d'un objet il vaut mieux passer par la sérialisation.
EDIT : Au vu de la suite du sujet, et des tests effectués, la lenteur de l'include par rapport à la lecture directe d'un fichier (même si cette lecture est suivie d'un unserialize), j'ai modifié ma classe qui n'utilise QUE la sérialisation.
Bref, du coup j'ai écrit cette classe (qui nécessite la précédente) :
1. Manipulation sécurisée de fichiers
La manipulation de fichier sous windows nécessite l'emploi des verrous, pour gérer les accès concurrents en écriture.
En utilisant cette classe on simplifie les choses:
class Fichier {
var $__pointeur;
function Fichier($fichier, $mode = 'r') {
$this->__pointeur = fopen($fichier, $mode);
if ($this->__pointeur)
flock($this->__pointeur, $mode=='r' ? LOCK_SH : LOCK_EX);
}
function ecrire($chaine) {
return fputs($this->__pointeur, $chaine);
}
function lire($longueur = false) {
if ($longueur)
return fgets($this->__pointeur, $longueur);
else
return fgets($this->__pointeur);
}
function & pointeur() {
return $this->__pointeur;
}
function fermer() {
flock($this->__pointeur, LOCK_UN);
fclose($this->__pointeur);
}
}On a accès aux fonctions de base : ouvrir, lire, ecrire, fermer. Sauf que là on gère le verrou de façon transparente. Par exemple si on effectue le test suivant, cela devrait être convaincant :script1.php
$f = new Fichier('bidule', 'w');
sleep(10.0); // on simule une écriture qui dure 10 secondes
$f->ecrire('...');
$f->fermer();
script2.php
$f = new Fichier('bidule', 'w');
sleep(10.0); // on simule une écriture qui dure 10 secondes
$f->ecrire('!!!');
$f->fermer();
Si on lance les deux scripts en même temps, celui qui a tenté d'accèder au fichier en deuxième devra attendre que le premier ait fini son opération. Sans les verrous sous windows on se retrouve avec un fichier contenant un joyeux mélange des deux tentatives d'écriture 2. Fichier variable
L'utilisation de var_export permet une application très sympa : le stockage de donnée directement dans un fichier PHP, sans passer par la serialisation (ce qui offre de bien meilleures performances).
Cela ne fonctionne pas correctement avec les objets, donc dans le cas d'un objet il vaut mieux passer par la sérialisation.
EDIT : Au vu de la suite du sujet, et des tests effectués, la lenteur de l'include par rapport à la lecture directe d'un fichier (même si cette lecture est suivie d'un unserialize), j'ai modifié ma classe qui n'utilise QUE la sérialisation.
Bref, du coup j'ai écrit cette classe (qui nécessite la précédente) :
// requis : manipulation sécurisée de fichiers
require_once 'fichier.class.php';
// classe définissant un fichier contenant une valeur
// wrapper de serialisation
// gzip ne devrait être activé que si le poids des fichiers est un facteur critique
// Ecriture 7 à 8 fois plus lente - Lecture 1.3 fois plus lente - Poids 4 fois moindre
class FichierVariable {
var $__valeur;
var $__fichier;
var $__gzip;
// constructeur : $fichier = nom du fichier
function FichierVariable($fichier, $gzip = false) {
$this->__gzip = $gzip;
$this->lire($fichier);
}
// active ou désactive la compression gzip
function gzip($gzip = true) {
$this->__gzip = $gzip;
}
// renvoie la valeur stockée dans le fichier
function valeur() {
return $this->__valeur;
}
// définit la valeur stockée dans le fichier
// cette modification n'est pas enregistrée tant qu'on n'a pas appelé la méthode sauve()
function definir($valeur) {
$this->__valeur = $valeur;
}
// lit la valeur dans le fichier
function lire($fichier = false) {
if ($fichier)
$this->__fichier = $fichier;
if (!is_file($this->__fichier)) {
$this->__valeur = NULL;
return false;
}
else {
$contenu = file_get_contents($this->__fichier);
if ($this->__gzip)
$contenu = gzuncompress($contenu);
$this->__valeur = unserialize($contenu);
return true;
}
}
// enregistre les modifications dans le fichier
// renvoie true ou false selon la valeur de succès
function ecrire($fichier = false) {
if ($fichier)
$this->__fichier = $fichier;
$f = new Fichier($this->__fichier, 'wb');
if (!$f)
return false;
$contenu = serialize($this->__valeur);
if ($this->__gzip)
$contenu = gzcompress($contenu,9);
if (!$f->ecrire($contenu))
return false;
$f->fermer();
return true;
}
}