0 XP
?
Intermédiaire40 min25 XP

Objectifs de cette leçon

  • Récupérer les données POST et GET
  • Valider les entrées utilisateur
  • Sécuriser l'affichage avec htmlspecialchars

Formulaires HTML et PHP 📝

Les formulaires sont le pont entre l'utilisateur et le serveur. PHP excelle pour traiter les données de formulaires de manière sécurisée.

Structure de base d'un formulaire

<!-- index.php -->
<form method="POST" action="traitement.php">
    <label for="nom">Nom :</label>
    <input type="text" id="nom" name="nom" required>

    <label for="email">Email :</label>
    <input type="email" id="email" name="email" required>

    <label for="message">Message :</label>
    <textarea id="message" name="message" required></textarea>

    <button type="submit">Envoyer</button>
</form>

💡 Règle d'or : L'attribut name est CE QUI ENVOIE les données au serveur. Sans name, la donnée n'est pas transmise.

Récupération des données

<?php
// traitement.php

// Vérifier si le formulaire a été soumis
if ($_SERVER["REQUEST_METHOD"] === "POST") {

    // Récupérer les données (avec ?? pour éviter les erreurs)
    $nom = $_POST["nom"] ?? "";
    $email = $_POST["email"] ?? "";
    $message = $_POST["message"] ?? "";

    // Validation de base
    if (empty($nom) || empty($email) || empty($message)) {
        echo "Tous les champs sont obligatoires.";
        exit;
    }

    // Afficher les données reçues
    echo "Nom : " . htmlspecialchars($nom) . "\n";
    echo "Email : " . htmlspecialchars($email) . "\n";
    echo "Message : " . htmlspecialchars($message) . "\n";
}
?>

Types de champs de formulaire

<?php
// Champ texte
<input type="text" name="prenom" value="<?= $_GET['prenom'] ?? '' ?>">

// Email avec validation HTML5
<input type="email" name="email" required>

// Mot de passe
<input type="password" name="mdp" minlength="8" required>

// Nombre
<input type="number" name="age" min="0" max="120" step="1">

// Date
<input type="date" name="naissance">

// Sélection (dropdown)
<select name="pays">
    <option value="">-- Choisir --</option>
    <option value="FR">France</option>
    <option value="BE">Belgique</option>
    <option value="CH">Suisse</option>
</select>

// Boutons radio
<input type="radio" name="genre" value="H" id="h">
<label for="h">Homme</label>
<input type="radio" name="genre" value="F" id="f">
<label for="f">Femme</label>

// Cases à cocher
<input type="checkbox" name="conditions" value="accepte" id="c">
<label for="c">J'accepte les conditions</label>

// Textarea
<textarea name="commentaire" rows="5" cols="40"></textarea>

// Fichier
<input type="file" name="avatar" accept="image/*">
?>

Sécurité — Les 3 règles d'or

<?php
// 1. TOUJOURS valider les données côté serveur
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if (!$email) {
    die("Email invalide");
}

// 2. TOUJOURS échapper la sortie (protection XSS)
echo htmlspecialchars($donneeUtilisateur, ENT_QUOTES, 'UTF-8');

// 3. TOUJOURS utiliser des requêtes préparées pour la base de données
$stmt = $pdo->prepare("INSERT INTO messages (nom, email, message) VALUES (?, ?, ?)");
$stmt->execute([$nom, $email, $message]);
?>

⚠️ XSS : Si tu affiches du contenu utilisateur sans htmlspecialchars(), quelqu'un peut injecter du JavaScript malveillant dans ta page.

Protection CSRF

<?php
session_start();

// Générer un token CSRF
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Dans le formulaire HTML :
// <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">

// Vérifier le token à la réception
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    if (!isset($_POST['csrf_token']) ||
        $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        die("Token CSRF invalide — tentative d'attaque ?");
    }
    // Traitement...
}
?>

Traitement d'un formulaire complet

<?php
// contact.php — formulaire complet sécurisé

session_start();

// Générer le token
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

$erreurs = [];
$succes = false;

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    // Vérifier CSRF
    if ($_POST['csrf_token'] ?? '' !== $_SESSION['csrf_token']) {
        die("Token invalide");
    }

    // Récupérer et nettoyer
    $nom = trim($_POST['nom'] ?? '');
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    $sujet = trim($_POST['sujet'] ?? '');
    $message = trim($_POST['message'] ?? '');

    // Valider
    if (empty($nom)) $erreurs[] = "Le nom est requis";
    if (!$email) $erreurs[] = "L'email est invalide";
    if (empty($sujet)) $erreurs[] = "Le sujet est requis";
    if (strlen($message) < 10) $erreurs[] = "Le message doit faire 10 caractères minimum";

    // Si pas d'erreurs, traiter
    if (empty($erreurs)) {
        // Envoyer l'email
        $destinataire = "contact@monsite.com";
        $headers = "From: $email\r\n";
        $headers .= "Reply-To: $email\r\n";
        $headers .= "Content-Type: text/plain; charset=UTF-8\r\n";

        mail($destinataire, $sujet, $message, $headers);
        $succes = true;

        // Regénérer le token
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
}
?>

Réception de fichier upload

<?php
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_FILES['avatar'])) {

    $fichier = $_FILES['avatar'];

    // Vérifier les erreurs
    if ($fichier['error'] !== UPLOAD_ERR_OK) {
        die("Erreur upload : " . $fichier['error']);
    }

    // Vérifier la taille (max 5 Mo)
    if ($fichier['size'] > 5 * 1024 * 1024) {
        die("Fichier trop volumineux (max 5 Mo)");
    }

    // Vérifier le type MIME
    $typesPermis = ['image/jpeg', 'image/png', 'image/gif'];
    $typeMime = mime_content_type($fichier['tmp_name']);
    if (!in_array($typeMime, $typesPermis)) {
        die("Type de fichier non autorisé");
    }

    // Déplacer le fichier
    $dossier = "uploads/";
    $nomFichier = uniqid() . "_" . basename($fichier['name']);
    move_uploaded_file($fichier['tmp_name'], $dossier . $nomFichier);

    echo "Fichier uploadé avec succès !";
}
?>

Comparaison avec JavaScript (fetch API)

AspectJavaScriptPHP
Soumissionfetch() ou XMLHttpRequestFormulaire HTML classique
ValidationCôté client (JS)Côté serveur (PHP)
SécuritéJS désactivableIncontournable côté serveur
FeedbackAsynchroneSynchrone (rechargement)

💡 Astuce : Utilise JavaScript pour la validation instantanée côté client (meilleure UX), mais TOUJOURS valider à nouveau côté serveur avec PHP.

Exercice pratique

Crée un formulaire d'inscription sécurisé avec :

  1. Champs : nom, email, mot de passe, confirmation
  2. Validation complète côté serveur
  3. Protection CSRF
  4. Affichage des erreurs
  5. Message de succès
<?php
session_start();
$csrf = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $csrf;

$erreurs = [];

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $nom = trim($_POST['nom'] ?? '');
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    $mdp = $_POST['mdp'] ?? '';
    $mdp2 = $_POST['mdp2'] ?? '';

    if (empty($nom)) $erreurs[] = "Nom requis";
    if (!$email) $erreurs[] = "Email invalide";
    if (strlen($mdp) < 8) $erreurs[] = "Mot de passe trop court (8 min)";
    if ($mdp !== $mdp2) $erreurs[] = "Les mots de passe ne correspondent pas";

    if (empty($erreurs)) {
        $hash = password_hash($mdp, PASSWORD_DEFAULT);
        echo "Inscription réussie pour $nom !";
    }
}
?>

🎯 A retenir : Sécurité d'abord — validation serveur, échappement de sortie, tokens CSRF, et requêtes préparées. Ne JAMAIS faire confiance aux données du client.


Prochaine leçon : Sessions et cookies en PHP — garde l'utilisateur connecté !

⚡ Simulateur — Formulaires PHP

Étape 1/6
📝 Pseudo-code
1<?php
2if ($_SERVER["REQUEST_METHOD"] === "POST") {
3 $nom = $_POST["nom"];
4 echo "Bonjour $nom";
5}
6?>
7<form method="POST">
8<input name="nom" />
9</form>
⚙️ Exécution
📦 Mémoire
Aucune variable
🔀 Flux d'exécution
<?php
3
Début du script

Ouverture du bloc PHP pour traiter le formulaire.