Les superglobales et l'upload de fichiers
Notions théoriques
Retour sur les superglobales déjà rencontrées
Depuis le début de ce cours, vous avez déjà utilisé plusieurs variables superglobales de PHP, sans forcément les regrouper : $_GET et $_SERVER (formulaires), $_SESSION et $_COOKIE (sessions et cookies), $_POST (formulaires et login). Ces variables sont dites "superglobales" car elles sont accessibles partout dans le script, y compris à l'intérieur des fonctions, sans avoir besoin de les déclarer global.
Voici un tableau de synthèse de toutes celles déjà rencontrées :
| Superglobale | Contenu | Exemple d'usage |
|---|---|---|
$_GET | Données envoyées dans l'URL (formulaire méthode GET, paramètres d'URL) | $_GET['recherche'] |
$_POST | Données envoyées dans le corps de la requête HTTP (formulaire méthode POST) | $_POST['username'] |
$_SERVER | Informations sur le serveur et sur la requête HTTP en cours | $_SERVER['REQUEST_METHOD'] |
$_SESSION | Données conservées d'une page à l'autre pour un même visiteur, côté serveur | $_SESSION['username'] |
$_COOKIE | Données stockées dans le navigateur du visiteur et renvoyées à chaque requête | $_COOKIE['theme'] |
Il existe encore d'autres superglobales, comme $_REQUEST (qui regroupe $_GET, $_POST et $_COOKIE) ou $_ENV (variables d'environnement, déjà vues en séance sur les variables d'environnement). Cette séance se concentre sur la dernière superglobale majeure que vous n'avez pas encore utilisée : $_FILES.
La superglobale $_FILES
Lorsqu'un formulaire permet à l'utilisateur d'envoyer un fichier (une photo, un document...), PHP place les informations sur ce fichier dans la superglobale $_FILES.
Pour un champ de formulaire nommé avatar, $_FILES['avatar'] contient un tableau avec les clés suivantes :
Array
(
[name] => photo.jpg // nom original du fichier, choisi par l'utilisateur
[type] => image/jpeg // type MIME annoncé par le navigateur (non fiable !)
[tmp_name] => /tmp/phpYzdqkD // emplacement temporaire du fichier sur le serveur
[error] => 0 // code d'erreur (0 = UPLOAD_ERR_OK, pas d'erreur)
[size] => 204800 // taille du fichier en octets
)
name: le nom du fichier tel qu'il existait sur l'ordinateur de l'utilisateur.type: le type MIME (par exempleimage/jpeg), annoncé par le navigateur, mais qui peut être falsifié.tmp_name: le chemin du fichier temporaire créé par PHP le temps du traitement du script. C'est ce fichier qu'il faut déplacer si l'on veut le conserver.error: un code numérique indiquant si l'envoi s'est bien déroulé. La constanteUPLOAD_ERR_OK(valeur0) signifie qu'il n'y a eu aucune erreur.size: la taille du fichier envoyé, exprimée en octets.
Le fichier situé à l'emplacement tmp_name est temporaire : si vous ne le déplacez pas avant la fin du script, il est automatiquement supprimé par PHP.
Le formulaire d'upload HTML
Pour qu'un formulaire HTML puisse envoyer un fichier, deux conditions sont indispensables :
- la méthode doit être
POST(un envoi de fichier n'est pas possible enGET), - l'attribut
enctype="multipart/form-data"doit être ajouté à la balise<form>.
<form action="upload.php" method="POST" enctype="multipart/form-data">
<label for="avatar">Choisir une image :</label>
<input type="file" id="avatar" name="avatar">
<input type="submit" value="Envoyer">
</form>
Sans l'attribut enctype="multipart/form-data", le fichier n'est pas du tout envoyé au serveur, même si le formulaire semble fonctionner normalement.
Traiter l'upload avec move_uploaded_file()
Une fois le fichier reçu, il faut le déplacer de son emplacement temporaire vers un dossier définitif du serveur, avec la fonction move_uploaded_file().
<?php
if ($_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
$destination = 'uploads/' . $_FILES['avatar']['name'];
move_uploaded_file($_FILES['avatar']['tmp_name'], $destination);
print("Fichier envoyé avec succès.");
} else {
print("Erreur lors de l'envoi du fichier.");
}
Bonnes pratiques pour sécuriser l'upload de fichiers
Accepter des fichiers envoyés par des utilisateurs est une opération sensible : il faut appliquer les réflexes de sécurité vus en séance précédente, et quelques réflexes spécifiques à l'upload.
- Vérifier l'extension du fichier : n'autoriser qu'une liste précise d'extensions (par exemple
jpg,jpeg,png) plutôt que de tout accepter. - Ne pas se fier au type MIME annoncé par le navigateur (
$_FILES['avatar']['type']) : cette information est fournie par le client et peut être falsifiée par un attaquant. - Limiter la taille du fichier accepté, en comparant
$_FILES['avatar']['size']à une limite maximale. - Renommer le fichier avant de l'enregistrer, plutôt que de garder son nom d'origine, pour éviter d'écraser un fichier existant ou d'exécuter un nom de fichier malveillant.
$extensionsAutorisees = ['jpg', 'jpeg', 'png'];
$extension = strtolower(pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION));
if (!in_array($extension, $extensionsAutorisees)) {
print("Extension de fichier non autorisée.");
} elseif ($_FILES['avatar']['size'] > 2 * 1024 * 1024) {
print("Le fichier est trop volumineux (2 Mo maximum).");
} else {
$nouveauNom = uniqid() . '.' . $extension;
move_uploaded_file($_FILES['avatar']['tmp_name'], 'uploads/' . $nouveauNom);
print("Fichier envoyé avec succès.");
}
Exemple pratique
<!-- Fichier index.html -->
<form action="upload.php" method="POST" enctype="multipart/form-data">
<label for="avatar">Photo de profil :</label>
<input type="file" id="avatar" name="avatar">
<input type="submit" value="Envoyer">
</form>
<?php
// Fichier upload.php
$extensionsAutorisees = ['jpg', 'jpeg', 'png'];
if ($_FILES['avatar']['error'] !== UPLOAD_ERR_OK) {
print("Erreur lors de l'envoi du fichier.");
} else {
$extension = strtolower(pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION));
if (!in_array($extension, $extensionsAutorisees)) {
print("Extension de fichier non autorisée.");
} elseif ($_FILES['avatar']['size'] > 2 * 1024 * 1024) {
print("Le fichier est trop volumineux (2 Mo maximum).");
} else {
$nouveauNom = uniqid() . '.' . $extension;
move_uploaded_file($_FILES['avatar']['tmp_name'], 'uploads/' . $nouveauNom);
print("Photo de profil mise à jour.");
}
}
Cet exemple combine toutes les bonnes pratiques vues plus haut : vérification de l'erreur d'envoi, whitelist d'extensions, limite de taille, et renommage du fichier.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Vous développez une page de profil utilisateur permettant d'envoyer une photo de profil (avatar), avec les vérifications de sécurité nécessaires.
- Dans votre répertoire "Documents", créez le répertoire
tp_upload. - Créez les fichiers
profil.htmletupload.php, ainsi qu'un sous-dossieruploads.
Étape 1 — Créer le formulaire HTML d'upload
Dans profil.html, créez un formulaire qui permet d'envoyer un fichier vers upload.php.
Dès qu'un formulaire contient un champ <input type="file">, réflexe automatique : ajouter enctype="multipart/form-data" sur la balise <form>. C'est une erreur fréquente et difficile à repérer, car le formulaire semble fonctionner normalement sans provoquer d'erreur visible.
Étape 2 — Vérifier qu'aucune erreur ne s'est produite lors de l'envoi
Dans upload.php, avant de traiter le fichier, il faut vérifier que l'envoi s'est bien déroulé.
Un envoi de fichier peut échouer pour de nombreuses raisons (fichier trop volumineux, envoi interrompu...). Ne jamais partir du principe que le fichier est arrivé correctement : vérifiez toujours $_FILES['avatar']['error'] en premier.
Étape 3 — Vérifier l'extension du fichier avant de le déplacer
Seules les images jpg, jpeg et png doivent être acceptées.
Toujours vérifier l'extension d'un fichier par rapport à une liste d'extensions autorisées (liste blanche), plutôt que par rapport à une liste d'extensions interdites : il est impossible d'anticiper toutes les extensions dangereuses possibles.
Étape 4 — Déplacer le fichier avec un nom unique
Enfin, il faut déplacer le fichier vers le dossier uploads/, en lui donnant un nom unique pour éviter d'écraser un fichier existant.
Ne conservez jamais le nom d'origine d'un fichier envoyé par un utilisateur : deux visiteurs pourraient envoyer un fichier portant le même nom et s'écraser mutuellement, ou le nom pourrait contenir des caractères dangereux. Une fonction comme uniqid() génère un nom de fichier unique.