Formulaires et données reçues
Jusqu’ici, les données affichées par nos pages étaient écrites par nous (dans les fichiers PHP) Cependant, une application web traite généralement aussi des données en provenance de l’utilisateur, d’autres programmes ou appareils. Dans ce chapitre, nous voyons un moyen de recevoir et traiter ce genre de données à l’aide de formulaire.
Recevoir des données du navigateur
Section intitulée « Recevoir des données du navigateur »Une requête peut transporter des données par deux chemins. Le premier est l’URL elle-même : tout ce qui suit le point d’interrogation forme la chaîne de requête, v isible dans la barre d’adresse. Le second est le corps de la requête, envoyé séparément et invisible dans la barre d’adresse.
Avant que la première ligne de votre script s’exécute, PHP a déjà lu la requête et rempli trois tableaux.
$_GET— les paires nom/valeur trouvées dans la chaîne de requête ;$_POST— les données envoyées dans le corps d’une requêtePOSTavec l’encodage habituel d’un formulaire HTML;$_SERVER— le contexte de la requête : méthode employée, adresse demandée, en-têtes reçus.
Modifiez la requête ci-dessous et observez les trois tableaux se remplir.
[
"page" => "produits",
"categorie" => "sirop",
][] (vide : la requête n'est pas un POST)REQUEST_METHOD => "GET" REQUEST_URI => "/index.php?page=produits&categorie=sirop" QUERY_STRING => "page=produits&categorie=sirop"
Notez que les deux chemins ne s’excluent pas. Un formulaire envoyé en POST vers traitement.php?etape=2 remplira $_POST avec ses champs et $_GET avec le paramètre de l’URL.
GET ou POST
Section intitulée « GET ou POST »Le choix se discute mieux à partir de ses conséquences observables qu’à partir d’une règle apprise par cœur.
| GET | POST | |
|---|---|---|
| Où voyagent les données | dans l’URL | dans le corps de la requête |
| Visible dans la barre d’adresse | oui | non |
| Peut être mise en favori, partagée par courriel | oui | non |
| Au rafraîchissement | rejouée sans avertissement | le navigateur demande confirmation |
| Taille utilisable | limitée en pratique | nettement plus large |
| Conservée dans le cache du navigateur | oui | non |
Écrire le formulaire
Section intitulée « Écrire le formulaire »Trois attributs suffisent à faire circuler des données : method sur le formulaire, action pour la destination, et name sur chaque champ.
<form method="post" action="traitement.php"> <label>Nom <input type="text" name="nom"> </label> <button type="submit">Envoyer</button></form>L’attribut name est le pont entre le champ et le code PHP. Un champ sans name s’affiche normalement, se remplit normalement, et n’arrive pas au serveur — c’est l’oubli le plus fréquent, et le plus silencieux.
name du champ devient la clé du tableau. Survolez un champ pour voir le lien.<label>Nom <input type="text" name="nom"> </label> <label>Courriel <input type="email" name="courriel"> </label> <label>Quantité <input type="number" name="quantite"> </label> <input type="checkbox" name="infolettre" value="oui"> Recevoir l'infolettre <input type="checkbox" name="interets[]" value="sirop"> Sirop <input type="checkbox" name="interets[]" value="miel"> Miel <input type="checkbox" name="interets[]" value="tire"> Tire <button type="submit">Envoyer</button>
[ "nom" => "Pacal Vincent", "courriel" => "pascal.vincent@exemple.test", "quantite" => "3", "interets" => [ "sirop", ],]
isset() ou de l'opérateur ?? avant de lire une case à cocher.Deux comportements méritent qu’on s’y arrête. Un nom terminé par [], comme interets[], indique à PHP de rassembler les valeurs répétées dans un tableau plutôt que de ne garder que la dernière. Et une case à cocher qui n’est pas cochée n’est pas transmise du tout : sa clé est absente du tableau, elle n’y figure pas à vide.
$nom = $_POST["nom"] ?? "";$infolettre = isset($_POST["infolettre"]);$interets = $_POST["interets"] ?? [];$_SERVER, le contexte de la requête
Section intitulée « $_SERVER, le contexte de la requête »$_SERVER ne contient pas les données saisies, mais les circonstances de la requête. Quelques entrées reviennent souvent.
| Entrée | Contenu |
|---|---|
REQUEST_METHOD |
la méthode employée, "GET" ou "POST" |
REQUEST_URI |
l’adresse demandée, chaîne de requête comprise |
SCRIPT_NAME |
le chemin du fichier PHP exécuté |
HTTP_USER_AGENT |
le navigateur annoncé par le client |
REMOTE_ADDR |
l’adresse IP à l’origine de la requête |
Une page qui se soumet à elle-même
Section intitulée « Une page qui se soumet à elle-même »Plutôt que de séparer le formulaire et son traitement en deux fichiers, on écrit souvent les deux dans le même, et REQUEST_METHOD sert à distinguer les deux passages.
-
Premier passage. La personne arrive par un lien : la requête est un GET, le formulaire s’affiche vide.
-
Deuxième passage. Elle envoie le formulaire : la requête est un POST vers la même adresse, et le script prend cette fois la branche de traitement.
<?php$erreurs = [];$soumis = $_SERVER["REQUEST_METHOD"] === "POST";
if ($soumis) { // normaliser, valider, puis agir}?>
<form method="post" action="">Valider côté serveur
Section intitulée « Valider côté serveur »Le balisage HTML sait déjà poser des contraintes : required, type="email", min et max. Elles rendent un vrai service, et il y a tout intérêt à les écrire : elles signalent le problème immédiatement, sans aller-retour vers le serveur, et guident la personne pendant qu’elle remplit.
Elles décrivent en revanche le comportement du navigateur. Une requête peut atteindre votre page sans être passée par lui : un outil en ligne de commande, un script, un formulaire recopié et modifié ailleurs produisent des requêtes tout aussi valides du point de vue du serveur.
required, type="email" et min="1" auraient refusées dans le navigateur. Elles arrivent quand même. Les contraintes du balisage guident la personne qui remplit le formulaire ; les règles du serveur, elles, s'appliquent à toutes les requêtes.Quatre familles de contrôles
Section intitulée « Quatre familles de contrôles »La plupart des validations se rangent dans l’une de ces catégories.
- Présence — le champ est-il rempli, une fois les espaces retirés ?
- Format ou type — la valeur ressemble-t-elle à un courriel, à un entier, à une date ?
- Plage ou longueur — le nombre est-il dans les bornes attendues, le texte pas trop long ?
- Cohérence — les deux mots de passe correspondent-ils, la date de fin suit-elle la date de début ?
On normalise avant de contrôler : retirer les espaces de début et de fin avec trim() évite de refuser un nom qui n’a que le tort d’avoir été copié-collé avec une espace.
$nom = trim($_POST["nom"] ?? "");$courriel = trim($_POST["courriel"] ?? "");$quantite = trim($_POST["quantite"] ?? "");$erreurs = [];
if ($nom === "") { $erreurs["nom"] = "Le nom est obligatoire.";}
if (!filter_var($courriel, FILTER_VALIDATE_EMAIL)) { $erreurs["courriel"] = "L'adresse ne ressemble pas à un courriel.";}
$q = filter_var($quantite, FILTER_VALIDATE_INT, [ "options" => ["min_range" => 1, "max_range" => 10],]);if ($q === false) { $erreurs["quantite"] = "La quantité doit se situer entre 1 et 10.";}
if (!isset($_POST["conditions"])) { $erreurs["conditions"] = "Les conditions doivent être acceptées.";}Valider n’est pas échapper
Section intitulée « Valider n’est pas échapper »Les deux opérations sont faciles à confondre, alors qu’elles répondent à des questions différentes et n’ont pas lieu au même moment.
| Validation | Échappement | |
|---|---|---|
| Question posée | cette valeur est-elle acceptable ? | comment l’écrire sans qu’elle soit interprétée ? |
| Moment | à la réception | à l’affichage |
| Résultat | un message d’erreur, ou rien | du texte transformé pour la sortie |
Une valeur peut être parfaitement valide et rester dangereuse à afficher telle quelle. htmlspecialchars() reste donc nécessaire au moment d’écrire dans la page, y compris pour des données qui ont passé toutes les validations.
Réafficher sans faire retaper
Section intitulée « Réafficher sans faire retaper »Quand des erreurs subsistent, le formulaire revient — et il gagne à revenir rempli. Retaper huit champs parce que l’un d’eux posait problème décourage vite.
<input type="text" name="nom" value="<?php echo htmlspecialchars($nom); ?>">
<?php if (isset($erreurs["nom"])): ?> <p class="erreur"><?php echo htmlspecialchars($erreurs["nom"]); ?></p><?php endif; ?>VérificationVos champs portent déjà required et le type email. Pourquoi revalider côté serveur ?
Parce que ces attributs décrivent ce que fait le navigateur avant d’envoyer. La requête, elle, peut être construite ailleurs : par un outil en ligne de commande, un script, ou une copie modifiée de votre formulaire. Le serveur est le seul endroit que toutes les requêtes traversent, quelles que soient leur provenance. Cela ne rend pas les contraintes HTML inutiles pour autant : elles restent le moyen le plus rapide de signaler un problème à la personne qui remplit.
Rediriger après traitement
Section intitulée « Rediriger après traitement »Une fois les données validées et l’action effectuée, il reste à répondre. Répondre directement par une page de confirmation pose un problème discret.
- Navigateur vers Serveur : POST /commande.php
- Serveur : enregistre la commande
- Serveur vers Navigateur : 200 · page de confirmation
- Navigateur vers Serveur : POST /commande.php (la personne rafraîchit la page)
- Serveur : enregistre une deuxième commande
- Serveur vers Navigateur : 200 · page de confirmation
La page affichée est la réponse à un POST. La rafraîchir peut demander au navigateur de renvoyer ce même POST, ce qui répète alors l’action. Une réponse de redirection règle la situation.
- Navigateur vers Serveur : POST /commande.php
- Serveur : enregistre la commande
- Serveur vers Navigateur : 303 · Location: merci.php
- Navigateur vers Serveur : GET /merci.php (suivi automatiquement par le navigateur)
- Serveur vers Navigateur : 200 · page de remerciement
- Navigateur vers Serveur : GET /merci.php (la personne rafraîchit : simple relecture)
- Serveur vers Navigateur : 200 · page de remerciement
Ce déroulement porte un nom : POST-Redirect-GET. Le POST agit, la redirection déplace le navigateur, et la page finalement affichée est le résultat d’un GET ordinaire — rejouable sans effet.
if ($soumis && empty($erreurs)) { // enregistrer la commande
header("Location: merci.php", true, 303); exit;}Le code 303 indique explicitement au navigateur d’aller chercher la nouvelle adresse en GET. Le code 302, plus ancien, est encore très répandu et fonctionne de la même façon dans la pratique.
Notez que l’on redirige en cas de succès seulement. En cas d’erreur, on réaffiche la page avec les messages, sans rediriger : la redirection ferait perdre $erreurs et les valeurs saisies, puisque le script suivant repartirait de zéro. Les transporter d’une requête à l’autre demanderait une session, que nous verrons plus loin.
Le trajet d’une soumission
Section intitulée « Le trajet d’une soumission »Ouverture sur les API
Section intitulée « Ouverture sur les API »Un formulaire n’est qu’une façon, parmi d’autres, d’envoyer des données. Une API reçoit des requêtes émises non par un navigateur, mais par un autre programme (application mobile, autre serveur, code JavaScript). Le principe reste le même — une requête, une méthode, une réponse — mais les données échangées sont généralement du JSON plutôt que du HTML.
<?php// Au lieu de renvoyer du HTML, on renvoie du JSON.header("Content-Type: application/json");echo json_encode(["statut" => "ok", "message" => "Reçu"]);<?php// Et on lit un corps JSON envoyé par le client (au lieu de $_POST).$donnees = json_decode(file_get_contents("php://input"), true);Réflexes et pièges
Section intitulée « Réflexes et pièges »- Un champ sans
namen’arrive pas au serveur, même s’il s’affiche et se remplit correctement. - Une case décochée est absente, pas vide :
isset()ou??avant de la lire. - Normaliser avant de valider : pour du texte,
trim()d’abord, contrôles ensuite. La normalisation dépend du type de donnée. - Les contraintes du balisage guident, les règles du serveur décident. Écrire les deux, se fier aux secondes.
- Valider et échapper sont deux moments distincts : à la réception pour l’un, à l’affichage pour l’autre.
- Rediriger après un POST réussi, avec
exitderrièreheader(), et sans la moindre sortie avant l’appel. - Réafficher les valeurs saisies en cas d’erreur, passées par
htmlspecialchars().
Récapitulatif
Section intitulée « Récapitulatif »- Les données d’une requête voyagent soit dans l’URL, soit dans le corps ; PHP les met à disposition dans
$_GETet$_POST, et décrit le contexte dans$_SERVER. - Ces trois tableaux reflètent la requête en cours et sont reconstruits à chaque nouvelle requête.
- Le choix entre GET et POST se raisonne à partir des conséquences : adresse partageable et rejouable d’un côté, action qu’il vaut mieux ne pas répéter de l’autre.
- L’attribut
named’un champ devient la clé dans le tableau reçu ; un nom terminé par[]produit un tableau de valeurs. - La validation côté serveur s’applique à toutes les requêtes, y compris à celles qui ne sont pas passées par le formulaire. Elle se distingue de l’échappement, qui intervient à l’affichage.
- Le patron POST-Redirect-GET évite qu’un rafraîchissement ne rejoue l’action, en répondant par une redirection plutôt que par une page.
Quiz. Une case à cocher nommée infolettre n'est pas cochée. Que contient $_POST['infolettre'] ?
Quiz. Vos champs portent déjà required et min='1'. Que peut-on en conclure sur les données reçues ?
Quiz. À quoi sert la redirection dans le patron POST-Redirect-GET ?
Quiz. Une donnée a passé toutes vos validations. Faut-il encore l'échapper avant de l'afficher ?