Aller au contenu principal

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 :

SuperglobaleContenuExemple d'usage
$_GETDonnées envoyées dans l'URL (formulaire méthode GET, paramètres d'URL)$_GET['recherche']
$_POSTDonnées envoyées dans le corps de la requête HTTP (formulaire méthode POST)$_POST['username']
$_SERVERInformations sur le serveur et sur la requête HTTP en cours$_SERVER['REQUEST_METHOD']
$_SESSIONDonnées conservées d'une page à l'autre pour un même visiteur, côté serveur$_SESSION['username']
$_COOKIEDonnées stockées dans le navigateur du visiteur et renvoyées à chaque requête$_COOKIE['theme']
info

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 exemple image/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 constante UPLOAD_ERR_OK (valeur 0) signifie qu'il n'y a eu aucune erreur.
  • size : la taille du fichier envoyé, exprimée en octets.
attention

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 en GET),
  • 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>
attention

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.");
}
}
info

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


Quelle superglobale contient les données envoyées dans l'URL de la page ?


Quelle superglobale permet de conserver des informations entre plusieurs pages pour un même visiteur, côté serveur ?


Quelle superglobale contient les informations sur les fichiers envoyés via un formulaire ?


Quel attribut de la balise <form> est indispensable pour permettre l'envoi de fichiers ?


Dans $_FILES['avatar'], quelle clé indique le nom original du fichier envoyé par l'utilisateur ?


Dans $_FILES['avatar'], quelle clé contient l'emplacement temporaire du fichier sur le serveur ?


Quelle fonction PHP permet de déplacer un fichier uploadé vers son emplacement définitif ?


Quelle constante correspond à un envoi de fichier sans erreur ?


Pourquoi ne faut-il jamais se fier uniquement à $_FILES['avatar']['type'] pour vérifier la nature d'un fichier ?


Parmi les pratiques suivantes, lesquelles sont recommandées pour sécuriser un upload de fichiers ?


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.html et upload.php, ainsi qu'un sous-dossier uploads.

É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.


Bonne pratique - Toujours préciser enctype avec un champ file

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é.


Bonne pratique - Toujours vérifier le code d'erreur avant de traiter un fichier

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.


Bonne pratique - Vérifier l'extension avec une liste blanche

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.


Bonne pratique - Renommer systématiquement les fichiers envoyés

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.

📌 Une solution