Formation PUB020 : WordPress, 2023 Traiter un formulaire côté site Web dans WordPress

35.2 Valider un formulaire côté serveur avec PHP


Cette fiche peut être utilisée pour un site PHP vanille ou encore pour un site WordPress.

La validation d'un formulaire Web est une tâche complexe. Il faut en effet s'assurer que les données entrées par l'usager correspondent au format attendu, qu'elles soient valables et qu'elles ne compromettent pas la sécurité du site Web.

Normalement, une fois que l'usager a cliqué sur le bouton de soumission, les données entrées devraient correspondre au format attendu puisqu'une validation a déjà été effectuée côté client.

Cependant, puisqu'il est possible que le code JavaScript ne soit pas exécuté, il est absolument nécessaire de refaire la validation côté serveur.

Dans cette fiche :

D'abord, se protéger contre les attaques XSS

Afin de se protéger des attaques XSS, les données saisies dans le formulaire devront être converties avec htmlspecialchars().

Grâce à cette précaution, aucune balise <script> et aucun apostrophe ou guillemet ne pourra causer de comportement inattendu.

Site PHP vanille

Dans un site PHP vanille, cette petite boucle convertira toutes les informations en provenance du formulaire.

Page de traitement du formulaire (PHP)

// *** protection XSS ******************************************************************
foreach ($_POST as $cle => $valeur) {
    $_POST[$cle] = htmlspecialchars($valeur, ENT_QUOTES);
}

Site WordPress

Dans le cas d'un formulaire WordPress, il faut prendre une précaution supplémentaire en appelant stripslaches_deep().

En effet, WordPress utilise les guillemets magiques, c'est-à-dire qu'il ajoute automatiquement une barre oblique inverse (\) devant les apostrophes et guillemets des données soumises. Cette pratique n'assure cependant pas que les apostrophes et guillemets seront correctement affichés si on doit réafficher les données dans le formulaire.

De plus, on utilisera esc_attr() plutôt que htmlspecialchars() puisque cette fonction, spécifiquement conçue pour WordPress, va plus loin, notamment en empêchant que le texte soit du utf-8 invalide.

Page de traitement du formulaire (WordPress)

// *** protection XSS ******************************************************************
foreach ( $_POST as $cle => $valeur ) {
    $_POST[$cle] = esc_attr( stripslashes_deep( $valeur ) );
}

Nettoyer les données entrées

Avant de traiter des données entrées par l'usager, il est primordial de procéder au nettoyage de ces données. On rencontre parfois les termes assainir ou désinfecter. En anglais, on dira sanitize inputs.

Le nettoyage des données touche à différents aspects :

  • la validation (les données respectent-elles le format attendu et est-ce qu'elles sont valables?)
  • la filtration ou la transformation (modifier les données pour qu'elles respectent certaines contraintes techniques, par exemple supprimer les espaces et caractères spéciaux d'une chaîne qui doit être utilisée dans un URL ou encore normaliser un numéro de téléphone pour s'assurer que les 10 chiffres entrés soient désormais au format 999 999-9999)
  • la sécurité (empêcher que certains caractères ne soient interprétés)

Nous traiterons ici du premier aspect : la validation.

Les aspect filtration et sécurité seront traités lors de l'enregistrement des données.

Vérifier le format attendu

Pour chaque information obligatoire, il faut vérifier si une valeur a été entrée.

La base de données fournit plusieurs indices sur les formats attendus :

  • Un champ VARCHAR(100) ne doit pas dépasser 100 caractères.
  • Un champ DATE doit respecter un format de date valide.

Certaines informations requièrent une validation plus spécifique :

  • Code postal
  • Numéro de téléphone
  • Adresse courriel
  • Mot de passe (longueur minimale, complexité)

Vérifier si les données sont valables

La vérification du format attendu n'est pas suffisante. Par exemple :

  • Une valeur numérique pourrait être négative alors que le contexte exige une valeur positive.
  • Une date peut être bien formée mais située dans le passé alors qu’on attend une date future.
  • Une valeur choisie dans une liste déroulante peut avoir été falsifiée.

Il faut donc s'assurer que les données sont valables, c'est-à-dire qu'elles correspondent aux valeurs acceptées.

Pour vous assurer que les données soient valables :

Il faut également s'assurer que chacune des validations côté client soit correctement reprise côté serveur.

Si au moins une erreur est détectée

Si au moins une erreur est détectée, il faut retenir le message d'erreur dans une variable de session puis permettre à l'usager de corriger la situation en réaffichant le formulaire avec les informations qui y ont été entrées.

Le message d'erreur sera affiché en haut du formulaire. Il doit indiquer clairement chacune des erreurs rencontrées.

Enregistrer les données sans compromettre la sécurité

Quand les données ont passé toutes les validations, on peut procéder à l'enregistrement ou aux opérations requises pour finaliser le traitement du formulaire.

Confirmation à l'usager

Bien important, une fois le traitement complété, un message devra indiquer à l'usager si l'opération a été réussie ou non.

Un exemple vaut mille mots

Voici un algorithme qui vous aidera à ne rien oublier lorsque vous traitez un formulaire.

Le formulaire est hétéroclite mais il permet d'illustrer différentes validations.

Cet algorithme est bien sûr perfectible. Libre à vous de l'utiliser, de le perfectionner ou de développer votre propre algorithme.

Page de traitement du formulaire (PHP 8.x)

// *** protection XSS ******************************************************************
foreach ($_POST as $cle => $valeur) {
    $_POST[$cle] = htmlspecialchars($valeur, ENT_QUOTES);
}

// *** initialisation des variables pour clarifier le code *****************************
$modele = $_POST['modele'];
$description = $_POST['description'];
$annee = $_POST['annee'];
$courriel = $_POST['courriel'];
$codePostal = $_POST['codepostal'];
$telephone = $_POST['telephone'];
$prix = $_POST['prix'];
$date = $_POST['date'];
$infoSaisieAvecBoutonRadio = $_POST['info'] ?? '';   // si l'usager n'a sélectionné aucun bouton radio, rien n'est envoyé dans $_POST.
 
// *** validations côté serveur ******************************************************** 
$messageErreur = [];
 
// format attendu : champs obligatoires
if ('' == $modele) {
    $messageErreur[] = 'Le modèle est requis.';
}
 
if ('' == $description) {
    $messageErreur[] = 'La description est requise.';
}
 
// format attendu : longueur des champs
if (!empty($annee) && 4 != mb_strlen($annee)) {
    $messageErreur[] = 'L\'année, lorsque fournie, doit comporter exactement 4 caractères.';
}
 
if (mb_strlen($description) > 100) {
    $messageErreur[] = 'La description ne doit pas comporter plus de 100 caractères.';
}
 
// format attendu : courriel
$courriel = filter_var($courriel, FILTER_SANITIZE_EMAIL);   // on commence par enlever les caractères non acceptés
if (!filter_var( $courriel, FILTER_VALIDATE_EMAIL)) {
    $messageErreur[] = 'Le courriel n\'est pas valide. Il doit être au format unnom@undomaine.uneextension.<br> &nbsp; &nbsp; Il doit comporter un seul caractère @.<br> &nbsp; &nbsp; Ce caractère doit être suivi d\'un nom de domaine qui contient au moins un point puis une extension.<br> &nbsp; &nbsp; Les caractères spéciaux ne sont pas acceptés.';}
 
// format attendu : code postal canadien
if(!preg_match("/^[ABCEGHJKLMNPRSTVXYabceghjklmnprstvxy][0-9][ABCEGHJKLMNPRSTVWXYZabceghjklmnprstvwxyz] ?[0-9][ABCEGHJKLMNPRSTVWXYZabceghjklmnprstvwxyz][0-9]$/i",$codePostal)) {
    $messageErreur[] = 'Le code postal n\'est pas valide. Il doit être au format A9A 9A9.<br> &nbsp; &nbsp; Les lettres D, F, I, O, Q et U ne sont pas acceptées.<br> &nbsp; &nbsp; Les lettres W et Z sont acceptées mais pas en première position.';
}

// format attendu : téléphone
if(!preg_match("/^[0-9]{3} [0-9]{3}-[0-9]{4}$/", $telephone)) {
    $messageErreur[] = 'Le téléphone n\'est pas valide. Il doit être au format 999 999-9999.';
}
 
// données valables : champs numériques
if (!empty($annee) && !ctype_digit($annee)) {
    $messageErreur[] = 'L\'année n\'est pas valide. Lorsqu\'elle est fournie, elle doit être un entier.';
}
 
//données valables : valeur décimale
if (!empty($prix)) {
    // si on n'a pas besoin de vérifier la valeur maximale, omettre le else
    if (!is_numeric($prix)) {
        $messageErreur[] = 'Le prix n\'est pas valide. Lorsqu\'il est fourni, il doit être un nombre qui peut comporter une partie décimale.';
    } else {
        // si on vérifie avec l'expression régulière, pas besoin de vérifier le is_numeric()
        if (!preg_match("/^[0-9]{1,3}([.,][0-9]{1,2})?$/", $prix)) {
            $messageErreur[] = 'Le prix n\'est pas valide. Lorsqu\'il est fourni, il doit être au format 999.99.';
        }
    }
}
 
// données valables : clés étrangères
$requete = "...";   // voir "Valider la clé étrangère saisie dans un formulaire avec PHP"

 

// données valables : date
$format = 'Y-m-d\TH:i';   // un input datetime-local retourne une date au format aaaa-mm-jjThh:mm
$nouvelleDate = DateTime::createFromFormat($format, $dateSaisie);   // on tente de recréer une date PHP à partir de la chaîne saisie
if ($nouvelleDate) {

    // si la date contenue dans l'objet ne correspond pas à la date saisie

    if ($nouvelleDate->format($format) !== $dateSaisie) {
        $messageErreur[] = 'La date est invalide.';
    }
} else {
    $messageErreur[] = 'La date a un format incorrect.';
}


...

if (0 == $stmt->num_rows) {   // sous WordPress, on fera plutôt if ( $wpdb->num_rows > 0 )
    $messageErreur[] = 'Le modèle choisi n\'est pas valide.';
}

// réagir selon que les données sont valides ou non
if (count($messageErreur) > 0) {
     // mécanisme pour afficher les erreurs de validation à l'usager
 
} else {
    // enregistrement des données avec mécanisme pour indiquer à l'usager si l'enregistrement a fonctionné
}

▼Publicité

Veuillez noter que le contenu de cette fiche vous est partagé à titre gracieux, au meilleur de mes connaissances et sans aucune garantie.
Merci de partager !
Soumettre