Les fichiers
Dernière mise à jour le 8 octobre 2022
Ouverture d'un fichier
La plupart des opérations sur un fichier nécessite que celui soit préalablement ouvert.
On utilise la fonction fopen() dont la syntaxe est :
resource descripteur = fopen(string nom_fichier, string mode [,bool utiliser_include_path]);
Paramètres :
-
Le premier paramètre est le nom du fichier, incluant éventuellement le chemin d'accès correspondant.
-
Le deuxième paramètre est le mode d'ouverture du fichier (voir plus loin).
- Le troisième paramètre (optionnel) spécifie si on veut que le fichier soit recherché dans le "include_path".
Qu'est-ce que le "include_path"?
Le "include_path" est une directive de configuration qui spécifie un chemin formé d'une liste de répertoires où les instructions require
et include
, de même que les fonctions fopen(), file(), readfile() et file_get_contents() chercheront les fichiers.
Le format est identique à la variable d'environnement système PATH
, soit une liste de répertoires séparés par deux points (:) sous Linux ou par un point-virgule (;) sous Windows.
Ceci est un exemple de directive "include_path" :
.:/usr/share/php
Dans ce exemple, les fichiers seront d'abord recherchés dans le répertoire courant ("."), puis dans /usr/share/php.
Le "include_path" est défini dans le fichier php.ini.
Attention! Les chemins utilisés par les fonctions de gestion des fichiers sont les chemins dans le système de fichiers du serveur!
Le chemin du répertoire Web d'un utilisateur est contenu dans la variable $_SERVER['CONTEXT_DOCUMENT_ROOT']
.
Pour l'utilisateur patoche, ce chemin correspond sur notre serveur d'hébergement à /home/patoche/public_html
.
Mode d'ouverture
Le tableau suivant présente quelques-unes des valeurs possibles du deuxième paramètre de la fonction fopen() :
Mode | Signification |
---|---|
r | Lecture seulement. |
r+ | Lecture et écriture. À la différence de "w+", le fichier doit exister. |
w | Écriture seulement. Si le fichier existe déjà, son contenu est écrasé. Dans le cas contraire, le fichier est créé. |
w+ | Lecture et écriture. Si le fichier existe déjà, son contenu est écrasé. Dans le cas contraire, le fichier est créé. Rare. |
a | Écriture seulement avec ajout des données à la fin du fichier. Si le fichier n'existe pas encore, il est créé. |
a+ | Lecture et écriture avec ajout des données à la fin du fichier. Si le fichier n'existe pas encore, il est créé. Rare. |
Exemples d'ouverture d'un fichier :
// ouverture en lecture seulement du fichier "proverbes.txt" dans le sous-répertoire // "textes" du répertoire courant $fich = fopen("textes/proverbes.txt", "r");
// ouverture en écriture du fichier "/home/utilisateur/public_html/donnees/clients.csv" $chemin = $_SERVER['CONTEXT_DOCUMENT_ROOT'] . "/donnees/clients.csv"; $data = fopen($chemin, "w");
// ouverture du fichier "/home/utilisateur/commandes/commandes.txt" $chemin = $_SERVER['CONTEXT_DOCUMENT_ROOT'] . "/../commandes/commandes.txt" $fich_commandes = fopen($chemin, "a");
Ouverture via FTP et HTTP
Dans la fonction fopen(), le nom de fichier local peut parfois être remplacé par une URL de type FTP ou HTTP. Cette possibilité dépend du serveur où est stocké le fichier.
Lorque l'URL commence par ftp://, une connexion FTP (mode passif) est ouverte avec le serveur et la fonction retourne un pointeur sur le début du fichier.
Lorque l'URL commence par http://, une connexion HTTP est ouverte avec le serveur et la fonction retourne un pointeur sur la réponse fournie (texte normalement affiché dans le navigateur suite à une requête GET).
Exemple :
// ouverture d'un fichier distant via HTTP $url = "http://198.245.55.77/~bob/stats/Baseball.csv"; $fich = fopen($url, "r"); ...
Rappelons que la casse (lettres majuscules/minuscules) n'a pas d'importance dans les noms de domaines, mais peut en avoir dans les chemins d'accès.
Problèmes d'ouverture
Les deux erreurs les plus fréquentes sont :
- un fichier inexistant;
- des droits d'accès inadéquats.
Lorsque l'appel à la fonction fopen() échoue, celle-ci renvoie la valeur FALSE
.
Exemple :
$fich = fopen("fichiers/blabla.txt", "r"); // si l'ouverture du fichier est possible if ($fich !== false) { ... }
L'échec peut entraîner (dépendant de la configuration de PHP sur le serveur) l'affichage d'un message déplaisant :
On peut traiter l'erreur avec plus de convivialité en supprimant le message d'erreur PHP (avec le caractère "@") et en générant notre propre message.
Exemple :
@ $fich = fopen("fichiers/blabla.txt", "r"); if ($fich !== false) { ... } else { echo <<<FIN Votre commande ne peut être traitée<br> Veuillez S.V.P. essayer plus tard FIN; exit(1); // termine immédiatement le programme }
Droits d'accès
Puisque notre but est de créer des applications Web, toutes les opérations sur nos fichiers se feront à partir d'un script invoqué depuis un navigateur Web.
Vous devrez donc maintenant tenir compte du fait suivant : pour des raisons de sécurité, toutes les opérations sur les fichiers effectuées à partir du Web sont effectuées dans le système de fichiers du serveur par un même usager, soit www-data.
Supposez par exemple que l'usager bob a écrit un script qui doit ajouter des données au fichier donnees/data.txt. Pour le serveur, lorsqu'un internaute exécute le script, la ligne suivante :$fich = fopen("donnees/data.txt", "a");
n'est pas exécutée par l'usager bob, mais par l'usager www-data. L'usager virtuel dont le nom est www-data (ou le groupe du même nom selon le réglage des permissions) doit donc avoir accès en écriture au fichier donnees/data.txt.
Puisque plusieurs d'entre vous ne sont pas (pour l'instant) très confortables en Linux, voici une procédure simple à utiliser si votre application contient un script qui doit écrire dans un fichier :
- créez dans le répertoire public_html le sous-répertoire (ex : donnees) qui contiendra le fichier devant être accessible en écriture (vous pouvez utiliser FileZilla pour toutes ces manipulations);
-
donnez (temporairement) à ce sous-répertoire les droits d'accès correspondant au code
777
(tous les droits pour toutes les catégories d'utilisateurs); -
exécutez (via le Web) un script pour créer dans ce sous répertoire le fichier qui devra être accessible en écriture, par exemple :
$fich = fopen("donnees/data.txt", "w"); // inutile d'y écrire quelque chose fclose($fich);
- vérifier l'existence de ce fichier (vous pourrez constater qu'il contient 0 octet et qu'il appartient à l'usager www-data);
-
rétablissez les droits d'accès du sous-répertoire contenant le fichier (code
755
).
Et voilà! Vous avez maintenant un fichier (donnees/data.txt) dans lequel vos scripts pourront écrire!
Fermeture d'un fichier
Il est nécessaire de fermer un fichier pour s'assurer que les données écrites y soient réellement enregistrées et qu'un autre usager puisse accéder au fichier en écriture.
On utilise la fonction fclose()
Exemple :
fclose($fich);
Cette fonction retourne TRUE
en cas de réussite et FALSE
en cas d'échec.
Les cas d'échec sont tellement rares que la valeur de retour est rarement testée.
Écriture
Attention! Tous nos exemples utiliseront des chaînes de caractères (usage le plus fréquent des fichiers plats (flat files) en PHP! Pour écrire et lire des données binaires, voir les fonctions pack() et unpack().
Pour écrire, on peut utiliser indistinctement fwrite() et fputs(), un alias de fwrite().
Syntaxe :
int fwrite(resource descripteur, string chaîne [, int longueur]);
Le paramètre longueur est optionnel et spécifie le nombre maximal d'octets à écrire.
L'écriture s'arrête lorsque toute la chaîne est écrite ou que le nombre d'octets spécifié est atteint.
La fonction fwrite() retourne le nombre d'octets écrits ou FALSE
en cas d'erreur.
Excemple :
$nb = fwrite($fich, $chaine); if ($nb === FALSE) { echo "Erreur d'écriture"; exit(1); } else { echo "$nb octets écrits"; }
Dans l'exemple précédent, il est important d'utiliser l'opérateur ===
et non ==
, puisque le fait d'avoir écrit 0 octets n'est pas nécessairement une erreur.
Lecture
Il existe plusieurs façons de lire le contenu d'un fichier. Nous verrons tour à tour :
- la lecture ligne par ligne;
- la lecture avec élimination des balises HTML
- la lecture de fichiers CSV
- la lecture de l'intégralité d'un fichier
- la lecture caractère par caractère
- la lecture d'une longueur arbitraire.
Détection de la fin du fichier
Mais avant de voir comment lire dans un fichier, il faut savoir comment on en détecte la fin lors de la lecture.
On utilise pour cela la fonction feof() dont la syntaxe est :
bool feof(resource descripteur);
Cette fonction retourne TRUE
si le "pointeur" de lecture est positionné à la fin du fichier et FALSE
autrement.
Exemple :
while (! feof($fich_clients)) { // lecture et traitement d'un enregistrement // ... }
Lecture ligne par ligne
On utilise la fonction fgets(), dont la syntaxe est :
string fgets(resource descripteur [, int longueur]);
La fonction fgets() s'arrête lorsqu'elle rencontre une des conditions suivantes :
- après avoir lu longueur – 1 octets;
- après avoir rencontré une fin de ligne;
- à la fin du fichier.
En cas d'absence du paramètre "longueur", la lecture se poursuit jusqu'à la fin de la ligne.
Exemple :
$fich = fopen("proverbes.txt", "r"); while (! feof($fich)) { $ligne = fgets($fich, 256); // lit au maximum 255 caractères par ligne echo $ligne . "<br>"; }
Lecture avec élimination des balises HTML
AVERTISSEMENT : La fonction fgetss() est maintenant considérée obsolète et ne devrait plus être utilisée. Vous pouvez passer immédiatement à la fonction strip_tags().
On utilise la fonction fgetss().
La syntaxe est :
string fgetss(resource descripteur, int longueur [, string balises_autorisées] );
Fonctionne de la même façon que fgets(), mais supprime toutes les balises HTML et PHP rencontrées.
Le paramètre optionnel permet de spécifier les balises acceptées.
Soit un fichier HTML (incomplet) :
<h2>Un titre de niveau 2</h2> <p>Un paragraphe</p> Une ligne de texte
Le code suivant affiche son contenu sans aucune balise HTML :
$fich = fopen("texte.html", "r"); while (! feof($fich)) { $ligne = fgetss($fich, 256); echo $ligne . "<br>"; }
Sortie :
Un paragraphe
Une ligne de texte
Permettons maintenant les titres de niveau 2 :
$fich = fopen("texte.html", "r"); while (! feof($fich)) { $ligne = fgetss($fich, 256, "<h2>"); echo $ligne . "<br>"; }
Sortie :
Un titre de niveau 2
Un paragrapheUne ligne de texte
La fonction strip_tags()
La fonction fgetss(), maintenant obsolète, utilise le même moteur de recherche que la fonction strip_tags().
Reprenons donc notre dernier exemple en utilisant fgets() et strip_tags().
Soit un fichier HTML :
<h2>Un titre de niveau 2</h2> <p>Un paragraphe</p> Une ligne de texte
Le code suivant lit ligne par ligne le contenu du fichier précédent en supprimant toutes les balises HTML et PHP, à l'exception de la balise <h2>
:
$fich = fopen("texte.html", "r"); while (! feof($fich)) { $ligne = strip_tags(fgets($fich, 256), "<h2>"); echo $ligne . "<br>"; }
Sortie :
Un titre de niveau 2
Un paragrapheUne ligne de texte
La fonction strip_tags() est souvent utilisée pour filtrer les données entrées par formulaire.
Lecture de fichiers CSV
Le format CSV
Lorsqu'on utilise des fichiers plats (flat files) pour stocker des données, on doit s'assurer que ces dernières seront facilement...
- récupérables par les autres scripts;
- lisibles par les humains à des fins de débogage.
La stratégie la plus courante consiste à stocker exactement un enregistrement par ligne, le caractère de nouvelle ligne ("\n") jouant alors le rôle de séparateur.
Dans chaque enregistrement (chaque ligne), les champs sont délimités par un caractère particulier, par exemple la virgule, qui joue ainsi le rôle de délimiteur.
Ce qui est décrit ici correspond exactement au format CSV.
Le format CSV pour "Comma Seperated Value" est presque devenu un standard (utilisé entre autres par Microsoft Excel et LibreOffice Calc).
La principale caractéristique de CSV est que les enregistrements sont séparés par une fin de ligne et que les champs sont délimités (par défaut) par des virgules (comma en anglais).
Exemple de fichier CSV :
Patoche,Alain,patoche@gmail.com,Ste-Thérèse Gratton,Robert,elvis@hotmail.com,Pointe-Calumet Monette,Linda,doudoune@hotmail.com,Laval Narrache,Jean,jean.narrache@dentistes.ca,Deux-Montagnes Labrosse,Adam,adam.labrosse@dentistes.ca,Ste-Thérèse
Un fichier CSV peut tout aussi bien être lu par un logiciel comme Microsoft Excel que par un simple éditeur de texte.
Il est important de choisir un séparateur et un délimiteur qui ne se retrouveront pas dans les données.
Il sera parfois nécessaire de filtrer les données à écrire (par exemple, celles provenant d'un formulaire) pour éviter que de tels caractères ne soient introduits, malicieusement ou non, parmi les données
Exemple de création d'un fichier CSV :
define("DELIMITEUR", ":"); $fich = fopen("fichiers/clients.csv", "w"); while (...) { ... $client = $nom . DELIMITEUR . $prenom . DELIMITEUR . $adresse . DELIMITEUR . $telephone . "\n"; fwrite($fich, $client); } ...
Il n'existe pas de fonction spécialisée pour écrire dans un fichier CSV.
Il en existe toutefois une pour lire.
Syntaxe de la fonction fgetcsv() :
array fgetcsv(resource descripteur, int longueur [, string délimiteur]);
La longueur spécifiée doit être plus grande que la plus grande ligne du fichier.
Le paramètre optionnel, le délimiteur, est par défaut la virgule.
Exemple d'affichage du contenu d'un fichier CSV :
define("DELIMITEUR", ":"); define("MAX_CAR", 128); $fich = fopen("fichiers/clients.csv", "r"); while (!feof($fich)) { // lit une ligne $tab_client = fgetcsv($fich, MAX_CAR, DELIMITEUR); // affiche les données echo "Nom : " . $tab_client[0] . "<br>"; echo "Prénom : " . $tab_client[1] . "<br>"; echo "Adresse : " . $tab_client[2] . "<br>"; echo "Téléphone : " . $tab_client[3] . "<br>"; echo "<hr>"; }
Lecture de l'intégralité d'un fichier
Pour lire un fichier au complet en une seule opération, la façon la plus intuitive consiste à...
- ouvrir le fichier;
- lire ligne par ligne son contenu;
- fermer le fichier.
Il existe heureusement des fonctions plus performantes!
readfile()
Syntaxe :
int readfile(string nom_de_fichier [, bool utiliser_include_path]);
La fonction readfile()...
- ouvre le fichier;
- imprime son contenu intégral sur la sortie standard (vers le navigateur Web);
- ferme le fichier;
- retourne le nombre total d'octets lus.
Le paramètre optionnel spécifie si l'interpréteur PHP doit rechercher le fichier dans le "include_path".
fpassthru()
Syntaxe :
int fpassthru(resource descripteur);
Fonctionne de manière semblable à readfile(), sauf que le fichier doit être préalablement ouvert.
La fonction fpassthru()...
- lit à partir de la position courante du pointeur;
- imprime son contenu intégral sur la sortie standard (vers le navigateur Web);
- ferme le fichier;
- retourne le nombre d'octets lus.
file()
Syntaxe :
array file(string nom_fichier [, utiliser_include_path]);
Ici le contenu du fichier n'est plus envoyé vers la sortie standard, mais récupéré dans un tableau.
Chaque élément du tableau contiendra une ligne du fichier.
La fonction retourbe FALSE
en cas de problème (ex : fichier introuvable).
file_get_contents()
Syntaxe :
string file_get_contents(string nom_fichier [, utiliser_include_path]);
Fonctionne de la même façon que file(), sauf que l'ensemble du fichier est retourné dans une seule chaîne.
En résumé
Les quatre fonctions suivantes permettent de lire un fichier au complet :
Fonction | |
---|---|
readfile() | Ouvre le fichier, lit et affiche son contenu, puis le ferme. |
fpassthru() | Lit un fichier ouvert à partir de la position courante, affiche son contenu, puis le ferme. |
file() | Retourne le contenu du fichier dans un tableau. |
file_get_contents() | Retourne le contenu du fichier dans une chaîne. |
Lecture caractère par caractère
Il arrive, même si cela est plutôt rare, que l'on doive lire un fichier caractère par caractère.
Syntaxe :
string fgetc(resource descripteur);
Retourne (sous forme de chaîne) le caractère à la position courante dans le fichier.
Retourne FALSE
à la fin du fichier.
Exemple :
// affiche le contenu du fichier en lisant un caractère à la fois $fini = FALSE; while (!fini) { $car = fgetc($fich); if ($car !== FALSE) { echo $car; else $fini = FALSE; } // en plus condensé while (FALSE !== ($car = fgetc( $fich))) { echo $car; }
Lecture d'une longueur arbitraire
La fonction fread() lit le nombre d'octets spécifié (ou moins si la fin du fichier est rencontrée) et les retourne sous forme de chaîne.
Syntaxe :
string fread(resource descripteur, int longueur);
Exemples :
// lecture de 50 octets $nom_fichier = "/usr/local/quelque_chose.txt"; $fich = fopen($nom_fichier, "r"); $contenu = fread($fich, 50); fclose($fich);
// lecture de l'intégralité du fichier, soit la même chose que file_get_contents() $nom_fichier = "/usr/local/quelque_chose.txt"; $fich = fopen($nom_fichier, "r"); $contenu = fread($fich, filesize($nom_fichier)); fclose($fich);
Détermination de la taille d'un fichier
On utilise la fonction filesize().
Syntaxe :
int filesize(string nom_fichier);
Retourne la taille en octets.
Vérification de l'existence d'un fichier
On utilise la fonction file_exists().
Syntaxe :
bool file_exists(string nom_fichier);
Permet de vérifier si un fichier existe, sans même l'ouvrir.
$nom_fichier = "/chemin/jusqu/a/fichier.txt"; echo "Le fichier $nom_fichier "; if (file_exists($nom_fichier)) { echo "existe"; } else { echo "n'existe pas"; }
Suppression
On utilise la fonction unlink().
Syntaxe :
bool unlink(string nom_fichier);
Supprime le fichier dont le nom est passé en paramètre.
En cas d'échec (ex : droits d'accès insuffisants), retourne FALSE
.
Remarque : le nom de la commande vient du monde Linux où un fichier existe tant qu'il y a un lien (link) dessus.
Exercices à faire en classe
Exercice 1
Vous trouverez à l'URL https://prog101.com/cours/kb9/anneaux.html un fichier contenant un extrait important du roman La Communauté de l'anneau.
Écrivez le script exercice1.php
qui affiche ce texte en supprimant toutes les balises HTML s'y trouvant, à l'exception de la balise <strong>
.
La sortie devrait ressembler à ceci :
Pour réaliser cet exercice, vous devrez d'abord récupérer le contenu du fichier distant avec file_get_contents(), puis l'afficher en supprimant certaines balises HTML avec strip_tags().
Assurez vous que la page affichée par votre script est bien valide.
Exercice 2
Dans cet exercice, vous allez réaliser une petite application (page HTML + script PHP) permettant de constituer une mini-base de données.
Commencez par écrire le fichier exercice2.html
qui présente à l'internaute un formulaire permettant d'entrer un nom, un prénom et une adresse courriel, comme dans l'exemple suivant :
Écrivez ensuite le script exercice2.php
dont le rôle est de recevoir les données du formulaire et de les ajouter à chaque fois à la fin d'un fichier CSV.
Vous devez gérer les deux types d'erreur suivants :
- échec à l'ouverture du fichier CSV (peu importe la cause);
- paramètre(s) manquant(s).
Oubliez la validation côté HTML. Il doit être possible d'envoyer un formulaire avec des champs vides pour tester le deuxième type d'erreur.
Exercice 3
Écrivez le script exercice3.php
qui affiche dans un tableau HTML le contenu du fichier CSV de l'exercice précédent.
Exemple de sortie :
Questions et exercices de révision
Nous ne fournirons pas les réponses à ces questions pour deux bonnes raisons. La première est que celles-ci se trouvent dans les notes de cours. La seconde est que, dans certains cas, ce serait presque donner la réponse à des questions d'examen.
Question 1
Donnez la différence entre r+
et w+
comme deuxième paramètre de la fonction fopen().
Question 2
Que contient, pour l'usager "patoche", la variable $_SERVER['CONTEXT_DOCUMENT_ROOT']
?
Question 3
Vrai ou faux? Un script PHP peut ouvrir un fichier situé sur un autre serveur.
Question 4
À quoi sert le symbole @
placé devant une instruction en PHP?
Question 5
Que va écrire le code suivant dans le fichier?
fwrite($fich, "A", 5);
Question 6
Expliquez la différence entre les opérateurs ==
et ===
.
Question 7
Supposez que le fichier dont le descripteur est $fich contient la chaîne "ABCDEFGH". Donnez la sortie du code suivant :
$ligne = fgets($fich, 8); echo $ligne;
Question 8
Donnez le code HTML généré par la série d'instructions suivante :
$html = "<div>Un texte <strong>important</strong> à lire.</div>"; $balises = "<div>"; $sortie = strip_tags($html, $balises); echo $sortie;
Question 9
Que veut dire l'acronyme CSV? Traduisez-le en français.
Question 10
Donnez la différence entre les fonctions readfile() et fpassthru().
Question 11
Donnez la différence entre les fonctions file() et file_get_contents().
Question 12
Donnez la différence entre les fonctions fgets() et fread().