Construire un gestionnaire de formulaires réutilisable

Présentation

Dans le chapitre précédent, le formulaire fonctionnait déjà correctement. Les données étaient lues côté serveur, validées, puis les messages d'erreur étaient affichés au bon endroit. En cas d'erreur, les valeurs déjà saisies pouvaient aussi être réaffichées dans les champs.

Cette solution est déjà sérieuse et suffisante pour de petits formulaires. Pourtant, dès que le nombre de champs augmente, le code commence à se répéter. Les mêmes étapes reviennent plusieurs fois, avec seulement quelques variations selon le champ concerné.

Le but de ce chapitre n'est donc pas de repartir de zéro, mais d'améliorer progressivement une base existante. On part d'un formulaire qui fonctionne, on repère les répétitions, puis on les réduit couche par couche jusqu'à obtenir une structure plus lisible, plus maintenable et plus réutilisable.

Cette progression est importante. Un gestionnaire de formulaires ne doit pas apparaître comme une solution magique tombée du ciel. Il doit être compris comme une réponse progressive à un vrai problème de répétition dans le code.

Observer ce qui se répète dans la vue

Pour garder un lien direct avec le chapitre précédent, repartons du formulaire de contact déjà construit. Celui-ci contient quatre champs classiques :

  • nom
  • prenom
  • email
  • message

Fichier index.php


                    <?php require_once __DIR__ . '/traitements/traiter-formulaire-contact.php'; ?>

                    <!DOCTYPE html>
                    <html lang="fr">
                    <head>
                        <meta charset="UTF-8">
                        <meta name="viewport" content="width=device-width, initial-scale=1.0">
                        <title>Contact</title>
                    </head>
                    <body>
                        <form method="post">
                            <div>
                                <label for="nom">Nom</label>
                                <input
                                    type="text"
                                    id="nom"
                                    name="nom"
                                    minlength="2"
                                    maxlength="100"
                                    value="<?= htmlspecialchars($anciennesValeurs['nom'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"
                                    required
                                >

                                <div class="message-erreur">
                                    <?= $erreurs['nom'] ?? '' ?>
                                </div>
                            </div>

                            <div>
                                <label for="prenom">Prénom</label>
                                <input
                                    type="text"
                                    id="prenom"
                                    name="prenom"
                                    maxlength="255"
                                    value="<?= htmlspecialchars($anciennesValeurs['prenom'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"
                                >

                                <div class="message-erreur">
                                    <?= $erreurs['prenom'] ?? '' ?>
                                </div>
                            </div>

                            <div>
                                <label for="email">Email</label>
                                <input
                                    type="email"
                                    id="email"
                                    name="email"
                                    value="<?= htmlspecialchars($anciennesValeurs['email'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"
                                    required
                                >

                                <div class="message-erreur">
                                    <?= $erreurs['email'] ?? '' ?>
                                </div>
                            </div>

                            <div>
                                <label for="message">Message</label>
                                <textarea
                                    id="message"
                                    name="message"
                                    minlength="10"
                                    maxlength="3000"
                                    required
                                ><?= htmlspecialchars($anciennesValeurs['message'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></textarea>

                                <div class="message-erreur">
                                    <?= $erreurs['message'] ?? '' ?>
                                </div>
                            </div>

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

                            <div class="message-global">
                                <?= $messageGlobal ?? '' ?>
                            </div>
                        </form>
                    </body>
                    </html>
                

Ici, la répétition la plus visible concerne le réaffichage des anciennes valeurs dans les champs du formulaire. Lorsqu'une erreur est détectée côté serveur, la page est réaffichée, et l'utilisateur ne doit pas être obligé de tout retaper. On réinjecte donc les anciennes valeurs dans les champs concernés.

Or, cette réinjection ne peut pas se faire n'importe comment. Une valeur saisie par l'utilisateur peut contenir des caractères spéciaux comme <, >, " ou '. Si on les affiche tels quels dans le HTML, le navigateur pourrait les interpréter comme du code HTML au lieu de les afficher comme du simple texte. C'est pour cette raison qu'on utilise htmlspecialchars().

Dans notre vue actuelle, cette même opération revient plusieurs fois :

  • lire une ancienne valeur dans $anciennesValeurs
  • utiliser une chaîne vide si la clé n'existe pas
  • passer le résultat dans htmlspecialchars()
  • réutiliser toujours les mêmes paramètres ENT_QUOTES | ENT_SUBSTITUTE et 'UTF-8'

Par exemple, on retrouve cette logique pour nom, prenom, email et message.


                    value="<?= htmlspecialchars($anciennesValeurs['nom'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"

                    value="<?= htmlspecialchars($anciennesValeurs['prenom'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"

                    value="<?= htmlspecialchars($anciennesValeurs['email'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"

                    <?= htmlspecialchars($anciennesValeurs['message'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>
                

Le code est correct. Pourtant, la même opération revient plusieurs fois avec très peu de variations. C'est précisément le genre de situation dans laquelle une petite fonction utilitaire peut améliorer la lisibilité sans changer le comportement.

Cette observation est intéressante sur le plan pédagogique. Elle montre déjà une idée importante du chapitre. Une fonction ne sert pas seulement à faire des calculs compliqués. Elle peut aussi servir à nommer proprement une petite intention répétée.

Première amélioration, créer une fonction d'échappement

Pour alléger la vue, on peut créer une petite fonction utilitaire chargée d'échapper une valeur avant son affichage dans le HTML.

Comme le fichier index.php charge d'abord /traitements/traiter-formulaire-contact.php, cette petite fonction peut être placée dans ce fichier de traitement, avant le bloc if ($_SERVER['REQUEST_METHOD'] === 'POST'). Elle sera alors disponible dans la vue au moment de l'affichage.

Fichier /traitements/traiter-formulaire-contact.php


                    <?php

                    function e(string $valeur): string
                    {
                        return htmlspecialchars($valeur, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
                    }

                    $erreurs = [];
                    $messageGlobal = '';
                    $anciennesValeurs = [];

                    if ($_SERVER['REQUEST_METHOD'] === 'POST')
                    {
                        // ...
                    }
                

Le nom e est volontairement court. Il signifie ici échapper pour affichage HTML. Cette fonction ne valide pas une donnée, ne la nettoie pas pour la base de données, et ne protège pas tout le programme à elle seule. Son rôle est beaucoup plus précis. Elle sert uniquement à préparer une valeur pour l'affichage dans une page HTML.

Le paramètre est déclaré comme string. Cela signifie que la fonction attend une chaîne de caractères. La gestion de la clé absente reste faite au moment de lire dans $anciennesValeurs, avec l'opérateur ??. Cette séparation est plus claire. La vue gère l'absence éventuelle d'une ancienne valeur, puis la fonction e() se charge uniquement de l'échappement HTML.

Le comportement reste donc le même qu'avant, mais le code devient plus léger à lire.

Dans la vue, on peut alors remplacer ceci :


                    value="<?= htmlspecialchars($anciennesValeurs['nom'] ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>"
                

par ceci :


                    value="<?= e($anciennesValeurs['nom'] ?? '') ?>"
                

On peut faire de même pour les autres champs :

Fichier index.php


                    <?php require_once __DIR__ . '/traitements/traiter-formulaire-contact.php'; ?>

                    <!DOCTYPE html>
                    <html lang="fr">
                    <head>
                        <meta charset="UTF-8">
                        <meta name="viewport" content="width=device-width, initial-scale=1.0">
                        <title>Contact</title>
                    </head>
                    <body>
                        <form method="post">
                            <div>
                                <label for="nom">Nom</label>
                                <input
                                    type="text"
                                    id="nom"
                                    name="nom"
                                    minlength="2"
                                    maxlength="100"
                                    value="<?= e($anciennesValeurs['nom'] ?? '') ?>"
                                    required
                                >

                                <div class="message-erreur">
                                    <?= $erreurs['nom'] ?? '' ?>
                                </div>
                            </div>

                            <div>
                                <label for="prenom">Prénom</label>
                                <input
                                    type="text"
                                    id="prenom"
                                    name="prenom"
                                    maxlength="255"
                                    value="<?= e($anciennesValeurs['prenom'] ?? '') ?>"
                                >

                                <div class="message-erreur">
                                    <?= $erreurs['prenom'] ?? '' ?>
                                </div>
                            </div>

                            <div>
                                <label for="email">Email</label>
                                <input
                                    type="email"
                                    id="email"
                                    name="email"
                                    value="<?= e($anciennesValeurs['email'] ?? '') ?>"
                                    required
                                >

                                <div class="message-erreur">
                                    <?= $erreurs['email'] ?? '' ?>
                                </div>
                            </div>

                            <div>
                                <label for="message">Message</label>
                                <textarea
                                    id="message"
                                    name="message"
                                    minlength="10"
                                    maxlength="3000"
                                    required
                                ><?= e($anciennesValeurs['message'] ?? '') ?></textarea>

                                <div class="message-erreur">
                                    <?= $erreurs['message'] ?? '' ?>
                                </div>
                            </div>

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

                            <div class="message-global">
                                <?= $messageGlobal ?? '' ?>
                            </div>
                        </form>
                    </body>
                    </html>
                

Le gain peut sembler modeste, mais il est réel. Le code de la vue devient plus agréable à parcourir, car le regard est moins encombré par la répétition de la même fonction longue et de ses mêmes paramètres. À la place, on lit plus directement l'intention. Ici, on comprend simplement qu'on affiche une valeur échappée.

Cette amélioration reste volontairement limitée. On ne cherche pas encore à rendre toute la vue réutilisable. On montre simplement qu'une petite abstraction bien choisie peut déjà améliorer la lisibilité sans compliquer le projet.

Maintenant que cette première répétition visible a été traitée dans la vue, on peut se concentrer sur les répétitions plus nombreuses et plus structurantes du traitement.

Observer ce qui se répète dans le traitement

Maintenant que la vue a été légèrement allégée, on peut se concentrer sur le traitement côté serveur. C'est là que les répétitions les plus nombreuses apparaissent.

Le traitement côté serveur :

  • lit les valeurs
  • vérifie leur validité
  • prépare les messages d'erreur
  • prépare le message de statut de soumission du formulaire
  • prépare les anciennes valeurs si le formulaire contient des erreurs

Fichier /traitements/traiter-formulaire-contact.php


                    <?php

                    function e(string $valeur): string
                    {
                        return htmlspecialchars($valeur, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
                    }

                    $erreurs = [];
                    $messageGlobal = '';
                    $anciennesValeurs = [];

                    if ($_SERVER['REQUEST_METHOD'] === 'POST')
                    {
                        $nom = trim($_POST['nom'] ?? '');
                        $prenom = trim($_POST['prenom'] ?? '');
                        $email = trim($_POST['email'] ?? '');
                        $message = trim($_POST['message'] ?? '');

                        if ($nom === '')
                        {
                            $erreurs['nom'] = 'Le nom est requis.';
                        }
                        elseif (mb_strlen($nom) < 2 || mb_strlen($nom) > 100)
                        {
                            $erreurs['nom'] = 'Le nom doit contenir entre 2 et 100 caractères.';
                        }

                        if ($prenom !== '' && mb_strlen($prenom) > 255)
                        {
                            $erreurs['prenom'] = 'Le prénom ne peut pas dépasser 255 caractères.';
                        }

                        if ($email === '')
                        {
                            $erreurs['email'] = 'L\'email est requis.';
                        }
                        elseif (!filter_var($email, FILTER_VALIDATE_EMAIL))
                        {
                            $erreurs['email'] = 'Veuillez saisir une adresse email valide.';
                        }

                        if ($message === '')
                        {
                            $erreurs['message'] = 'Le message est requis.';
                        }
                        elseif (mb_strlen($message) < 10 || mb_strlen($message) > 3000)
                        {
                            $erreurs['message'] = 'Le message doit contenir entre 10 et 3000 caractères.';
                        }

                        if (empty($erreurs))
                        {
                            $messageGlobal = 'Le formulaire a bien été envoyé.';
                        }
                        else
                        {
                            $messageGlobal = 'Le formulaire contient des erreurs.';

                            $anciennesValeurs['nom'] = $nom;
                            $anciennesValeurs['prenom'] = $prenom;
                            $anciennesValeurs['email'] = $email;
                            $anciennesValeurs['message'] = $message;
                        }
                    }
                

Ce code est correct. Il est lisible, il valide les données et il offre déjà un comportement propre pour l'utilisateur. Le but n'est donc pas de le remplacer parce qu'il serait mauvais, mais parce qu'il va devenir lourd si l'on continue à ajouter des champs du même type.

Pour chaque champ, on retrouve souvent les mêmes étapes :

  • lire la valeur dans $_POST
  • appliquer trim()
  • tester si la valeur est vide ou non
  • vérifier éventuellement une longueur minimale ou maximale
  • vérifier parfois un format particulier, comme un e-mail
  • ajouter un message dans $erreurs

À cela s'ajoute une autre répétition en fin de traitement, lorsqu'il faut recopier manuellement chaque valeur dans $anciennesValeurs si le formulaire contient des erreurs. Pour quatre champs, c'est supportable. Pour dix ou quinze champs, le fichier devient beaucoup plus long et difficile à maintenir, et le risque augmente d'oublier un champ, de recopier une logique avec une petite différence involontaire, ou de devoir modifier la même idée à plusieurs endroits différents.

C'est précisément ce type de situation qui justifie une optimisation progressive.

Deuxième amélioration, extraire la lecture et le nettoyage

Après avoir légèrement allégé la vue, on peut revenir au traitement. La répétition la plus facile à réduire côté serveur concerne la lecture des valeurs envoyées par le formulaire. Jusqu'ici, on écrivait plusieurs fois des expressions comme trim($_POST['nom'] ?? ''), trim($_POST['email'] ?? '') ou trim($_POST['message'] ?? '').

On peut regrouper cette logique dans une petite fonction dédiée.


                    <?php

                    function lireChampTexte(array $donnees, string $nomChamp): string
                    {
                        return trim($donnees[$nomChamp] ?? '');
                    }
                

Le premier argument représente ici la source des données, par exemple $_POST. Le second indique le nom du champ à lire.

La fonction retourne toujours une chaîne. Si la clé n'existe pas dans le tableau reçu, elle retourne une chaîne vide. Sinon, elle retourne la valeur nettoyée avec trim().

Le traitement devient déjà un peu plus compact :


                    <?php

                    if ($_SERVER['REQUEST_METHOD'] === 'POST')
                    {
                        $nom = lireChampTexte($_POST, 'nom');
                        $prenom = lireChampTexte($_POST, 'prenom');
                        $email = lireChampTexte($_POST, 'email');
                        $message = lireChampTexte($_POST, 'message');
                    }
                

Cette deuxième amélioration peut sembler modeste, mais elle est importante. Elle montre qu'on commence à nommer une intention. Ici, l'intention est de lire proprement un champ textuel. Le code devient donc un peu plus expressif.

Troisième amélioration, extraire des validations fréquentes

Une fois la lecture des valeurs centralisée, on peut extraire quelques vérifications qui reviennent souvent.

Par exemple :

  • vérifier qu'une valeur est remplie
  • vérifier qu'une longueur respecte une plage minimale et maximale
  • vérifier qu'une valeur ressemble à une adresse e-mail valide

                    <?php

                    function estRempli(string $valeur): bool
                    {
                        return $valeur !== '';
                    }

                    function respecteLongueurMin(string $valeur, int $longueurMin): bool
                    {
                        return mb_strlen($valeur) >= $longueurMin;
                    }

                    function respecteLongueurMax(string $valeur, int $longueurMax): bool
                    {
                        return mb_strlen($valeur) <= $longueurMax;
                    }

                    function respecteLongueur(string $valeur, int $longueurMin, int $longueurMax): bool
                    {
                        return respecteLongueurMin($valeur, $longueurMin)
                            && respecteLongueurMax($valeur, $longueurMax);
                    }

                    function estEmailValide(string $valeur): bool
                    {
                        return filter_var($valeur, FILTER_VALIDATE_EMAIL) !== false;
                    }
                

Le code n'est pas encore transformé en gestionnaire réutilisable complet, mais il gagne déjà en lisibilité.


                    <?php

                    $nom = lireChampTexte($_POST, 'nom');
                    $prenom = lireChampTexte($_POST, 'prenom');
                    $email = lireChampTexte($_POST, 'email');
                    $message = lireChampTexte($_POST, 'message');

                    if (!estRempli($nom))
                    {
                        $erreurs['nom'] = 'Le nom est requis.';
                    }
                    elseif (!respecteLongueur($nom, 2, 100))
                    {
                        $erreurs['nom'] = 'Le nom doit contenir entre 2 et 100 caractères.';
                    }

                    // Prénom étant un champ facultatif,
                    // réaliser les tests de validation
                    // uniquement si le champ a été rempli.
                    if (estRempli($prenom))
                    {
                        if (!respecteLongueurMax($prenom, 255))
                        {
                            $erreurs['prenom'] = 'Le prénom ne peut pas dépasser 255 caractères.';
                        }
                    }

                    if (!estRempli($email))
                    {
                        $erreurs['email'] = 'L\'email est requis.';
                    }
                    elseif (!estEmailValide($email))
                    {
                        $erreurs['email'] = 'Veuillez saisir une adresse email valide.';
                    }

                    if (!estRempli($message))
                    {
                        $erreurs['message'] = 'Le message est requis.';
                    }
                    elseif (!respecteLongueur($message, 10, 3000))
                    {
                        $erreurs['message'] = 'Le message doit contenir entre 10 et 3000 caractères.';
                    }
                

Chaque fonction correspond à une idée simple et ne fait qu'une seule chose. Le code devient plus facile à lire, et chaque vérification peut être testée ou réutilisée indépendamment.

Mais si on examine le code d'appel, le problème n'a pas disparu. Pour chaque champ, on écrit encore un bloc conditionnel à la main, on appelle explicitement les fonctions dans l'ordre voulu, et on répète la même structure. Ajouter un cinquième champ signifie ajouter un cinquième bloc similaire. Ce n'est plus la logique de validation qui se répète, c'est la structure de contrôle elle-même.

Pour sortir de cette répétition, il faut changer de niveau d'abstraction. Plutôt que d'appeler les fonctions de validation une par une pour chaque champ, on peut décrire ce que le formulaire attend dans un tableau de règles, puis confier à une seule fonction le soin de parcourir ces règles et d'appliquer les vérifications automatiquement.

Décrire les champs avec un tableau de règles

L'idée consiste à créer un tableau associatif dans lequel chaque clé correspond à un champ du formulaire, et chaque valeur décrit les règles à respecter pour ce champ.


                    <?php

                    $reglesDesChamps = [
                        'nom' => [
                            'requis' => true,
                            'longueurMin' => 2,
                            'longueurMax' => 100
                        ],
                        'prenom' => [
                            'requis' => false,
                            'longueurMax' => 255
                        ],
                        'email' => [
                            'requis' => true,
                            'type' => 'email'
                        ],
                        'message' => [
                            'requis' => true,
                            'longueurMin' => 10,
                            'longueurMax' => 3000
                        ]
                    ];
                

Ce tableau devient la description centrale du formulaire. On ne dit plus seulement comment vérifier les champs. On commence à décrire ce que le formulaire attend.

Cette étape change la manière de raisonner. Au lieu d'écrire une suite de conditions dispersées, on regroupe les informations au même endroit. Pour les types de champs déjà pris en charge par la fonction de validation, ajouter un champ revient simplement à ajouter une entrée dans ce tableau. En revanche, si l'on introduit un type de champ entièrement nouveau, comme un champ date ou un champ fichier, la fonction principale devra elle aussi évoluer pour l'interpréter.

Il faut aussi remarquer un point important. La boucle future parcourra ce tableau de règles, et non directement $_POST. C'est le serveur qui décide quels champs il attend et quelles règles s'appliquent. Il ne faut pas dépendre uniquement des données effectivement envoyées par le navigateur.

Construire une fonction principale de validation

Maintenant que les règles sont centralisées, on peut écrire une fonction principale chargée de parcourir ces règles et de produire un résultat structuré.

Cette fonction parcourt elle-même l'ensemble des règles et applique les vérifications en une seule passe.

Elle renvoie trois ensembles d'informations :

  • le tableau des erreurs
  • le tableau des anciennes valeurs, utile pour réafficher les saisies en cas d'erreur
  • le message de statut de la soumission

Les vérifications de longueur sont maintenant séparées en deux fonctions distinctes, respecteLongueurMin() et respecteLongueurMax(), parce que la boucle doit pouvoir appliquer chaque contrainte indépendamment. Un champ peut posséder seulement une borne minimale, seulement une borne maximale, ou les deux. La fonction respecteLongueur() reste utile comme raccourci lorsque les deux bornes existent en même temps, mais elle ne suffit plus à elle seule pour couvrir tous les cas.

Voici une première version complète.


                    <?php

                    function lireChampTexte(array $donnees, string $nomChamp): string
                    {
                        return trim($donnees[$nomChamp] ?? '');
                    }

                    function estRempli(string $valeur): bool
                    {
                        return $valeur !== '';
                    }

                    function respecteLongueurMin(string $valeur, int $longueurMin): bool
                    {
                        return mb_strlen($valeur) >= $longueurMin;
                    }

                    function respecteLongueurMax(string $valeur, int $longueurMax): bool
                    {
                        return mb_strlen($valeur) <= $longueurMax;
                    }

                    function respecteLongueur(string $valeur, int $longueurMin, int $longueurMax): bool
                    {
                        return respecteLongueurMin($valeur, $longueurMin)
                            && respecteLongueurMax($valeur, $longueurMax);
                    }

                    function estEmailValide(string $valeur): bool
                    {
                        return filter_var($valeur, FILTER_VALIDATE_EMAIL) !== false;
                    }

                    function validerFormulaire(array $reglesDesChamps, array $donnees): array
                    {
                        $erreurs = [];
                        $anciennesValeurs = [];
                        $messageGlobal = '';

                        foreach ($reglesDesChamps as $nomChamp => $reglesDuChamp)
                        {
                            $valeur = lireChampTexte($donnees, $nomChamp);

                            // On conserve d'abord la valeur nettoyée.
                            // Si le formulaire contient des erreurs plus loin,
                            // elle pourra être réaffichée dans la vue.
                            $anciennesValeurs[$nomChamp] = $valeur;

                            // Récupérer la valeur de la configuration du champ requis si celle-ci existe,
                            // sinon la configurer à false par défaut.
                            $champEstRequis = $reglesDuChamp['requis'] ?? false;

                            // Si le champ est requis et qu'il est vide,
                            // retourner une erreur.
                            if ($champEstRequis && !estRempli($valeur))
                            {
                                $erreurs[$nomChamp] = 'Ce champ est requis.';

                                // Ne pas aller plus loin et gérer le champ suivant (itération suivante).
                                continue;
                            }

                            // Si le champ est vide mais facultatif,
                            // il n'y a rien d'autre à vérifier.
                            if (!$champEstRequis && !estRempli($valeur))
                            {
                                continue;
                            }

                            if (isset($reglesDuChamp['longueurMin']) && isset($reglesDuChamp['longueurMax']))
                            {
                                if (!respecteLongueur($valeur, $reglesDuChamp['longueurMin'], $reglesDuChamp['longueurMax']))
                                {
                                    $erreurs[$nomChamp] = "Ce champ doit contenir entre {$reglesDuChamp['longueurMin']} et {$reglesDuChamp['longueurMax']} caractères.";
                                    continue;
                                }
                            }
                            elseif (isset($reglesDuChamp['longueurMin']))
                            {
                                if (!respecteLongueurMin($valeur, $reglesDuChamp['longueurMin']))
                                {
                                    $erreurs[$nomChamp] = "Ce champ doit contenir au moins {$reglesDuChamp['longueurMin']} caractères.";
                                    continue;
                                }
                            }
                            elseif (isset($reglesDuChamp['longueurMax']))
                            {
                                if (!respecteLongueurMax($valeur, $reglesDuChamp['longueurMax']))
                                {
                                    $erreurs[$nomChamp] = "Ce champ ne peut pas dépasser {$reglesDuChamp['longueurMax']} caractères.";
                                    continue;
                                }
                            }

                            if (
                                isset($reglesDuChamp['type'])
                                && $reglesDuChamp['type'] === 'email'
                                && !estEmailValide($valeur)
                            )
                            {
                                $erreurs[$nomChamp] = 'Veuillez saisir une adresse email valide.';
                                continue;
                            }
                        }

                        // Si aucune erreur n'a été trouvée,
                        // il est inutile de conserver les anciennes valeurs
                        // pour le rendu suivant.
                        if (empty($erreurs))
                        {
                            $anciennesValeurs = [];
                            $messageGlobal = 'Le formulaire a bien été envoyé.';
                        }
                        else
                        {
                            $messageGlobal = 'Le formulaire contient des erreurs.';
                        }

                        return [
                            'erreurs' => $erreurs,
                            'anciennesValeurs' => $anciennesValeurs,
                            'messageGlobal' => $messageGlobal
                        ];
                    }
                

Cette fonction marque une vraie étape. Le formulaire n'est plus décrit principalement par une suite de if écrits à la main. Il est maintenant défini par un tableau de règles, puis traité automatiquement par une fonction commune.

Remarquons aussi la décision prise en fin de fonction. Si la validation réussit, $anciennesValeurs est vidé intentionnellement. Lorsqu'aucune erreur n'est présente, il n'y a aucune raison de réafficher les saisies précédentes dans le formulaire. Ce choix simplifie légèrement la vue et évite de laisser traîner des données inutiles.

Réutiliser le résultat dans la page

Une fois la fonction principale prête, le traitement devient beaucoup plus compact. Le fichier /traitements/traiter-formulaire-contact.php peut maintenant se concentrer sur trois tâches :

  • préparer les variables
  • définir les règles du formulaire
  • appeler la fonction de validation

Nouveau fichier avec la logique de traitement réutilisable : /core/gestion-formulaire.php


                    <?php

                    function e(string $valeur): string
                    {
                        return htmlspecialchars($valeur, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
                    }

                    function lireChampTexte(array $donnees, string $nomChamp): string
                    {
                        return trim($donnees[$nomChamp] ?? '');
                    }

                    function estRempli(string $valeur): bool
                    {
                        return $valeur !== '';
                    }

                    function respecteLongueurMin(string $valeur, int $longueurMin): bool
                    {
                        return mb_strlen($valeur) >= $longueurMin;
                    }

                    function respecteLongueurMax(string $valeur, int $longueurMax): bool
                    {
                        return mb_strlen($valeur) <= $longueurMax;
                    }

                    function respecteLongueur(string $valeur, int $longueurMin, int $longueurMax): bool
                    {
                        return respecteLongueurMin($valeur, $longueurMin)
                            && respecteLongueurMax($valeur, $longueurMax);
                    }

                    function estEmailValide(string $valeur): bool
                    {
                        return filter_var($valeur, FILTER_VALIDATE_EMAIL) !== false;
                    }

                    function validerFormulaire(array $reglesDesChamps, array $donnees): array
                    {
                        $erreurs = [];
                        $anciennesValeurs = [];
                        $messageGlobal = '';

                        foreach ($reglesDesChamps as $nomChamp => $reglesDuChamp)
                        {
                            $valeur = lireChampTexte($donnees, $nomChamp);
                            $anciennesValeurs[$nomChamp] = $valeur;

                            $champEstRequis = $reglesDuChamp['requis'] ?? false;

                            if ($champEstRequis && !estRempli($valeur))
                            {
                                $erreurs[$nomChamp] = 'Ce champ est requis.';
                                continue;
                            }

                            if (!$champEstRequis && !estRempli($valeur))
                            {
                                continue;
                            }

                            if (isset($reglesDuChamp['longueurMin']) && isset($reglesDuChamp['longueurMax']))
                            {
                                if (!respecteLongueur($valeur, $reglesDuChamp['longueurMin'], $reglesDuChamp['longueurMax']))
                                {
                                    $erreurs[$nomChamp] = "Ce champ doit contenir entre {$reglesDuChamp['longueurMin']} et {$reglesDuChamp['longueurMax']} caractères.";
                                    continue;
                                }
                            }
                            elseif (isset($reglesDuChamp['longueurMin']))
                            {
                                if (!respecteLongueurMin($valeur, $reglesDuChamp['longueurMin']))
                                {
                                    $erreurs[$nomChamp] = "Ce champ doit contenir au moins {$reglesDuChamp['longueurMin']} caractères.";
                                    continue;
                                }
                            }
                            elseif (isset($reglesDuChamp['longueurMax']))
                            {
                                if (!respecteLongueurMax($valeur, $reglesDuChamp['longueurMax']))
                                {
                                    $erreurs[$nomChamp] = "Ce champ ne peut pas dépasser {$reglesDuChamp['longueurMax']} caractères.";
                                    continue;
                                }
                            }

                            if (
                                isset($reglesDuChamp['type'])
                                && $reglesDuChamp['type'] === 'email'
                                && !estEmailValide($valeur)
                            )
                            {
                                $erreurs[$nomChamp] = 'Veuillez saisir une adresse email valide.';
                                continue;
                            }
                        }

                        if (empty($erreurs))
                        {
                            $anciennesValeurs = [];
                            $messageGlobal = 'Le formulaire a bien été envoyé.';
                        }
                        else
                        {
                            $messageGlobal = 'Le formulaire contient des erreurs.';
                        }

                        return [
                            'erreurs' => $erreurs,
                            'anciennesValeurs' => $anciennesValeurs,
                            'messageGlobal' => $messageGlobal
                        ];
                    }
                

Nouvelle version de /traitements/traiter-formulaire-contact.php


                    <?php

                    require_once __DIR__ . '/../core/gestion-formulaire.php';

                    $erreurs = [];
                    $anciennesValeurs = [];
                    $messageGlobal = '';

                    if ($_SERVER['REQUEST_METHOD'] === 'POST')
                    {
                        $reglesDesChamps = [
                            'nom' => [
                                'requis' => true,
                                'longueurMin' => 2,
                                'longueurMax' => 100
                            ],
                            'prenom' => [
                                'requis' => false,
                                'longueurMax' => 255
                            ],
                            'email' => [
                                'requis' => true,
                                'type' => 'email'
                            ],
                            'message' => [
                                'requis' => true,
                                'longueurMin' => 10,
                                'longueurMax' => 3000
                            ]
                        ];

                        $resultatValidation = validerFormulaire($reglesDesChamps, $_POST);

                        $erreurs = $resultatValidation['erreurs'];
                        $anciennesValeurs = $resultatValidation['anciennesValeurs'];
                        $messageGlobal = $resultatValidation['messageGlobal'];
                    }
                

La fonction produit maintenant un résultat complet à partir d'un simple tableau de règles. Ajouter un champ ne demande plus qu'une entrée supplémentaire dans ce tableau, sans toucher au reste du code.

La vue peut continuer à utiliser la fonction e(). En effet, index.php charge d'abord le fichier de traitement. Ce fichier de traitement charge ensuite /core/gestion-formulaire.php. Au moment où la vue s'affiche, la fonction e() est donc déjà disponible.

Il reste cependant une limite. Les messages d'erreur sont écrits directement dans la fonction de validation. Si l'on souhaite proposer le formulaire en plusieurs langues, il faudra modifier cette fonction pour chaque langue, ce qui mélange deux responsabilités distinctes. D'un côté, appliquer les règles. De l'autre, formuler les messages.

Une piste pour aller plus loin consiste à externaliser ces messages dans des fichiers séparés, un par langue, que la fonction chargerait selon le contexte. La logique de validation resterait inchangée quelle que soit la langue affichée. Ce sujet dépasse cependant le cadre de ce chapitre et ne sera pas traité ici.

Étendre le système à d'autres types de champs

Jusqu'ici, le gestionnaire traite surtout des champs textuels classiques. Pourtant, un formulaire réel contient souvent d'autres types de champs. Il faut donc enrichir progressivement le système, sans tout bouleverser.

L'idée reste la même. On ajoute de nouvelles règles à la structure de configuration, puis on adapte la fonction principale pour interpréter ces règles.

Ajouter des valeurs autorisées pour un select ou un groupe radio

Pour un select simple ou un groupe de boutons radio, le serveur doit vérifier que la valeur reçue appartient bien à une liste autorisée.

On peut décrire cela avec une clé nommée valeursAutorisees.


                    <?php

                    $reglesDesChamps = [
                        'niveau' => [
                            'requis' => true,
                            'valeursAutorisees' => ['debutant', 'intermediaire', 'avance']
                        ]
                    ];
                

La fonction principale peut alors être enrichie avec le test suivant :


                    <?php

                    if (isset($reglesDuChamp['valeursAutorisees']))
                    {
                        if (!in_array($valeur, $reglesDuChamp['valeursAutorisees'], true))
                        {
                            $erreurs[$nomChamp] = 'La valeur envoyée pour ce champ est invalide.';
                            continue;
                        }
                    }
                

Le troisième argument de in_array() vaut ici true. Il active une comparaison stricte. C'est une bonne habitude, car elle évite certains rapprochements automatiques indésirables entre types de valeurs.

Gérer un champ multiple

Les champs comme checkbox[] ou select multiple introduisent une différence importante. Ils ne renvoient pas une chaîne, mais un tableau de valeurs.

Il faut donc ajouter une petite fonction de normalisation dédiée.


                    <?php

                    function lireChampMultiple(array $donnees, string $nomChamp): array
                    {
                        $valeurs = $donnees[$nomChamp] ?? [];

                        if (!is_array($valeurs))
                        {
                            return [];
                        }

                        return array_map('trim', $valeurs);
                    }
                

La règle du champ peut ensuite indiquer qu'il s'agit d'un champ multiple :


                    <?php

                    'langages' => [
                        'requis' => true,
                        'multiple' => true,
                        'valeursAutorisees' => ['html', 'css', 'javascript', 'php']
                    ]
                

Cette branche ne s'ajoute pas simplement à la fin du corps de la boucle existante. Elle doit être placée en tête, avant toutes les vérifications textuelles, et se terminer par un continue qui court-circuite le reste du traitement. La structure générale de la fonction centrale est donc à réorganiser comme suit :


                    <?php

                    foreach ($reglesDesChamps as $nomChamp => $reglesDuChamp)
                    {
                        // 1. Branche champ multiple : traitement entièrement distinct,
                        //    on saute le reste de la boucle avec continue.
                        if (($reglesDuChamp['multiple'] ?? false) === true)
                        {
                            $valeurs = lireChampMultiple($donnees, $nomChamp);
                            $anciennesValeurs[$nomChamp] = $valeurs;

                            if (($reglesDuChamp['requis'] ?? false) === true && empty($valeurs))
                            {
                                $erreurs[$nomChamp] = 'Veuillez sélectionner au moins une valeur.';
                                continue;
                            }

                            foreach ($valeurs as $valeur)
                            {
                                if (!in_array($valeur, $reglesDuChamp['valeursAutorisees'] ?? [], true))
                                {
                                    $erreurs[$nomChamp] = 'Une valeur envoyée pour ce champ est invalide.';
                                    break;
                                }
                            }

                            continue;
                        }

                        // 2. Branche champ simple : lecture, nettoyage, vérifications textuelles.
                        $valeur = lireChampTexte($donnees, $nomChamp);
                        $anciennesValeurs[$nomChamp] = $valeur;

                        // ... vérifications requis, longueurMin, longueurMax, type email ...
                    }
                

Si cette branche était insérée après les vérifications textuelles, le code atteindrait lireChampTexte() avant de tester si le champ est multiple, ce qui produirait un comportement incorrect sans message d'erreur évident. La position de la branche dans la boucle n'est donc pas un détail.

Ce point illustre une limite plus générale. Tous les champs ne renvoient pas le même type de données. C'est pour cette raison qu'un gestionnaire réutilisable reste progressif. On ne peut pas tout traiter de manière strictement identique.

Version plus complète de la configuration

Avec les extensions précédentes, le tableau de règles peut désormais décrire plusieurs types de champs classiques dans une même structure.


                    <?php

                    $reglesDesChamps = [
                        'nom' => [
                            'requis' => true,
                            'longueurMin' => 2,
                            'longueurMax' => 100
                        ],
                        'prenom' => [
                            'requis' => false,
                            'longueurMax' => 255
                        ],
                        'email' => [
                            'requis' => true,
                            'type' => 'email'
                        ],
                        'message' => [
                            'requis' => true,
                            'longueurMin' => 10,
                            'longueurMax' => 3000
                        ],
                        'niveau' => [
                            'requis' => true,
                            'valeursAutorisees' => ['debutant', 'intermediaire', 'avance']
                        ],
                        'langages' => [
                            'requis' => true,
                            'multiple' => true,
                            'valeursAutorisees' => ['html', 'css', 'javascript', 'php']
                        ]
                    ];
                

Cette structure reste accessible à un débutant, car elle repose sur des tableaux associatifs et sur des règles nommées de manière explicite. Chaque champ est décrit à un seul endroit, ce qui rend le formulaire plus facile à relire et à faire évoluer.

Avantages de cette approche

Construire un gestionnaire réutilisable n'a pas pour but de rendre le code impressionnant ou compliqué. Son intérêt est beaucoup plus concret.

Cette approche apporte notamment :

  • moins de répétition dans le traitement
  • une meilleure centralisation des règles
  • une maintenance plus simple lorsque le formulaire évolue
  • une meilleure cohérence entre les champs
  • une base plus propre pour des projets plus grands

Par exemple, si demain il faut ajouter un champ pseudo, le changement devient beaucoup plus localisé. Il suffit d'ajouter le champ dans la vue, puis d'ajouter sa configuration dans le tableau de règles. Cela reste vrai tant que le nouveau champ correspond à un type déjà géré par la fonction de validation. Si l'on ajoute un type inédit, la fonction centrale devra évoluer en conséquence.

Exercices

Exercice 01

Dans cet exercice, vous allez améliorer légèrement la vue du formulaire de contact en supprimant une répétition très visible. Le formulaire fonctionne déjà correctement. Le but n'est donc pas de réparer un projet cassé, mais de rendre le code de la vue un peu plus lisible.

La répétition visée se trouve dans index.php. Chaque fois qu'une ancienne valeur doit être réaffichée dans un champ, la même opération revient avec htmlspecialchars() et toujours les mêmes paramètres.

Dans cet exercice, vous n'allez pas encore modifier la logique de validation du formulaire. Vous allez seulement créer une petite fonction utilitaire nommée e(), puis l'utiliser dans la vue à la place des appels répétés à htmlspecialchars().

Cette étape est volontairement simple. Elle permet de montrer qu'une fonction peut aussi servir à nommer proprement une petite intention répétée, même lorsqu'il ne s'agit pas d'un calcul compliqué.

Structure

Repartir de la version finale du projet de l'exercice précédent, c'est-à-dire celle dans laquelle le formulaire contient les champs nom, prenom, email et message, avec affichage des erreurs sous les champs, message global sous le bouton et réaffichage des anciennes valeurs en cas d'erreur.

Dupliquer ce projet dans un nouveau dossier nommé Exo-gestionnaire-formulaire-reutilisable-01.


                    📁 Exo-gestionnaire-formulaire-reutilisable-01/
                    ├── 📁 traitements/
                    │   └── 📄 traiter-formulaire-contact.php
                    └── 📄 index.php
                

Dans cet exercice, vous travaillerez surtout dans index.php et un peu dans /traitements/traiter-formulaire-contact.php.

Ne créez pas encore de dossier core. Ne déplacez pas encore de logique dans un autre fichier. Cette étape viendra plus tard.

Étape 01

Avant de modifier quoi que ce soit, commencer par observer calmement la vue. Cette étape est importante pour un débutant. Si vous voyez clairement ce qui se répète, vous comprendrez beaucoup mieux pourquoi la fonction e() va être utile.

Ici, le but n'est pas encore de coder. Le but est d'identifier précisément la répétition présente dans index.php.

  1. Ouvrir le fichier index.php.
  2. Repérer les endroits où une ancienne valeur est réinjectée dans un champ.
    • pour nom
    • pour prenom
    • pour email
    • pour message
  3. Observer ce que ces morceaux de code ont en commun.
    • ils lisent une valeur dans $anciennesValeurs
    • ils utilisent une chaîne vide si la clé n'existe pas
    • ils passent le résultat dans htmlspecialchars()
    • ils réutilisent toujours ENT_QUOTES | ENT_SUBSTITUTE et 'UTF-8'
  4. Relire aussi l'explication théorique sur le rôle de htmlspecialchars(). Il faut bien comprendre que cette fonction sert ici à préparer une valeur pour un affichage HTML sûr.
  5. Si cela vous aide, ajouter un petit commentaire temporaire dans index.php pour repérer mentalement cette zone répétitive.
  6. Ne modifiez encore aucune logique.

À la fin de cette étape, vous devez voir clairement la répétition qui sera remplacée par la fonction e().

Étape 02

Vous allez maintenant créer la fonction e(). Cette fonction ne sera pas placée dans index.php, mais dans /traitements/traiter-formulaire-contact.php.

Ce choix est important à comprendre. Comme index.php charge d'abord ce fichier de traitement, toutes les fonctions définies dans ce fichier seront déjà disponibles au moment où la vue s'affichera.

  1. Ouvrir le fichier /traitements/traiter-formulaire-contact.php.
  2. Repérer le début du fichier, avant l'initialisation de $erreurs, $messageGlobal et $anciennesValeurs.
  3. À cet endroit, ajouter une fonction nommée e().
  4. Cette fonction doit recevoir un seul paramètre de type string.
  5. Dans cette fonction, retourner le résultat de htmlspecialchars() avec les paramètres suivants :
    • la valeur reçue
    • ENT_QUOTES | ENT_SUBSTITUTE
    • 'UTF-8'
  6. Relire ensuite ce que fait cette fonction. Elle ne valide pas une donnée. Elle ne nettoie pas une donnée pour la base de données. Elle ne sert qu'à préparer une valeur pour l'affichage HTML.
  7. Enregistrer le fichier.
  8. Recharger la page dans le navigateur. À ce stade, rien ne doit encore changer visuellement, car la fonction existe, mais elle n'est pas encore utilisée dans la vue.

À la fin de cette étape, la fonction e() doit exister dans /traitements/traiter-formulaire-contact.php, avant le bloc if ($_SERVER['REQUEST_METHOD'] === 'POST').

Étape 03

Vous allez maintenant remplacer progressivement les appels à htmlspecialchars() dans la vue.

Il est important de procéder lentement. Ne remplacez pas tout d'un seul coup. Faites un remplacement, enregistrez, testez, puis passez au suivant.

  1. Revenir dans index.php.
  2. Repérer l'endroit où la valeur du champ nom est réaffichée.
  3. Remplacer uniquement cet appel long à htmlspecialchars() par un appel à e().
  4. Conserver l'opérateur ?? au moment de lire dans $anciennesValeurs. La gestion de la clé absente doit donc rester dans la vue.
  5. Enregistrer, puis faire un test invalide pour vérifier que la valeur du champ nom est toujours réaffichée correctement.
  6. Si ce premier test fonctionne, faire ensuite la même chose pour le champ prenom.
  7. Enregistrer, puis retester.
  8. Faire ensuite le même travail pour email.
  9. Enregistrer, puis retester.
  10. Terminer enfin par le contenu du textarea du champ message.
  11. Enregistrer, puis refaire un test complet avec une soumission invalide.

À la fin de cette étape, les anciennes valeurs de nom, prenom, email et message doivent toutes passer par e().

Étape 04

Vous allez maintenant vérifier que le comportement du formulaire n'a pas changé, et que la nouvelle écriture reste sûre.

  1. Faire une soumission invalide simple pour vérifier que les anciennes valeurs sont toujours réaffichées.
  2. Vérifier que les messages d'erreur apparaissent toujours sous les bons champs.
  3. Vérifier que le message global fonctionne toujours comme avant.
  4. Faire ensuite un petit test d'injection.
    • saisir dans un champ une valeur contenant du HTML, par exemple <b>Bonjour</b>
    • provoquer en même temps une erreur sur un autre champ pour forcer le réaffichage du formulaire
    • vérifier que la valeur réaffichée apparaît comme du texte et non comme du HTML interprété
  5. Relire enfin la vue. Vérifier que le code est maintenant plus léger à parcourir à l'endroit où les anciennes valeurs sont réinjectées.

À la fin de cette étape, vous devez constater deux choses. D'une part, le formulaire se comporte toujours comme avant. D'autre part, la vue est devenue un peu plus lisible grâce à une petite fonction utilitaire bien choisie.

Conclusion

Dans cet exercice, vous n'avez pas encore rendu tout le formulaire réutilisable. Vous avez simplement traité une répétition visible dans la vue. Cette étape reste modeste, mais elle prépare bien la suite.

Dans l'exercice suivant, vous quitterez la vue pour vous concentrer sur les répétitions plus nombreuses et plus structurantes du traitement côté serveur.

Exercice 02

Dans cet exercice, vous allez quitter la vue pour vous concentrer sur le traitement côté serveur. Le formulaire fonctionne déjà correctement, et la vue a déjà été légèrement allégée grâce à la fonction e(). Le but est maintenant de réduire les répétitions les plus visibles dans /traitements/traiter-formulaire-contact.php.

À ce stade, vous ne construisez pas encore le gestionnaire complet basé sur un tableau de règles. Ce travail viendra dans l'exercice suivant. Ici, vous préparez surtout le terrain en isolant plusieurs petites fonctions simples et réutilisables.

L'idée pédagogique est la suivante. Avant de créer une fonction centrale plus importante, il faut d'abord apprendre à repérer les petites tâches qui reviennent souvent dans le traitement. Par exemple :

  • lire proprement un champ texte
  • vérifier si une valeur est remplie
  • vérifier une longueur minimale
  • vérifier une longueur maximale
  • vérifier une plage de longueur
  • vérifier si une adresse e-mail est valide

Vous allez avancer lentement. À chaque étape, modifier peu de choses, enregistrer, tester, puis seulement ensuite passer à la suite. Pour un débutant, cette méthode est beaucoup plus sûre que de modifier tout le fichier d'un seul coup.

Pendant presque tout l'exercice, vous travaillerez uniquement dans /traitements/traiter-formulaire-contact.php. Ne modifiez pas index.php sauf si une étape vous le demande explicitement. Ne créez pas encore de dossier core. Ne déplacez pas encore vos fonctions dans un autre fichier.

Structure

Repartir du projet terminé à la fin de l'exercice 01, c'est-à-dire avec la fonction e() déjà utilisée dans la vue.

Dupliquer ce projet dans un nouveau dossier nommé Exo-gestionnaire-formulaire-reutilisable-02.


                    📁 Exo-gestionnaire-formulaire-reutilisable-02/
                    ├── 📁 traitements/
                    │   └── 📄 traiter-formulaire-contact.php
                    └── 📄 index.php
                

Étape 01

Avant d'améliorer le traitement, commencer par l'observer calmement. Cette étape est très importante pour un débutant. Si vous voyez clairement ce qui se répète, vous comprendrez beaucoup mieux pourquoi les petites fonctions que vous allez créer seront utiles.

Ici, vous ne devez toujours pas modifier le comportement du formulaire. Le but est simplement d'identifier clairement les répétitions présentes dans /traitements/traiter-formulaire-contact.php.

  1. Ouvrir le fichier /traitements/traiter-formulaire-contact.php.
  2. Repérer les lignes qui lisent les données utilisateur.
    • la ligne qui récupère $nom
    • la ligne qui récupère $prenom
    • la ligne qui récupère $email
    • la ligne qui récupère $message
  3. Observer ce que ces lignes ont en commun.
    • elles lisent une valeur dans $_POST
    • elles utilisent une chaîne vide si la clé n'existe pas
    • elles appliquent trim()
  4. Repérer ensuite les validations répétitives.
    • les tests qui vérifient si un champ est vide
    • les tests qui utilisent mb_strlen()
    • les tests qui vérifient une longueur minimale ou maximale
    • la vérification spécifique de l'e-mail
    • les lignes qui ajoutent un message dans $erreurs
  5. Repérer enfin la partie qui recopie les anciennes valeurs dans $anciennesValeurs lorsque le formulaire contient des erreurs.
  6. Si cela vous aide, ajouter quelques commentaires temporaires pour séparer mentalement les zones suivantes :
    • lecture et nettoyage
    • validations
    • anciennes valeurs
    • message global
  7. Ne modifiez encore aucune logique.

À la fin de cette étape, vous devez voir plus clairement où se trouvent les répétitions du traitement.

Étape 02

Une fois ces répétitions repérées, vérifier que votre copie de départ fonctionne correctement. Cette étape ne sert pas à modifier le code. Elle sert à vous assurer que votre point de départ est sain avant de commencer les refactorisations.

  1. Ouvrir le projet dans le navigateur.
  2. Faire un test avec des valeurs valides.
    • remplir correctement tous les champs
    • soumettre le formulaire
    • vérifier que le message global de réussite s'affiche
  3. Faire ensuite un test avec des valeurs invalides.
    • laisser au moins un champ requis vide
    • ou provoquer une erreur de longueur
    • vérifier que les messages d'erreur apparaissent sous les bons champs
    • vérifier que les anciennes valeurs sont réaffichées
  4. Vérifier aussi que la fonction e() est toujours utilisée dans la vue.
  5. Ne modifier encore aucun fichier.

À la fin de cette étape, vous devez simplement avoir confirmé que la version copiée fonctionne déjà correctement.

Étape 03

La première répétition facile à réduire concerne la lecture et le nettoyage des champs texte. Vous allez créer une fonction dédiée à cette tâche.

L'idée n'est pas seulement de gagner quelques caractères. L'idée est surtout de donner un nom clair à une action qui revient souvent. Au lieu de relire à chaque fois une expression complète, on pourra lire une intention. Ici, cette intention est lire proprement un champ texte.

  1. Dans /traitements/traiter-formulaire-contact.php, repérer le bloc if ($_SERVER['REQUEST_METHOD'] === 'POST').
  2. Juste au-dessus de ce bloc, ajouter une fonction nommée lireChampTexte().
  3. Cette fonction doit recevoir deux paramètres :
    • un tableau de données
    • le nom du champ à lire
  4. Dans cette fonction, faire en sorte que la valeur retournée soit :
    • la valeur du champ si la clé existe
    • une chaîne vide sinon
    • dans les deux cas, après passage par trim()
  5. Ne remplacez pas encore toutes les lectures à la fois. Commencer uniquement par la ligne qui récupère $nom.
  6. Remplacer cette seule ligne par un appel à votre nouvelle fonction.
  7. Enregistrer, puis tester immédiatement avec une soumission valide.
  8. Si ce premier test fonctionne, remplacer ensuite de la même manière la lecture de $prenom.
  9. Enregistrer, puis retester.
  10. Faire ensuite le même travail pour $email puis pour $message. Vous pouvez tester après chaque remplacement, ou au minimum après les deux derniers.
  11. Faire enfin un test avec des espaces avant et après une valeur pour vérifier que le nettoyage fonctionne toujours.

À la fin de cette étape, les quatre lectures de champs doivent passer par lireChampTexte(). Le comportement du formulaire doit rester identique.

Étape 04

Vous allez maintenant extraire une idée très fréquente vérifier si une valeur est réellement remplie. Cette fonction est simple, mais elle rendra certaines conditions plus lisibles.

Là encore, le but n'est pas seulement de raccourcir le code. Le but est de mieux nommer ce que l'on est en train de vérifier. Quand on lit estRempli(...), on comprend plus vite l'intention que lorsqu'on relit plusieurs fois une comparaison avec une chaîne vide.

  1. Toujours au-dessus du bloc POST, ajouter une fonction nommée estRempli().
  2. Cette fonction doit recevoir une chaîne de caractères.
  3. Elle doit retourner true si la chaîne n'est pas vide, sinon false.
  4. Ne remplacez pas toutes les conditions du fichier. Commencer uniquement par une seule condition, par exemple celle qui vérifie si $nom est vide.
  5. Réécrire cette condition avec estRempli().
  6. Enregistrer, puis tester avec un nom vide.
  7. Si ce test fonctionne, utiliser ensuite estRempli() dans les autres endroits du fichier où l'idée est exactement la même.
  8. Refaire un test valide puis un test invalide.

À la fin de cette étape, les tests de présence de valeur doivent être plus lisibles grâce à estRempli(), sans changer le comportement du formulaire.

Étape 05

Vous allez maintenant extraire les vérifications de longueur qui reviennent souvent. La théorie présente trois fonctions :

  • respecteLongueurMin()
  • respecteLongueurMax()
  • respecteLongueur()

Vous allez les construire dans cet ordre. Cela permet de comprendre le rôle précis de chacune d'elles, puis de voir comment une fonction plus pratique peut réutiliser les deux premières. Cette progression suit exactement celle de la théorie.

  1. Toujours au-dessus du bloc POST, ajouter une fonction nommée respecteLongueurMin().
  2. Cette fonction doit recevoir :
    • la valeur à tester
    • la longueur minimale attendue
  3. Elle doit utiliser mb_strlen() et retourner true si la valeur contient au moins le nombre de caractères demandé.
  4. Ajouter ensuite une seconde fonction nommée respecteLongueurMax().
  5. Cette fonction doit recevoir :
    • la valeur à tester
    • la longueur maximale autorisée
  6. Elle doit utiliser mb_strlen() et retourner true si la valeur ne dépasse pas cette limite.
  7. Ajouter enfin une troisième fonction nommée respecteLongueur().
  8. Cette troisième fonction doit recevoir :
    • la valeur à tester
    • une longueur minimale
    • une longueur maximale
  9. Dans cette troisième fonction, ne recopiez pas toute la logique avec mb_strlen(). Appeler plutôt respecteLongueurMin() et respecteLongueurMax(). Cela montre qu'une fonction peut aussi s'appuyer sur d'autres fonctions déjà créées.
  10. Ne remplacez pas encore toutes les vérifications à la fois. Commencer par le champ nom, car il possède à la fois une borne minimale et une borne maximale.
  11. Réécrire uniquement la vérification de longueur du nom avec respecteLongueur().
  12. Enregistrer, puis tester avec :
    • un nom trop court
    • un nom trop long
    • un nom valide
  13. Si ce test fonctionne, utiliser ensuite la même fonction pour le champ message.
  14. Refaire les tests avec un message trop court, un message trop long puis un message valide.
  15. Pour le champ prenom, qui ne possède qu'une borne maximale, utiliser respecteLongueurMax() seule. Cette différence est importante, car elle prépare déjà la logique plus souple de l'exercice suivant.

À la fin de cette étape, les fonctions respecteLongueurMin(), respecteLongueurMax() et respecteLongueur() doivent exister, et être déjà utilisées dans le traitement.

Étape 06

Vous allez maintenant extraire la validation du format e-mail. Là encore, il s'agit de donner un nom clair à une vérification qui pourra être réutilisée.

Cette étape complète les petites fonctions fréquentes vues dans la théorie. Après cela, vous aurez déjà isolé plusieurs briques utiles, mais vous verrez aussi que cela ne suffit pas encore à centraliser complètement le formulaire.

  1. Toujours au-dessus du bloc POST, ajouter une fonction nommée estEmailValide().
  2. Cette fonction doit recevoir une chaîne de caractères.
  3. Elle doit utiliser filter_var() avec FILTER_VALIDATE_EMAIL.
  4. Elle doit retourner true si la valeur correspond à une adresse e-mail valide, sinon false.
  5. Repérer dans le traitement la partie qui vérifie le format de $email.
  6. Réécrire uniquement cette partie avec estEmailValide().
  7. Enregistrer, puis tester avec :
    • un e-mail invalide
    • un e-mail vide
    • un e-mail valide
  8. Faire enfin une série de tests plus complète pour vérifier que l'ensemble du formulaire se comporte toujours comme avant.
    • champ requis vide
    • champ facultatif vide
    • texte trop court
    • texte trop long
    • e-mail invalide
    • soumission valide

À la fin de cette étape, les principales petites fonctions réutilisables doivent exister déjà : lireChampTexte(), estRempli(), respecteLongueurMin(), respecteLongueurMax(), respecteLongueur() et estEmailValide().

Étape 07

Le code est maintenant un peu plus lisible, mais le problème de fond n'a pas encore disparu. Cette dernière étape sert à observer cette limite avant de passer à l'exercice suivant.

Le point important à comprendre est le suivant. Les petites fonctions aident déjà beaucoup, mais le fichier de traitement décrit encore le formulaire champ par champ. C'est cette limite qui justifiera ensuite le tableau de règles et la fonction centrale.

  1. Relire le bloc principal de /traitements/traiter-formulaire-contact.php.
  2. Observer que, malgré les nouvelles fonctions, vous devez encore :
    • lire les champs un par un
    • lancer les validations champ par champ
    • écrire encore plusieurs blocs conditionnels semblables
    • recopier manuellement les anciennes valeurs dans $anciennesValeurs
  3. Imaginer que le formulaire contienne 10 ou 15 champs.
  4. Constater que le fichier deviendrait encore long, même si certaines vérifications ont été extraites dans des fonctions.
  5. Ne modifiez plus le projet à cette étape.

À la fin de cet exercice, votre projet doit toujours fonctionner, mais vous devez maintenant voir clairement que l'extraction de petites fonctions, à elle seule, ne suffit pas encore à centraliser toute la description du formulaire. C'est précisément ce qui préparera l'exercice suivant.

Exercice 03

Dans cet exercice, vous allez passer à l'étape suivante. Jusqu'ici, vous avez surtout extrait de petites fonctions réutilisables. Cela a déjà rendu le traitement plus lisible, mais le fichier continue malgré tout à décrire le formulaire champ par champ.

Le but est maintenant de changer de niveau d'organisation. Au lieu d'écrire les validations une par une dans le traitement, vous allez d'abord décrire ce que le formulaire attend dans un tableau de règles. Ensuite, vous allez créer une fonction capable de parcourir ce tableau automatiquement. Enfin, une fois que tout fonctionne, vous rangerez cette logique réutilisable dans /core/gestion-formulaire.php.

Il est très important de respecter cet ordre. Si vous essayez de tout faire d'un seul coup, vous risquez de mélanger plusieurs difficultés : la logique de validation, la manipulation des tableaux, l'organisation des fichiers et les chemins de require_once. Ici, vous allez avancer une difficulté à la fois.

Pendant une grande partie de l'exercice, la nouvelle fonction centrale peut rester provisoirement dans /traitements/traiter-formulaire-contact.php. Vous ne la déplacerez dans /core qu'une fois qu'elle fonctionnera déjà correctement.

Structure

Repartir du projet terminé à la fin de l'exercice 02, c'est-à-dire avec :

  • la fonction e() déjà utilisée dans la vue
  • les petites fonctions de lecture et de validation déjà présentes dans le fichier de traitement
  • le formulaire toujours fonctionnel

Dupliquer ce projet dans un nouveau dossier nommé Exo-gestionnaire-formulaire-reutilisable-03.


                    📁 Exo-gestionnaire-formulaire-reutilisable-03/
                    ├── 📁 traitements/
                    │   └── 📄 traiter-formulaire-contact.php
                    └── 📄 index.php
                

Étape 01

Commencer par vérifier que votre copie de départ fonctionne correctement. Cette étape ne sert pas à modifier le code. Elle sert à vous assurer que vous repartez d'une base saine.

  1. Ouvrir le projet dans le navigateur.
  2. Faire un test valide.
    • remplir correctement tous les champs
    • soumettre le formulaire
    • vérifier que le message global de réussite s'affiche
  3. Faire ensuite un test invalide.
    • laisser au moins un champ requis vide
    • ou provoquer une erreur de longueur
    • vérifier que les messages d'erreur apparaissent sous les bons champs
    • vérifier que les anciennes valeurs sont bien réaffichées
  4. Vérifier aussi que la fonction e() continue à être utilisée dans index.php.
  5. Ne modifier encore aucun fichier.

À la fin de cette étape, vous devez être certain que votre copie de départ fonctionne correctement.

Étape 02

Vous allez maintenant créer le tableau de règles. Ce tableau ne servira pas encore à valider le formulaire. Dans cette étape, il sera seulement préparé.

Le but est de faire évoluer votre manière de raisonner. Jusqu'ici, vous écriviez surtout comment vérifier chaque champ. Maintenant, vous allez commencer à décrire ce que le formulaire attend. Plus tard, la fonction centrale pourra lire cette description et s'en servir pour valider automatiquement les champs.

  1. Ouvrir le fichier /traitements/traiter-formulaire-contact.php.
  2. Repérer le grand bloc if ($_SERVER['REQUEST_METHOD'] === 'POST'). C'est ce bloc qui ne s'exécute que lorsque le formulaire est soumis.
  3. Dans ce bloc, repérer l'endroit où commence actuellement la validation écrite champ par champ.
  4. Juste avant cette validation manuelle, créer une variable nommée $reglesDesChamps.
  5. Commencer par décrire uniquement le champ nom. Pour ce champ, indiquer dans le tableau :
    • qu'il est requis
    • qu'il possède une longueur minimale
    • qu'il possède une longueur maximale
  6. Enregistrer, puis recharger la page. Rien ne doit changer, car ce tableau n'est pas encore utilisé.
  7. Ajouter ensuite le champ prenom. Pour ce champ, indiquer :
    • qu'il n'est pas requis
    • qu'il possède une longueur maximale
  8. Enregistrer, puis recharger la page.
  9. Ajouter ensuite le champ email. Pour ce champ, indiquer :
    • qu'il est requis
    • qu'il doit être validé comme e-mail
  10. Enregistrer, puis recharger la page.
  11. Ajouter enfin le champ message. Pour ce champ, indiquer :
    • qu'il est requis
    • qu'il possède une longueur minimale
    • qu'il possède une longueur maximale
  12. Relire ensuite tout le tableau pour vérifier que les quatre champs sont bien décrits.
  13. Vérifier enfin que l'ancien traitement manuel est toujours présent. À ce stade, il ne faut encore rien supprimer.

À la fin de cette étape, le tableau $reglesDesChamps doit exister dans le bloc POST, mais le comportement du projet ne doit pas encore avoir changé.

Étape 03

Avant d'écrire toute la logique de validation, vous allez créer le squelette de la fonction centrale. Cette fonction existera, mais elle ne sera pas encore utilisée par le projet.

Pour le moment, elle peut rester dans le fichier de traitement. Vous la déplacerez plus tard dans /core, une fois qu'elle fonctionnera correctement.

  1. Toujours dans /traitements/traiter-formulaire-contact.php, vérifier que les petites fonctions créées dans l'exercice 02 se trouvent bien au-dessus du bloc POST.
  2. Juste sous ces petites fonctions, ajouter une nouvelle fonction nommée validerFormulaire().
  3. Cette fonction doit recevoir :
    • le tableau des règles
    • le tableau des données reçues
  4. Pour le moment, ne coder qu'un squelette minimal.
  5. Ce squelette doit retourner un tableau contenant déjà les trois clés suivantes :
    • erreurs
    • anciennesValeurs
    • messageGlobal
  6. Les deux premières clés doivent contenir un tableau vide, et la troisième une chaîne vide.
  7. Enregistrer, puis recharger la page. Rien ne doit être cassé.
  8. Vérifier bien que le formulaire fonctionne toujours comme avant. C'est normal, car cette nouvelle fonction n'est pas encore utilisée.

À la fin de cette étape, la fonction validerFormulaire() doit exister, mais elle n'est pas encore branchée au projet.

Étape 04

Vous allez maintenant construire la structure générale de validerFormulaire(). Dans cette étape, vous n'allez pas encore gérer les longueurs ni l'e-mail. Vous allez d'abord mettre en place la mécanique principale de la fonction.

Ce découpage est volontaire. Si vous essayez de tout coder d'un seul coup, vous risquez plus facilement de vous perdre. Ici, vous commencez par faire fonctionner la boucle, la lecture des valeurs et la gestion des champs requis ou facultatifs.

  1. Revenir dans la fonction validerFormulaire().
  2. Remplacer le retour immédiat par l'initialisation de trois variables locales :
    • $erreurs
    • $anciennesValeurs
    • $messageGlobal
  3. Ajouter ensuite une boucle foreach qui parcourt le tableau des règles.
  4. Dans cette boucle, récupérer à chaque tour :
    • le nom du champ courant
    • les règles du champ courant
  5. Ajouter ensuite la lecture de la valeur du champ courant avec lireChampTexte().
  6. Juste après, stocker cette valeur dans $anciennesValeurs à la clé correspondant au nom du champ. Cela servira plus tard au réaffichage si le formulaire contient des erreurs.
  7. Récupérer ensuite l'information indiquant si le champ est requis. Ici, cela signifie lire dans les règles du champ la valeur associée à la clé requis.
  8. Si cette information n'existe pas, considérer par défaut que le champ n'est pas requis.
  9. Ajouter maintenant le premier cas :
    • si le champ est requis et que la valeur est vide, enregistrer une erreur pour ce champ
  10. Après cette erreur, faire en sorte que la fonction passe directement au champ suivant, sans continuer les autres tests pour le champ courant.
  11. Ajouter ensuite le second cas :
    • si le champ n'est pas requis et que la valeur est vide, passer directement au champ suivant
  12. Comprendre pourquoi ces deux cas arrivent très tôt. Si un champ requis est vide, ou si un champ facultatif est vide, il est inutile de continuer les autres vérifications pour ce champ.
  13. Après la boucle, faire en sorte que la fonction retourne encore un tableau contenant erreurs, anciennesValeurs et messageGlobal.
  14. Enregistrer, puis relire calmement la fonction. À ce stade, elle doit déjà savoir :
    • parcourir les règles
    • lire les valeurs
    • stocker les anciennes valeurs
    • gérer les champs requis et facultatifs

À la fin de cette étape, la structure générale de validerFormulaire() doit être en place, même si toutes les vérifications ne sont pas encore ajoutées.

Étape 05

Vous allez maintenant compléter la seconde moitié de validerFormulaire(). Ici, vous ajoutez les vérifications plus précises : les longueurs, le format e-mail, puis le message global final.

  1. Revenir dans la fonction validerFormulaire().
  2. Ajouter d'abord le cas où le champ possède à la fois longueurMin et longueurMax.
    • dans ce cas, utiliser respecteLongueur()
    • si la règle n'est pas respectée, enregistrer une erreur puis passer directement au champ suivant
  3. Enregistrer, puis relire cette partie seulement.
  4. Ajouter ensuite le cas où seule longueurMin existe.
    • dans ce cas, utiliser respecteLongueurMin()
    • si la règle n'est pas respectée, enregistrer une erreur puis passer directement au champ suivant
  5. Enregistrer, puis relire cette partie seulement.
  6. Ajouter ensuite le cas où seule longueurMax existe.
    • dans ce cas, utiliser respecteLongueurMax()
    • si la règle n'est pas respectée, enregistrer une erreur puis passer directement au champ suivant
  7. Enregistrer, puis relire cette partie seulement.
  8. Ajouter ensuite la vérification spécifique à l'e-mail.
    • ne faire ce test que si le champ courant possède la règle type avec la valeur email
    • si le format n'est pas valide, enregistrer une erreur puis passer directement au champ suivant
  9. Après la boucle, ajouter la règle finale suivante :
    • s'il n'y a aucune erreur, vider le tableau $anciennesValeurs
    • dans ce cas, définir aussi $messageGlobal avec un message de réussite
    • sinon, définir $messageGlobal avec un message indiquant que le formulaire contient des erreurs
  10. Terminer ensuite la fonction en retournant un tableau contenant :
    • erreurs
    • anciennesValeurs
    • messageGlobal
  11. Enregistrer, puis relire calmement toute la fonction. Le but n'est pas d'aller vite. Le but est de comprendre l'ordre des vérifications.

À la fin de cette étape, la fonction validerFormulaire() doit être complète, mais elle n'est toujours pas encore branchée au traitement principal.

Étape 06

La fonction centrale existe maintenant. Vous allez seulement à présent remplacer l'ancien traitement manuel. N'effacez pas trop tôt l'ancien code. Commencer par brancher le nouveau système, puis supprimer l'ancien une fois le résultat vérifié.

  1. Dans le bloc principal POST de /traitements/traiter-formulaire-contact.php, repérer l'ancien traitement écrit champ par champ.
  2. Avant de le supprimer, ajouter un appel à validerFormulaire() en lui passant le tableau de règles et les données reçues.
  3. Stocker le résultat dans une variable, par exemple $resultatValidation.
  4. Récupérer ensuite à partir de ce résultat :
    • $erreurs
    • $anciennesValeurs
    • $messageGlobal
  5. Vérifier bien ce point. La fonction validerFormulaire() renvoie déjà le message global. Il n'est donc plus nécessaire de recalculer ce message à la main dans le fichier de traitement.
  6. Enregistrer, puis tester immédiatement avec :
    • une soumission valide
    • une soumission invalide
  7. Si tout fonctionne, supprimer alors seulement l'ancien bloc de validation manuelle devenu inutile.
  8. Enregistrer, puis refaire une série de tests complète :
    • champ requis vide
    • texte trop court
    • texte trop long
    • e-mail invalide
    • soumission valide

À la fin de cette étape, le fichier de traitement doit déjà fonctionner avec validerFormulaire().

Étape 07

Maintenant que la logique fonctionne, vous allez la déplacer dans un fichier réutilisable. Ce déplacement arrive seulement maintenant, car il est plus simple d'organiser correctement un code qui fonctionne déjà qu'un code encore en construction.

  1. À la racine du projet, créer un dossier nommé core s'il n'existe pas déjà.
  2. Dans ce dossier, créer un fichier nommé gestion-formulaire.php.
  3. Voici la structure attendue :
    
                                📁 Exo-gestionnaire-formulaire-reutilisable-03/
                                ├── 🆕📁 core/
                                │   └── 🆕📄 gestion-formulaire.php
                                ├── 📁 traitements/
                                │   └── 📄 traiter-formulaire-contact.php
                                └── 📄 index.php
                            
  4. Ouvrir ensuite /traitements/traiter-formulaire-contact.php.
  5. Repérer toutes les fonctions réutilisables. Cela inclut :
    • e()
    • lireChampTexte()
    • estRempli()
    • respecteLongueurMin()
    • respecteLongueurMax()
    • respecteLongueur()
    • estEmailValide()
    • validerFormulaire()
  6. Copier d'abord toutes ces fonctions dans /core/gestion-formulaire.php.
  7. Enregistrer ce nouveau fichier.
  8. Revenir ensuite dans le fichier de traitement.
  9. Ajouter en haut du fichier de traitement le chargement du fichier /core/gestion-formulaire.php avec require_once.
  10. Faire attention au chemin relatif. Le fichier de traitement se trouve dans /traitements. Il faut donc d'abord remonter d'un niveau, puis entrer dans /core.
  11. Une fois le chargement ajouté, supprimer ensuite du fichier de traitement les anciennes copies locales des fonctions. Il faut faire cela seulement maintenant, quand le nouveau fichier existe déjà et qu'il est déjà chargé.
  12. Vérifier aussi que le fichier de traitement conserve bien ses variables de départ, par exemple $erreurs, $anciennesValeurs et $messageGlobal.
  13. Enregistrer, puis recharger la page.
  14. Refaire un test valide puis un test invalide pour vérifier que le déplacement n'a rien cassé.

À la fin de cette étape, la logique réutilisable doit être rangée dans /core/gestion-formulaire.php, tandis que /traitements/traiter-formulaire-contact.php doit surtout se concentrer sur le traitement du formulaire concret.

Étape 08

Vous allez maintenant vérifier que la vue continue à exploiter correctement les résultats produits par la fonction centrale. Il est possible que vous n'ayez presque rien à modifier dans index.php. C'est normal. L'amélioration a surtout eu lieu dans /core et dans /traitements.

  1. Ouvrir le fichier index.php.
  2. Vérifier que /traitements/traiter-formulaire-contact.php est toujours inclus tout en haut du fichier, avant le HTML.
  3. Vérifier ensuite que chaque champ continue à utiliser $anciennesValeurs pour le réaffichage.
  4. Vérifier que chaque conteneur d'erreur continue à lire son message dans $erreurs.
  5. Vérifier que le message global continue à utiliser $messageGlobal.
  6. Vérifier aussi que la fonction e() reste bien utilisée dans la vue.
  7. Faire ensuite une série de tests :
    • soumission valide
    • soumission invalide
    • réaffichage des anciennes valeurs
    • message global de réussite
    • message global d'erreur
    • affichage échappé des anciennes valeurs

À la fin de cette étape, la page du formulaire doit continuer à afficher correctement les erreurs, le message global et les anciennes valeurs, tout en utilisant maintenant une logique de validation centralisée.

Projet Progressif 02 - Bonus : Intégrer votre gestionnaire de formulaire réutilisable

Cet exercice fait suite au Projet Progressif 02. L'objectif est d'intégrer dans votre projet le gestionnaire de formulaire réutilisable construit dans l'exercice précédent du chapitre actuel, afin de remplacer le traitement manuel du formulaire de contact par une solution plus propre et plus réutilisable.

Ici aussi, il faut respecter le même fil pédagogique que dans le cours. D'abord, identifier ce qui relève du traitement concret de la page. Ensuite, repérer ce qui peut être réutilisé ailleurs. Enfin, ranger cette logique réutilisable dans /core.

Attendu

À la fin de cet exercice bonus, le formulaire de la page contact.php doit toujours :

  • valider les champs côté serveur
  • afficher les erreurs sous les bons champs
  • afficher un message global
  • réafficher les anciennes valeurs en cas d'erreur

La logique réutilisable doit maintenant se trouver dans /core/gestion-formulaire.php, tandis que le traitement concret du formulaire de contact doit rester dans /traitements/traitement-contact.php.

Structure

Poursuivre le projet progressif 02, puis y faire évoluer l'organisation des fichiers de cette manière.


                    📁 projet-progressif/
                    ├── 📁 assets/
                    │   └── 📁 css/
                    │       └── 📄 style.css
                    ├── 📁 config/
                    │   └── 📄 config.php
                    ├── 📁 templates/
                    │   ├── 📄 header.php
                    │   └── 📄 footer.php
                    ├── 📁 core/
                    │   └── 🆕📄 gestion-formulaire.php
                    ├── 📁 traitements/
                    │   └── 📄 traitement-contact.php
                    ├── 📄 index.php
                    └── 📄 contact.php
                

Instructions

Étape 01

Commencer par vérifier que votre projet progressif 02 fonctionne correctement avant toute modification. Cette étape sert à sécuriser votre point de départ.

  1. Ouvrir la page contact.php dans le navigateur.
  2. Réaliser un test valide.
  3. Réaliser ensuite un test invalide.
  4. Vérifier que les erreurs, le message global et les anciennes valeurs fonctionnent correctement.

À la fin de cette étape, vous devez être certain que votre version actuelle du projet progressif 02 fonctionne correctement.

Étape 02

Créer maintenant le fichier qui accueillera le gestionnaire réutilisable. Dans cette étape, vous ne modifiez encore ni le comportement du formulaire, ni le traitement concret de la page de contact.

  1. À la racine du projet, créer un dossier nommé core s'il n'existe pas déjà.
  2. Dans ce dossier, créer un fichier nommé gestion-formulaire.php.
  3. Ouvrir votre projet Exo-gestionnaire-formulaire-reutilisable-03.
  4. Repérer les fonctions réutilisables de votre gestionnaire.
  5. Copier ces fonctions dans /core/gestion-formulaire.php.
  6. Vérifier que vous ne copiez pas un bloc principal complet de traitement. Vous ne devez récupérer ici que la logique réutilisable.
  7. Enregistrer le fichier.

À la fin de cette étape, le fichier /core/gestion-formulaire.php doit contenir uniquement les fonctions réutilisables du gestionnaire, sans traitement lancé automatiquement.

Étape 03

Brancher maintenant ce gestionnaire dans le traitement du formulaire de contact. Le but n'est pas de refaire tout le travail pas à pas, mais de faire en sorte que votre traitement concret s'appuie désormais sur la logique réutilisable rangée dans /core.

  1. Ouvrir le fichier /traitements/traitement-contact.php.
  2. Tout en haut de ce fichier, charger le fichier /core/gestion-formulaire.php avec require_once.
  3. Faire attention au chemin relatif. Comme ce fichier se trouve dans /traitements, il faut d'abord remonter d'un niveau, puis entrer dans /core.
  4. Enregistrer, puis recharger la page contact.php dans le navigateur.
  5. Vérifier qu'aucune erreur PHP n'apparaît.

À la fin de cette étape, le fichier /traitements/traitement-contact.php doit avoir accès au gestionnaire réutilisable situé dans /core.

Étape 04

Préparer maintenant les règles du formulaire de contact. Dans cette étape, vous n'allez pas encore supprimer l'ancien traitement manuel. Vous allez seulement construire le tableau de règles correspondant au formulaire actuel.

  1. Dans /traitements/traitement-contact.php, repérer le bloc exécuté lorsque la requête utilise la méthode POST.
  2. Vérifier que les variables de départ existent toujours avant ce bloc :
    • $erreurs
    • $messageGlobal
    • $anciennesValeurs
  3. Dans ce bloc, créer une variable nommée $reglesDesChamps.
  4. Décrire dans ce tableau les règles du formulaire de contact actuel.
  5. Enregistrer, puis recharger la page.
  6. Vérifier qu'aucune erreur PHP n'apparaît.
  7. Vérifier aussi que le comportement du formulaire n'a pas encore changé, puisque ce tableau n'est pas encore utilisé.

À la fin de cette étape, le tableau $reglesDesChamps doit exister dans /traitements/traitement-contact.php, mais l'ancien traitement manuel doit encore être présent.

Étape 05

Brancher maintenant la fonction centrale de validation. Ici, vous ne devez plus valider les champs un par un. Vous devez passer le tableau des règles à validerFormulaire(), puis récupérer le résultat complet.

  1. Toujours dans le bloc POST de /traitements/traitement-contact.php, ajouter un appel à validerFormulaire() en lui passant le tableau de règles et les données reçues.
  2. Stocker le résultat dans une variable, par exemple $resultatValidation.
  3. Récupérer ensuite dans cette variable :
    • le tableau $erreurs
    • le tableau $anciennesValeurs
    • la chaîne $messageGlobal
  4. Vérifier bien ce point. La fonction validerFormulaire() renvoie déjà le message global. Il n'est donc plus nécessaire de recalculer ce message à la main dans le traitement.
  5. Enregistrer, puis tester immédiatement avec :
    • une soumission valide
    • une soumission invalide
  6. Si tout fonctionne déjà correctement avec ce nouveau système, supprimer alors seulement l'ancien code de validation manuelle devenu inutile.
  7. Enregistrer, puis refaire une série de tests complète.

À la fin de cette étape, le fichier /traitements/traitement-contact.php ne doit plus contenir une suite de validations écrites champ par champ.

Étape 06

Vérifier maintenant ce qui doit rester inchangé dans la vue. Le but du bonus n'est pas de reconstruire la page contact.php, mais de brancher dessus un meilleur traitement.

  1. Ouvrir le fichier contact.php.
  2. Vérifier que /traitements/traitement-contact.php est bien chargé avant l'affichage du HTML.
  3. Vérifier que le formulaire continue à utiliser :
    • $erreurs pour l'affichage des erreurs
    • $messageGlobal pour le message global
    • $anciennesValeurs pour le réaffichage des valeurs
    • e() pour l'affichage échappé des anciennes valeurs
  4. Si tout fonctionne déjà correctement, ne modifiez pas la vue inutilement.

À la fin de cette étape, vous devez constater que le plus gros du bonus concerne surtout /traitements/traitement-contact.php et /core/gestion-formulaire.php.

Étape 07

Réaliser enfin une série de tests complète pour vérifier que le comportement du formulaire n'a pas changé après l'intégration du gestionnaire réutilisable.

  1. Tester une soumission entièrement valide.
  2. Tester ensuite plusieurs cas invalides en contournant si nécessaire les vérifications HTML via l'inspecteur du navigateur.
  3. Vérifier à chaque fois que :
    • les messages d'erreur s'affichent sous les bons champs
    • le message global indique bien qu'il y a des erreurs
    • les anciennes valeurs sont réaffichées
    • les champs réaffichés restent sécurisés à l'écran
  4. Réaliser enfin un test d'injection.
    • saisir dans un champ une valeur contenant du HTML ou du JavaScript
    • provoquer une erreur sur un autre champ pour forcer le réaffichage
    • vérifier que le contenu est réaffiché comme du texte et n'est pas interprété par le navigateur
  5. Relire enfin /traitements/traitement-contact.php et constater que ce fichier contient moins de répétitions qu'avant.

À la fin de ce bonus, le formulaire de contact doit toujours se comporter comme dans le projet progressif 02, mais son traitement doit maintenant s'appuyer sur votre gestionnaire de formulaire réutilisable, rangé dans /core et appelé depuis /traitements/traitement-contact.php.