Formulaires Interactifs

Formulaires PHP

Maîtrisez les formulaires HTML/PHP pour créer des applications web interactives, de la création basique à la sécurisation avancée.

📝 Formulaires HTML
🔄 GET & POST
✅ Validation
🛡️ Sécurisation

Bases HTML des Formulaires

Comprenez la structure HTML fondamentale des formulaires avant d'ajouter la logique PHP.

1 Structure HTML de Base

Éléments essentiels :

<!-- Formulaire de base -->
<form method="POST" action="traitement.php">
<!-- Champ texte -->
<input type="text" name="nom" required>
<!-- Champ email -->
<input type="email" name="email" required>
<!-- Bouton d'envoi -->
<button type="submit">Envoyer</button>
</form>
Attributs importants :
  • method : GET ou POST
  • action : fichier PHP de traitement
  • name : identifiant pour PHP
  • required : champ obligatoire

Types de champs HTML5 :

type="text"
Texte simple (nom, prénom)
type="email"
Adresse email avec validation automatique
type="password"
Mot de passe masqué
type="number"
Nombres avec min/max
type="date"
Sélecteur de date
type="tel"
Numéro de téléphone
type="file"
Upload de fichiers

2 Éléments de Formulaire Avancés

Select et Options :

<!-- Liste déroulante -->
<select name="pays" required>
<option value="">Choisir un pays</option>
<option value="fr">France</option>
<option value="be">Belgique</option>
<option value="ch">Suisse</option>
</select>
<!-- Zone de texte -->
<textarea name="message" rows="4"
placeholder="Votre message...">
</textarea>

Checkboxes et Radio :

<!-- Cases à cocher -->
<input type="checkbox" name="hobbies[]"
value="sport" id="sport">
<label for="sport">Sport</label>
<input type="checkbox" name="hobbies[]"
value="lecture" id="lecture">
<label for="lecture">Lecture</label>
<!-- Boutons radio -->
<input type="radio" name="sexe"
value="M" id="homme">
<label for="homme">Homme</label>
<input type="radio" name="sexe"
value="F" id="femme">
<label for="femme">Femme</label>

GET vs POST : Récupération des Données

Découvrez la différence entre GET et POST et comment récupérer les données envoyées par un formulaire en PHP.

1 Méthode GET

HTML - Formulaire GET :

<!-- search.html -->
<form method="GET" action="recherche.php">
<label for="q">Rechercher :</label>
<input type="text" name="q" id="q"
placeholder="Votre recherche...">
<select name="categorie">
<option value="tous">Toutes catégories</option>
<option value="produits">Produits</option>
<option value="articles">Articles</option>
</select>
<button type="submit">Rechercher</button>
</form>
Caractéristiques GET :
  • • Données visibles dans l'URL
  • • Parfait pour recherches, filtres
  • • URLs partageables
  • • Limité en taille (~2000 caractères)
  • • Peut être mis en cache

PHP - Traitement GET :

// recherche.php
<?php
// Récupération des données GET
$recherche = $_GET['q'] ?? '';
$categorie = $_GET['categorie'] ?? 'tous';
// Nettoyage des données
$recherche = trim($recherche);
$recherche = htmlspecialchars($recherche);
// Affichage des résultats
if (!empty($recherche)) {
echo "<h2>Recherche : $recherche</h2>";
echo "<p>Catégorie : $categorie</p>";
// Logique de recherche ici...
$resultats = rechercher($recherche, $categorie);
foreach ($resultats as $resultat) {
echo "<div>$resultat</div>";
}
} else {
echo "<p>Veuillez saisir une recherche.</p>";
}
?>
URL résultante :
recherche.php?q=macbook&categorie=produits

2 Méthode POST

HTML - Formulaire POST :

<!-- contact.html -->
<form method="POST" action="contact.php">
<label for="nom">Nom complet :</label>
<input type="text" name="nom" id="nom" required>
<label for="email">Email :</label>
<input type="email" name="email" id="email" required>
<label for="message">Message :</label>
<textarea name="message" id="message"
rows="5" required></textarea>
<input type="checkbox" name="newsletter"
value="1" id="newsletter">
<label for="newsletter">Newsletter</label>
<button type="submit">Envoyer</button>
</form>

PHP - Traitement POST :

// contact.php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Récupération des données POST
$nom = trim($_POST['nom'] ?? '');
$email = trim($_POST['email'] ?? '');
$message = trim($_POST['message'] ?? '');
$newsletter = isset($_POST['newsletter']);
// Validation de base
if (empty($nom) || empty($email) || empty($message)) {
echo "Tous les champs sont obligatoires !";
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo "Email invalide !";
} else {
// Traitement réussi
echo "<h2>Message reçu !</h2>";
echo "<p>Merci $nom, nous vous répondrons à $email</p>";
if ($newsletter) {
echo "<p>✓ Inscription newsletter</p>";
}
}
}
?>
Caractéristiques POST :
  • • Données cachées (pas dans l'URL)
  • • Parfait pour formulaires sensibles
  • • Taille illimitée (dans les limites du serveur)
  • • Peut envoyer des fichiers
  • • Pas de cache navigateur

Validation et Sécurisation

Apprenez à valider et sécuriser les données de vos formulaires pour éviter les failles de sécurité.

1 Validation des Données

Fonctions de validation PHP :

<?php
// Validation email
function validerEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
// Validation nom (lettres + espaces)
function validerNom($nom) {
return preg_match('/^[a-zA-ZÀ-ÿ\s]{2,50}$/', $nom);
}
// Validation âge
function validerAge($age) {
$age = (int)$age;
return $age >= 0 && $age <= 120;
}
// Validation téléphone français
function validerTelephone($tel) {
$pattern = '/^(?:(?:\+|00)33|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/';
return preg_match($pattern, $tel);
}
?>

Validation complète d'un formulaire :

<?php
$erreurs = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Récupération et nettoyage
$nom = trim($_POST['nom'] ?? '');
$email = trim($_POST['email'] ?? '');
$age = $_POST['age'] ?? '';
// Validations
if (empty($nom)) {
$erreurs[] = "Le nom est obligatoire";
} elseif (!validerNom($nom)) {
$erreurs[] = "Nom invalide (2-50 caractères)";
}
if (empty($email)) {
$erreurs[] = "L'email est obligatoire";
} elseif (!validerEmail($email)) {
$erreurs[] = "Email invalide";
}
if (!empty($age) && !validerAge($age)) {
$erreurs[] = "Âge invalide (0-120)";
}
// Traitement si aucune erreur
if (empty($erreurs)) {
// Traitement des données valides...
echo "Formulaire validé !";
}
}
?>

2 Sécurisation des Données

Protection XSS :

<?php
// Échappement pour affichage HTML
function echapper($texte) {
return htmlspecialchars($texte, ENT_QUOTES, 'UTF-8');
}
// Nettoyage avancé
function nettoyer($texte) {
$texte = trim($texte);
$texte = stripslashes($texte);
$texte = strip_tags($texte);
return $texte;
}
// Exemple d'utilisation
$nom_utilisateur = nettoyer($_POST['nom']);
echo "Bonjour " . echapper($nom_utilisateur);
?>
Protection contre XSS :
  • • htmlspecialchars() pour l'affichage
  • • strip_tags() pour supprimer HTML
  • • filter_var() pour validation
  • • Toujours échapper avant affichage

Protection CSRF :

<?php
session_start();
// Génération token CSRF
function genererTokenCSRF() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// Vérification token CSRF
function verifierTokenCSRF($token) {
return isset($_SESSION['csrf_token']) &&
hash_equals($_SESSION['csrf_token'], $token);
}
// Traitement formulaire
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['csrf_token'] ?? '';
if (!verifierTokenCSRF($token)) {
die('Token CSRF invalide !');
}
// Traitement sécurisé...
}
?>
<!-- Dans le formulaire HTML -->
<input type="hidden" name="csrf_token"
value="<?= genererTokenCSRF() ?>">

Upload de Fichiers

Apprenez à gérer l'upload de fichiers de manière sécurisée avec PHP.

1 Upload de Fichier Simple

HTML - Formulaire d'upload :

<!-- IMPORTANT: enctype="multipart/form-data" -->
<form method="POST" action="upload.php"
enctype="multipart/form-data">
<label for="fichier">Choisir un fichier :</label>
<input type="file" name="fichier" id="fichier"
accept=".jpg,.jpeg,.png,.gif" required>
<label for="description">Description :</label>
<input type="text" name="description"
id="description" required>
<button type="submit">Uploader</button>
</form>
Points importants :
  • • enctype="multipart/form-data" obligatoire
  • • method="POST" requis pour les fichiers
  • • accept limite les types de fichiers
  • • Configuration php.ini à vérifier

PHP - Traitement de l'upload :

// upload.php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Vérifier si fichier uploadé
if (isset($_FILES['fichier']) && $_FILES['fichier']['error'] === 0) {
// Informations du fichier
$fichier = $_FILES['fichier'];
$nom_fichier = $fichier['name'];
$taille_fichier = $fichier['size'];
$type_fichier = $fichier['type'];
$fichier_temp = $fichier['tmp_name'];
// Validation de base
$extensions_autorisees = ['jpg', 'jpeg', 'png', 'gif'];
$extension = strtolower(pathinfo($nom_fichier, PATHINFO_EXTENSION));
if (in_array($extension, $extensions_autorisees)) {
// Dossier de destination
$dossier_upload = 'uploads/';
$nouveau_nom = uniqid() . '.' . $extension;
$chemin_destination = $dossier_upload . $nouveau_nom;
// Déplacer le fichier
if (move_uploaded_file($fichier_temp, $chemin_destination)) {
echo "Fichier uploadé avec succès !";
} else {
echo "Erreur lors de l'upload.";
}
} else {
echo "Extension non autorisée.";
}
}
}
?>

Projet Complet : Formulaire de Contact Avancé

Créons un formulaire de contact complet avec validation, sécurisation et persistance des données.

contact_complet.php - Formulaire Avancé

<?php
/**
 * Formulaire de Contact Avancé - Projet complet PHP
 * Fonctionnalités : validation, sécurisation, CSRF, upload, persistance
 */

session_start();

// CONFIGURATION
$dossier_uploads = 'uploads/';
$fichier_contacts = 'contacts.json';
$extensions_autorisees = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx'];
$taille_max = 5 * 1024 * 1024; // 5MB

// FONCTIONS UTILITAIRES
function genererTokenCSRF() {
    if (!isset($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function verifierTokenCSRF($token) {
    return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}

function echapper($texte) {
    return htmlspecialchars($texte, ENT_QUOTES, 'UTF-8');
}

function validerEmail($email) {
    return filter_var($email, FILTER_VALIDATE_EMAIL);
}

function validerNom($nom) {
    return preg_match('/^[a-zA-ZÀ-ÿ\s\'-]{2,50}$/', $nom);
}

function validerTelephone($tel) {
    $pattern = '/^(?:(?:\+|00)33|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/';
    return preg_match($pattern, $tel);
}

// VARIABLES
$erreurs = [];
$succes = '';
$nom = $prenom = $email = $telephone = $sujet = $message = '';
$priorite = 'normale';
$newsletter = false;
$fichier_uploade = '';

// TRAITEMENT DU FORMULAIRE
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    
    // Vérification CSRF
    $token = $_POST['csrf_token'] ?? '';
    if (!verifierTokenCSRF($token)) {
        $erreurs[] = "Token CSRF invalide. Veuillez recharger la page.";
    } else {
        
        // RÉCUPÉRATION ET NETTOYAGE DES DONNÉES
        $nom = trim($_POST['nom'] ?? '');
        $prenom = trim($_POST['prenom'] ?? '');
        $email = trim($_POST['email'] ?? '');
        $telephone = trim($_POST['telephone'] ?? '');
        $sujet = trim($_POST['sujet'] ?? '');
        $message = trim($_POST['message'] ?? '');
        $priorite = $_POST['priorite'] ?? 'normale';
        $newsletter = isset($_POST['newsletter']);
        
        // VALIDATIONS
        if (empty($nom)) {
            $erreurs[] = "Le nom est obligatoire.";
        } elseif (!validerNom($nom)) {
            $erreurs[] = "Le nom doit contenir 2-50 caractères (lettres, espaces, apostrophes).";
        }
        
        if (empty($prenom)) {
            $erreurs[] = "Le prénom est obligatoire.";
        } elseif (!validerNom($prenom)) {
            $erreurs[] = "Le prénom doit contenir 2-50 caractères (lettres, espaces, apostrophes).";
        }
        
        if (empty($email)) {
            $erreurs[] = "L'email est obligatoire.";
        } elseif (!validerEmail($email)) {
            $erreurs[] = "L'adresse email n'est pas valide.";
        }
        
        if (!empty($telephone) && !validerTelephone($telephone)) {
            $erreurs[] = "Le numéro de téléphone n'est pas valide.";
        }
        
        if (empty($sujet)) {
            $erreurs[] = "Le sujet est obligatoire.";
        } elseif (strlen($sujet) < 5 || strlen($sujet) > 100) {
            $erreurs[] = "Le sujet doit contenir entre 5 et 100 caractères.";
        }
        
        if (empty($message)) {
            $erreurs[] = "Le message est obligatoire.";
        } elseif (strlen($message) < 20 || strlen($message) > 1000) {
            $erreurs[] = "Le message doit contenir entre 20 et 1000 caractères.";
        }
        
        if (!in_array($priorite, ['faible', 'normale', 'haute'])) {
            $priorite = 'normale';
        }
        
        // TRAITEMENT DE L'UPLOAD (OPTIONNEL)
        if (isset($_FILES['piece_jointe']) && $_FILES['piece_jointe']['error'] === UPLOAD_ERR_OK) {
            $fichier = $_FILES['piece_jointe'];
            $nom_fichier = $fichier['name'];
            $taille_fichier = $fichier['size'];
            $fichier_temp = $fichier['tmp_name'];
            
            // Validation du fichier
            $extension = strtolower(pathinfo($nom_fichier, PATHINFO_EXTENSION));
            
            if (!in_array($extension, $extensions_autorisees)) {
                $erreurs[] = "Extension de fichier non autorisée. Extensions permises : " . implode(', ', $extensions_autorisees);
            } elseif ($taille_fichier > $taille_max) {
                $erreurs[] = "Le fichier est trop volumineux. Taille maximum : 5 MB.";
            } else {
                // Créer le dossier si nécessaire
                if (!is_dir($dossier_uploads)) {
                    mkdir($dossier_uploads, 0755, true);
                }
                
                // Nom sécurisé
                $nouveau_nom = uniqid('contact_') . '.' . $extension;
                $chemin_destination = $dossier_uploads . $nouveau_nom;
                
                if (move_uploaded_file($fichier_temp, $chemin_destination)) {
                    $fichier_uploade = $nouveau_nom;
                } else {
                    $erreurs[] = "Erreur lors de l'upload du fichier.";
                }
            }
        }
        
        // SAUVEGARDE SI AUCUNE ERREUR
        if (empty($erreurs)) {
            $contact = [
                'id' => uniqid(),
                'date' => date('Y-m-d H:i:s'),
                'nom' => $nom,
                'prenom' => $prenom,
                'email' => $email,
                'telephone' => $telephone,
                'sujet' => $sujet,
                'message' => $message,
                'priorite' => $priorite,
                'newsletter' => $newsletter,
                'fichier' => $fichier_uploade,
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'inconnue'
            ];
            
            // Lire les contacts existants
            $contacts = [];
            if (file_exists($fichier_contacts)) {
                $json = file_get_contents($fichier_contacts);
                $contacts = json_decode($json, true) ?: [];
            }
            
            // Ajouter le nouveau contact
            $contacts[] = $contact;
            
            // Sauvegarder
            if (file_put_contents($fichier_contacts, json_encode($contacts, JSON_PRETTY_PRINT))) {
                $succes = "Votre message a été envoyé avec succès ! Nous vous répondrons dans les plus brefs délais.";
                
                // Réinitialiser le formulaire
                $nom = $prenom = $email = $telephone = $sujet = $message = '';
                $priorite = 'normale';
                $newsletter = false;
                
                // Régénérer le token CSRF
                unset($_SESSION['csrf_token']);
            } else {
                $erreurs[] = "Erreur lors de la sauvegarde. Veuillez réessayer.";
            }
        }
    }
}
?>

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Formulaire de Contact Avancé - PHP</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 800px;
            margin: 0 auto;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }
        
        .header {
            background: linear-gradient(135deg, #8b5cf6, #3b82f6);
            color: white;
            padding: 40px;
            text-align: center;
        }
        
        .header h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
        }
        
        .form-container {
            padding: 40px;
        }
        
        .alert {
            padding: 15px;
            margin-bottom: 20px;
            border-radius: 8px;
            font-weight: 500;
        }
        
        .alert-success {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        
        .alert-error {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        
        .form-group {
            margin-bottom: 25px;
        }
        
        .form-row {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }
        
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
            color: #333;
        }
        
        .required {
            color: #e74c3c;
        }
        
        input, select, textarea {
            width: 100%;
            padding: 12px 15px;
            border: 2px solid #e1e5e9;
            border-radius: 8px;
            font-size: 16px;
            transition: border-color 0.3s, box-shadow 0.3s;
        }
        
        input:focus, select:focus, textarea:focus {
            outline: none;
            border-color: #8b5cf6;
            box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
        }
        
        .checkbox-group {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .checkbox-group input[type="checkbox"] {
            width: auto;
        }
        
        .priority-group {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 10px;
        }
        
        .priority-option {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 10px;
            border: 2px solid #e1e5e9;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s;
        }
        
        .priority-option:hover {
            border-color: #8b5cf6;
            background: #f8f7ff;
        }
        
        .priority-option input[type="radio"] {
            width: auto;
        }
        
        .priority-option.faible { border-color: #10b981; }
        .priority-option.normale { border-color: #3b82f6; }
        .priority-option.haute { border-color: #ef4444; }
        
        .file-input {
            position: relative;
            overflow: hidden;
            display: inline-block;
            cursor: pointer;
            background: #f8f9fa;
            border: 2px dashed #dee2e6;
            border-radius: 8px;
            padding: 30px;
            text-align: center;
            width: 100%;
            transition: all 0.3s;
        }
        
        .file-input:hover {
            border-color: #8b5cf6;
            background: #f8f7ff;
        }
        
        .file-input input[type="file"] {
            position: absolute;
            left: -9999px;
        }
        
        .submit-btn {
            background: linear-gradient(135deg, #8b5cf6, #3b82f6);
            color: white;
            padding: 15px 40px;
            border: none;
            border-radius: 10px;
            font-size: 18px;
            font-weight: 600;
            cursor: pointer;
            transition: transform 0.2s, box-shadow 0.3s;
            width: 100%;
        }
        
        .submit-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 20px rgba(139, 92, 246, 0.3);
        }
        
        .stats {
            background: #f8f9fa;
            padding: 20px;
            border-radius: 8px;
            margin-top: 30px;
            text-align: center;
            color: #666;
            font-size: 14px;
        }
        
        @media (max-width: 768px) {
            .form-row {
                grid-template-columns: 1fr;
            }
            
            .priority-group {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>

<div class="container">
    <div class="header">
        <h1>📧 Contact Avancé</h1>
        <p>Formulaire complet avec validation, sécurisation et upload</p>
    </div>
    
    <div class="form-container">
        <?php if ($succes): ?>
            <div class="alert alert-success">
                ✅ <?= echapper($succes) ?>
            </div>
        <?php endif; ?>
        
        <?php if (!empty($erreurs)): ?>
            <div class="alert alert-error">
                ❌ <strong>Erreurs détectées :</strong>
                <ul style="margin-top: 10px; padding-left: 20px;">
                    <?php foreach ($erreurs as $erreur): ?>
                        <li><?= echapper($erreur) ?></li>
                    <?php endforeach; ?>
                </ul>
            </div>
        <?php endif; ?>
        
        <form method="POST" enctype="multipart/form-data">
            <input type="hidden" name="csrf_token" value="<?= genererTokenCSRF() ?>">
            
            <div class="form-row">
                <div class="form-group">
                    <label for="nom">Nom <span class="required">*</span></label>
                    <input type="text" id="nom" name="nom" value="<?= echapper($nom) ?>" 
                           placeholder="Votre nom de famille" required>
                </div>
                
                <div class="form-group">
                    <label for="prenom">Prénom <span class="required">*</span></label>
                    <input type="text" id="prenom" name="prenom" value="<?= echapper($prenom) ?>" 
                           placeholder="Votre prénom" required>
                </div>
            </div>
            
            <div class="form-row">
                <div class="form-group">
                    <label for="email">Email <span class="required">*</span></label>
                    <input type="email" id="email" name="email" value="<?= echapper($email) ?>" 
                           placeholder="votre@email.com" required>
                </div>
                
                <div class="form-group">
                    <label for="telephone">Téléphone</label>
                    <input type="tel" id="telephone" name="telephone" value="<?= echapper($telephone) ?>" 
                           placeholder="01 23 45 67 89">
                </div>
            </div>
            
            <div class="form-group">
                <label for="sujet">Sujet <span class="required">*</span></label>
                <input type="text" id="sujet" name="sujet" value="<?= echapper($sujet) ?>" 
                       placeholder="Objet de votre message" required>
            </div>
            
            <div class="form-group">
                <label>Priorité</label>
                <div class="priority-group">
                    <label class="priority-option faible">
                        <input type="radio" name="priorite" value="faible" <?= $priorite === 'faible' ? 'checked' : '' ?>>
                        🟢 Faible
                    </label>
                    <label class="priority-option normale">
                        <input type="radio" name="priorite" value="normale" <?= $priorite === 'normale' ? 'checked' : '' ?>>
                        🔵 Normale
                    </label>
                    <label class="priority-option haute">
                        <input type="radio" name="priorite" value="haute" <?= $priorite === 'haute' ? 'checked' : '' ?>>
                        🔴 Haute
                    </label>
                </div>
            </div>
            
            <div class="form-group">
                <label for="message">Message <span class="required">*</span></label>
                <textarea id="message" name="message" rows="6" 
                          placeholder="Votre message détaillé (20-1000 caractères)" required><?= echapper($message) ?></textarea>
            </div>
            
            <div class="form-group">
                <label>Pièce jointe (optionnel)</label>
                <div class="file-input">
                    <input type="file" id="piece_jointe" name="piece_jointe" 
                           accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.docx">
                    📎 Cliquez pour choisir un fichier<br>
                    <small>Formats acceptés : JPG, PNG, GIF, PDF, DOC, DOCX (max 5MB)</small>
                </div>
            </div>
            
            <div class="form-group">
                <div class="checkbox-group">
                    <input type="checkbox" id="newsletter" name="newsletter" value="1" 
                           <?= $newsletter ? 'checked' : '' ?>>
                    <label for="newsletter">📧 Je souhaite recevoir la newsletter</label>
                </div>
            </div>
            
            <button type="submit" class="submit-btn">
                📤 Envoyer le message
            </button>
        </form>
        
        <div class="stats">
            <strong>Fonctionnalités intégrées :</strong><br>
            ✅ Validation côté serveur • 🛡️ Protection CSRF • 📎 Upload de fichiers • 
            💾 Sauvegarde JSON • 🔒 Sécurisation XSS • 📱 Design responsive
        </div>
    </div>
</div>

<script>
// Affichage du nom du fichier sélectionné
document.getElementById('piece_jointe').addEventListener('change', function(e) {
    const fileName = e.target.files[0]?.name || 'Aucun fichier sélectionné';
    const fileInput = e.target.closest('.file-input');
    fileInput.innerHTML = `📎 ${fileName}<br><small>Cliquez pour changer</small>`;
    fileInput.appendChild(e.target);
});

// Compteur de caractères pour le message
document.getElementById('message').addEventListener('input', function(e) {
    const count = e.target.value.length;
    const max = 1000;
    const min = 20;
    
    let color = '#666';
    if (count < min) color = '#ef4444';
    else if (count > max * 0.9) color = '#f59e0b';
    else color = '#10b981';
    
    // Afficher le compteur (si vous voulez l'ajouter)
    console.log(`Caractères: ${count}/${max}`);
});
</script>

</body>
</html>

Instructions d'Installation

  1. 1.
    Créer le dossier du projet :
    mkdir contact_avance && cd contact_avance
  2. 2.
    Créer le fichier :
    Copier le code dans contact_complet.php
  3. 3.
    Créer le dossier uploads :
    mkdir uploads (permissions 755)
  4. 4.
    Lancer le serveur :
    php -S localhost:8000
  5. 5.
    Tester :
    Ouvrir http://localhost:8000/contact_complet.php

Fonctionnalités Intégrées

Validation Complète

Email, nom, téléphone, longueur des messages

Sécurisation

Protection CSRF, XSS, validation stricte

Upload de Fichiers

Images, PDF, documents avec validation taille/type

Persistance JSON

Sauvegarde des contacts avec métadonnées

UX Avancée

Design responsive, animations, feedback utilisateur

Maîtrisez les Formulaires PHP !

Vous savez maintenant créer des formulaires sécurisés et interactifs. Passez à l'étape suivante : les bases de données !

🎯 Ce que vous savez maintenant :

Structure HTML • Méthodes GET/POST • Validation PHP • Sécurisation XSS/CSRF • Upload de fichiers • Gestion d'erreurs • Persistance des données