Les formulaires et PHP5

Guillaume Rossolini

Comment envoyer des informations à un site Web ? Comment est-il possible de publier une news sur son site, voire de gérer tout son site (backend) ?
Ce tutoriel traite de différentes manières permettant de récupérer des informations depuis un formulaire HTML à l'aide d'un script PHP.

Commentez ♪

- Titre : Les formulaires et PHP5

- Auteur : Guillaume Rossolini

- Parution : 30 juillet 2006

- Mise à jour : 11 septembre 2006

- Licence : Copyright ® 2006 Guillaume Rossolini. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. Remerciements

- I-B. Problématique

- II. Vision d'ensemble

- II-A. Partie HTML : le formulaire lui-même

- Obligatoire : la balise <form>

- Fondamental : les contrôles <input>, <textarea>, <select>, etc.

- Facultatif, mais pratique : <fieldset>, <legend> et <label>

- II-B. Partie PHP : traitement du formulaire

- Les superglobales $_GET, $_POST et $_FILES

- Les variables

- III. Mise en pratique

- III-A. Uniquement du texte : application/x-www-form-urlencoded

- Le contrôle de saisie de texte court : <input type=« text » />

- Le contrôle caché : <input type=« hidden » />

- Le contrôle de choix alternatifs : <input type=« radio » />

- Le contrôle de choix multiples : <input type=« checkbox » />

- Le contrôle de saisie de texte long : <textarea></textarea>

- Le contrôle de choix alternatifs ou multiples : <select></select>

- III-B. Du texte + des fichiers : multipart/form-data

- Les contrôles classiques

- Le contrôle de choix de fichier : <input type=« file » />

- Les fonctions PHP

- IV. Formulaire réparti sur plusieurs pages

- IV-A. Les formulaires

- Informations de connexion

- Informations personnelles

- Études

- Résumé

- IV-B. La page d'index

- Mise en place des variables

- Les liens en haut de la page

- Traitement des données utilisateur

- IV-C. Fonctionnement

- IV-C-1. Mise en place

- IV-C-2. Navigation

- IV-C-3. Sécurité

- V. Améliorations de l'expérience utilisateur

- V-A. Les boutons « actualiser » et « précédent » du navigateur

- La fonction header()

- Bien organiser son code

- V-B. Un peu de JS (JavaScript)

- Les évènements disponibles

- Vérifier que le formulaire est complètement rempli

- Contrôler que les deux mots de passe sont identiques

- V-C. Un peu de CSS (feuilles de style)

- VI. Conclusion

- VI-A. Épilogue

- VI-B. Liens

I - Introduction

I-A - Remerciements

Un grand merci à siddh et titoumimi pour leurs commentaires, ainsi qu'à Maxoo pour m'avoir fourni la feuille de style utilisée en fin d'article.

I-B - Problématique

Il nous arrive fréquemment de voir des formulaires sur Internet. Ils prennent l'allure d'un sondage, d'une boîte de connexion ou encore d'une interface permettant de publier un message sur un forum.
Ces formulaires permettent à l'internaute d'envoyer des informations au site Web. Ces informations peuvent être enregistrées (cas d'un forum), traitées pour sortir des statistiques (cas d'un sondage) ou encore comprises comme des ordres (panneau d'administration de site). Il est également possible d'envoyer des fichiers sans passer par le protocole FTP.

Les formulaires sont des éléments de la page HTML affichée par le navigateur. Ils ont généralement une destination : le script qui agit lorsque l'internaute valide le formulaire.

La première partie de ce tutoriel, « vision d'ensemble », sert de référence à la suite.
Le reste du tutoriel montre différentes utilisations de formulaires en les illustrant par des exemples concrets.

Les formulaires ne sont pas complexes, mais il y a beaucoup de choses à dire…

Les objectifs du tutoriel

- voir tous les contrôles HTML des formulaires ;

- voir comment traiter en PHP les informations envoyées par un formulaire ;

- commencer à voir comment JavaScript peut être utile ;

- commencer à voir comment les feuilles de style (CSS) peuvent être utiles.

II - Vision d'ensemble

Je pense qu'il est nécessaire de commencer par voir tous les aspects d'un formulaire. Nous reviendrons par la suite sur les éléments les uns après les autres. Nous allons commencer par les formulaires eux-mêmes, côté HTML, puis nous verrons rapidement comment recevoir les informations d'un formulaire avec PHP.

II-A - Partie HTML : le formulaire lui-même

Obligatoire : la balise <form>

Un formulaire est une balise HTML contenant des éléments permettant à un utilisateur de saisir des informations.

Code 1.1 : Formulaire minimum

<form method="post" action="form.php">
<input type="submit" value="Envoyer" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-1.php

La balise <form> est invisible à l'internaute. Elle sert uniquement à indiquer au navigateur Web qu'il a affaire à un formulaire. Nous pouvons voir ici les deux paramètres les plus courants : « method » et « action ».

Propriétés de la balise <form>

- action
Sa valeur détermine l'adresse du script qui recevra le contenu du formulaire, une fois transmis.
Cet attribut est requis.

- enctype
Sa valeur détermine le type de données envoyées par le formulaire : sont-ce seulement des informations textuelles ou bien y a-t-il également des fichiers joints ?
Cet attribut est optionnel et vaut « application/x-www-form-urlencoded » par défaut. Si l'on souhaite envoyer des fichiers en plus de texte, il faut donner la valeur « multipart/form-data ».

- id
Sa valeur est utilisée pour les manipulations du DOM (Document Object Model) à l'aide d'un script comme ECMAScript (alias JavaScript ou JScript, dans le cas du Web).

- method
Les valeurs peuvent être « get » ou bien « post ».
Cet attribut est optionnel et vaut « get » s'il est omis.

Quelle que soit la méthode de soumission choisie, le formulaire enverra ses informations à une page de destination spécifiée grâce à la propriété action.

Les deux méthodes de soumission de formulaires

- GET : Les variables sont transmises par l'URL, ce qui les rend visibles et modifiables très simplement par l'internaute.

- POST : Les variables sont transmises de manière cachée : c'est généralement la méthode préférée des développeurs.

Au cours de ce tutoriel, j'utiliserai exclusivement la méthode POST, car c'est la plus courante.

Un formulaire a généralement pour vocation de permettre à l'internaute de saisir des informations. La plus connue des balises permettant d'y parvenir s'appelle <input>.

Fondamental : les contrôles <input>, <textarea>, <select>, etc.

Les contrôles sont les éléments HTML qui permettent de saisir des informations. Il en existe trois différents : input, textarea et select.
La balise input a une signification très simple : elle permet de « saisir » des informations.

La forme que prend cette balise est définie par la propriété 'type' :

- text : Permet d'obtenir une petite boîte de saisie de texte dans la page HTML

- submit : Envoie le formulaire

- reset : Rétablit le formulaire à son état d'origine

- radio : Permet d'obtenir une liste dont un seul choix est possible ; il faut utiliser plusieurs boutons radio du même nom (propriété name) pour que cela soit utile

- password : Même principe que text, prévu pour les mots de passe : le texte n'est pas affiché clairement lors de la saisie

- image : Même principe que submit, mais c'est une image au lieu d'un bouton

- hidden : Même principe que text mais celui-ci n'est pas affiché du tout dans la page

- file : Affiche un bouton permettant de sélectionner un fichier de l'ordinateur ; la plupart des navigateurs l'accompagnent d'une case de texte contenant le chemin d'accès au fichier

- checkbox : Permet d'obtenir une case à cocher

- button : Simplement un bouton ayant la même allure que submit ou reset, mais celui-ci n'a pas d'utilité propre

Code 1.2 : Formulaire mixte

<form method="POST" action="test.php" enctype="multipart/form-data">
Une ligne de texte (quelques mots) :
<input type="text" name="text" /><br />

Un mot de passe :
<input type="password" name="password" /><br />

Case à cocher :
<input type="checkbox" name="checkbox_1" checked="checked" />
<input type="checkbox" name="checkbox_2" />
<input type="checkbox" name="checkbox_3" /><br />

Bouton radio à sélectionner :
<input type="radio" name="radio" checked="checked" />
<input type="radio" name="radio" />
<input type="radio" name="radio" /><br />

Envoi de fichier :
<input type="file" name="file" /><br />

Bloc de texte (plusieurs lignes) :
<textarea name="textarea" cols="20" rows="7"></textarea><br />

Liste de valeurs :
<select name="select">
<option value="Option 1">Option 1</option>
<option value="Option 2">Option 2</option>
<option value="Option 3">Option 3</option>
</select><br />

Champ caché :
<input type="hidden" name="hidden" /><br />

Image servant de bouton :
<input type="image" src="http://www.developpez.net/forums/images/logo16.gif" /><br />

Bouton minimal :
<input type="button" value="Bouton inutile" /><br />

Bouton d'envoi :
<input type="submit" value="Envoyer" /><br />

Bouton de réinitialisation :
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-2.php

Nous pouvons noter que les contrôles actifs (les boutons) portent une valeur (le texte à afficher), tandis que les contrôles de saisie (les autres) portent un nom. Nous verrons plus tard qu'ils peuvent également porter une valeur.
L'élément textarea permet de saisir du texte sur plusieurs lignes en utilisant la touche « Entrée », ce qui est impossible avec input type=« text ».
Un select est une alternative intéressante à des boutons radio s'il y a plus de trois choix possibles ou bien si le nombre de choix est dynamique.

Il peut y avoir un ou plusieurs submit par formulaire. Tous auront le même effet : soumettre le formulaire au script défini dans la propriété form action="".

Essayez ce formulaire avec la méthode POST puis la méthode GET pour voir la différence.

Facultatif, mais pratique : <fieldset>, <legend> et <label>

L'élément fieldset permet de regrouper les contrôles par thème. Utilisé conjointement à un élément legend, cela permet d'obtenir un formulaire structuré (à la fois dans le code et visuellement dans le navigateur).

Code 1.3 : Exemple de fieldset+legend

<form method="post" action="form.php">
<fieldset>
<legend>Informations personnelles</legend>
Prénom : <input type="text" name="first_name" /><br />
Nom : <input type="text" name="last_name" /><br />
</fieldset>
<fieldset>
<legend>Informations virtuelles</legend>
Pseudonyme : <input type="text" name="nickname" /><br />
Site Web : <input type="text" name="website" /><br />
Messagerie instantanée : <input type="text" name="instant_messenger" /><br />
</fieldset><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-3.php

Chaque fieldset ne contient qu'un seul legend mais peut contenir plusieurs contrôles. Il est possible d'assigner un élément label à chaque contrôle de la manière suivante :

Code 1.4 : Exemple de label

<form method="post" action="form.php">
<fieldset>
<legend>Informations personnelles</legend>
<label>
Prénom : <input type="text" name="first_name" />
</label><br />
<label>
Nom : <input type="text" name="last_name" />
</label><br />
</fieldset>
<fieldset>
<legend>Informations virtuelles</legend>
<label>
Pseudonyme : <input type="text" name="nickname" />
</label><br />
<label>
Site Web : <input type="text" name="website" />
</label><br />
<label>
Messagerie instantanée : <input type="text" name="instant_messenger" />
</label><br />
</fieldset>
<fieldset>
<legend>Fichiers joints</legend>
<label>
Fichier 1 : <input type="file" name="file_1" />
</label><br />
<label>
Fichier 2 : <input type="file" name="file_2" />
</label><br />
</fieldset><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-4.php

Cet exemple est identique au précédent à un détail près : si l'on clique sur un label, le curseur est maintenant déplacé dans le contrôle associé.

Il est possible d'associer les labels et les contrôles d'une autre manière (explicitement avec la propriété « for »), mais c'est moins intuitif et cela peut entrer en conflit avec certaines de vos habitudes de programmation, dans la mesure où cela fait intervenir la propriété « id » de votre contrôle. Cela permet de placer les labels où on le souhaite dans le formulaire. Bien entendu, rien ne vous empêche de répéter les labels.

Code 1.5 : Exemple d'association explicite

<form method="post" action="form.php">
<ul>
<li>Informations personnelles
<ul>
<li><label for="first_name">Prénom</label></li>
<li><label for="last_name">Nom</label></li>
</ul>
</li>
<li>Informations virtuelles
<ul>
<li><label for="nickname">Pseudonyme</label></li>
<li><label for="website">Site Web</label></li>
<li><label for="instant_messenger">Messagerie instantanée</label></li>
</ul>
</li>
<li>Fichiers joints
<ul>
<li><label for="file_1">Fichier 1</label></li>
<li><label for="file_2">Fichier 2</label></li>
</ul>
</li>
</ul>

<fieldset>
<legend>Informations personnelles</legend>
<input type="text" name="first_name" id="first_name" /><br />
<input type="text" name="last_name" id="last_name" /><br />
</fieldset>
<fieldset>
<legend>Informations virtuelles</legend>
<input type="text" name="nickname" id="nickname" /><br />
<input type="text" name="website" id="website" /><br />
<input type="text" name="instant_messenger" id="instant_messenger" /><br />
</fieldset>
<fieldset>
<legend>Fichiers joints</legend>
<input type="file" name="file_1" id="file_1" /><br />
<input type="file" name="file_2" id="file_2" />
</fieldset><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-5.php

II-B - Partie PHP : traitement du formulaire

Les superglobales $_GET, $_POST et $_FILES

Les informations envoyées au moyen d'un formulaire sont stockées dans les tableaux superglobaux de l'environnement PHP.

Voici les tableaux utilisés :

- $_GET : Les variables de l'URL (méthode GET)

- $_POST : Les variables envoyées par la méthode POST

- $_FILES : Les fichiers envoyés par formulaire

- $_REQUEST : Les variables $_POST, $_GET et $_COOKIE confondues en une seule (cette superglobale $_REQUEST est à éviter) ; $_FILES n'est plus inclus depuis la version 4.3 de PHP

Il est possible d'utiliser print_r() pour afficher le contenu d'un formulaire :

Code 1.6 : Afficher le contenu d'un formulaire

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<b>Variables</b> :<br />';
echo '<pre>';
print_r($_POST);
echo '</pre>';
}

if(!empty($_FILES)){
//
// Debug
//
echo '<b>Fichiers</b> :<br />';
echo '<pre>';
print_r($_FILES);
echo '</pre>';
}

?>

<form method="post" action="form.php" enctype="multipart/form-data">
<label>Titre : <input type="text" name="title" /></label><br />
<label>Message : <br />
<textarea name="message" cols="20" rows="7"></textarea></label><br />
<label>Fichier joint : <input type="file" name="file" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-6.php

Tout au long de ce tutoriel, j'effectuerai mes traitements dans une condition protégée par !empty(). C'est une bonne habitude à prendre, afin de n'effectuer les traitements que dans le cas où le formulaire a été transmis. Si vous ne le faites pas, il est probable que PHP vous affiche des avertissements « undefined index » lorsque le formulaire n'a pas encore été envoyé. Protéger par isset() n'est pas suffisant.

Le code ci-dessus et la plupart des exemples suivants sont vulnérables à une faille XSS. Pensez à toujours protéger l'affichage à l'aide de la fonction htmlentities() ou au moins htmlspecialchars().

Les variables

Afin de parcourir le contenu d'un formulaire, il est nécessaire de manipuler le tableau $_POST comme un tableau associatif. Cela signifie que chaque nom de champ (propriété name de chaque contrôle) du formulaire est utilisé comme index de $_POST. Dans l'exemple suivant, deux contrôles portent un nom : « login » devient $_POST['login'] et « password » devient $_POST['password'].

Code 1.7 : Exemple de formulaire simple et affichage du contenu

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';

//
// Récupération normale des informations
//
echo "<b>Nom d'utilisateur</b> : ".$_POST['login'].'<br />';
echo "<b>Mot de passe</b> : ".$_POST['password'].'<br /><br />';
}

?>

<form method="post" action="form.php">
<label>Nom d utilisateur : <input type="text" name="login" /></label><br />
<label>Mot de passe : <input type="password" name="password" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-7.php

Pour les grands formulaires, il est parfois moins pénible d'utiliser la structure foreach. Tout dépend de l'utilisation du formulaire. N'adaptez surtout pas les noms des contrôles de manière à les afficher directement, car ce serait prendre le problème à l'envers !

Code 1.8 : Exemple INCORRECT

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';

//
// Récupération normale des informations
//
foreach($_POST as $field => $value){
echo '<b>'.$field.'</b> : '.$value.'<br />';
}
echo '<br /><br />';
}

?>

<form method="post" action="form.php">
<label>Nom d utilisateur : <input type="text" name="Nom d utilisateur" /></label><br />
<label>Mot de passe : <input type="password" name="Mot de passe" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-8.php

Code 1.9 : Exemple correct de parcours du résultat d'un formulaire

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';

//
// Récupération normale des informations
//
foreach($_POST as $field => $value){
echo '<b>'.$field.'</b> : '.$value.'<br />';
}
echo '<br /><br />';
}

?>

<form method="post" action="form.php">
<label>Nom d utilisateur : <input type="text" name="login" /></label><br />
<label>Mot de passe : <input type="password" name="password" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/1-9.php

III - Mise en pratique

Il faut toujours garder à l'esprit qu'un formulaire contient des informations envoyées par l'internaute. Il n'est pas possible de se fier à ce qu'envoie un internaute, car il peut manipuler les formulaires comme bon lui semble. L'aspect sécurité est donc fondamental.
La meilleure pratique de sécurité est d'inclure le minimum possible de choses dans chaque formulaire. Si l'utilisateur a déjà renseigné une information, il est préférable d'utiliser une base de données ou des sessions pour la conserver durant sa navigation, plutôt que d'utiliser un champ de type hidden dans le formulaire (car cela donnerait à l'internaute l'occasion de modifier cette information, ce qui peut ne pas être prévu par votre application).
De plus, il convient de filtrer les variables envoyées au moyen d'un formulaire, de manière à vérifier qu'elles contiennent effectivement ce à quoi nous nous attendons. Il vous faut déterminer votre propre politique de filtrage pour chaque variable transmise par formulaire.

III-A - Uniquement du texte : application/x-www-form-urlencoded

Voici le plus courant (à tort ?) des encodages de formulaires. Il correspond à la valeur par défaut de la propriété enctype, qu'il n'est donc pas nécessaire de spécifier. Attention à ne pas envoyer autre chose que du texte au format ASCII avec cet entype.

Code 2.1 : Ces deux lignes sont équivalentes

<form action="form.php" enctype="application/x-www-form-urlencoded">
<form action="form.php">

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-1.php

Il est possible de donner une valeur par défaut à un contrôle input à l'aide de sa propriété value="". Attention, les contrôles textarea et select fonctionnent différemment.

Le contrôle de saisie de texte court : <input type=« text » />

La balise input type=« text » a été vue dans la première partie de ce tutoriel. Elle permet à l'internaute de saisir une petite quantité de texte (généralement quelques dizaines de caractères), en une seule ligne.
Reprenons l'un des premiers exemples de ce tutoriel afin de mieux organiser notre code PHP.

Code 2.2 : Prévoir les noms de variables PHP dans le formulaire

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre>';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
<fieldset>
<legend>Informations personnelles</legend>
<label>Prénom : <input type="text" name="personal[first_name]" /></label><br />
<label>Nom : <input type="text" name="personal[last_name]" /></label><br />
</fieldset>
<fieldset>
<legend>Informations virtuelles</legend>
<label>Pseudonyme : <input type="text" name="virtual[nickname]" /></label><br />
<label>Site Web : <input type="text" name="virtual[website]" /></label><br />
<label>Messagerie instantanée : <input type="text" name="virtual[instant_messenger]" /></label><br />
</fieldset><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-2.php

J'ai organisé les noms des contrôles selon la structure du formulaire. Les crochets [ ] dans le name="" permettent de définir un nom d'index pour le tableau associatif PHP. Ici, personal[first_name] signifie que la variable PHP correspondante est $_POST['personal']['first_name'].

J'utilise systématiquement <?php echo basename(__FILE__); ?> dans la propriété form action="", car cela me permet de donner le nom que je souhaite à mon script sans devoir me préoccuper de tout mettre à jour.

Le contrôle caché : <input type=« hidden » />

Ce champ est à utiliser avec précaution. Il donne une impression de sécurité, car il n'est pas visiblement présent dans la page. Cependant, un utilisateur averti saura en tirer parti s'il est mal utilisé. Il est donc fondamental de ne l'introduire que dans des situations où il aura un impact minimal et contrôlé.

Un bon exemple est un formulaire pour envoyer un commentaire sur un blog. Imaginons que l'utilisateur ait suivi un lien le conduisant à notre formulaire, qui lui permet d'entrer le titre de son message (je ne mets pas de contenu afin de simplifier l'exemple). De manière à savoir dans quel billet le commentaire sera enregistré, il est possible d'utiliser un contrôle hidden. L'utilisateur n'aurait aucun intérêt à modifier la valeur de ce champ puisque son message n'apparaîtrait pas où il le souhaite et puisque cela ne pourrait rien lui apporter.
Le contrôle hidden permet ainsi de stocker l'identifiant (de préférence numérique) du billet de destination.

Code 2.3 : Exemple prévu pour un blog

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';

//
// Récupération normale des informations
//
echo '<b>Titre</b> : '.$_POST['title'].'<br />';
echo '<b>Dans le billet numéro</b> : '.$_POST['entry_id'];
echo '<br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
<label>Titre : <input type="text" name="title" /></label><br />
<input type="hidden" name="entry_id" value="3" /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-3.php

Le contrôle de choix alternatifs : <input type=« radio » />

Cet élément permet de répondre à une question ne pouvant avoir qu'une seule réponse parmi une liste précise de propositions. Il s'agit souvent d'un choix booléen (vrai ou faux) ou tout du moins ayant un nombre fixe (et peu élevé) de réponses possibles. C'est à mon avis le plus adapté pour répondre à cette question très fréquente : « Acceptez-vous les conditions d'utilisation de nos services (EULA) ? Oui/Non ».
Pour l'utiliser, il faut répéter la propriété name=« « en modifiant value= » ». Les boutons radio fonctionnent par groupe. Le navigateur Web sélectionne l'un ou l'autre des éléments du groupe, jamais plusieurs à la fois. L'emplacement dans le formulaire n'a aucune importance, mais il est préférable de regrouper les boutons radio pour des questions d'ergonomie.

Code 2.4 : Boutons radio avec deux questions

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre>';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
Civilité :<br />
<label>
<input type="radio" name="civilite" value="Mr" checked="checked" />
Mr &nbsp;&nbsp;&nbsp;
</label>
<label>
<input type="radio" name="civilite" value="Mme" />
Mme &nbsp;&nbsp;&nbsp;
</label>
<label>
<input type="radio" name="civilite" value="Mlle" />
Mlle
</label><br />
<br />

Sexe :<br />
<label>
<input type="radio" name="sexe" value="masculin" checked="checked" />
masculin &nbsp;&nbsp;&nbsp;
</label>
<label>
<input type="radio" name="sexe" value="féminin" />
féminin<br />
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-4.php

Le contrôle de choix multiples : <input type=« checkbox » />

Ce contrôle permet de donner plusieurs réponses à une question donnée. Typiquement : « Quels sujets vous intéressent ? ».
J'ai expliqué plus haut qu'il est possible d'utiliser les crochets [ ] dans le nom du contrôle pour que PHP le traite comme un tableau de valeurs. Nous allons nous en servir ici pour regrouper les checkboxes afin de les traiter plus facilement à l'aide de la structure foreach.

Code 2.5 : Cases de sélection pour deux questions

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre>';

//
// Récupération normale des informations
//
echo '<b>Sujets :</b><ul>';
foreach($_POST['subjects'] as $subject){
echo '<li>'.$subject.'</li>';
}
echo '</ul>';

echo '<b>Utilisations :</b><ul>';
foreach($_POST['uses'] as $use){
echo '<li>'.$use.'</li>';
}
echo '</ul><br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
Quels sujets vous intéressent ?<br />
<label>
<input type="checkbox" name="subjects[]" value="Technologie" checked="checked" />
Technologie &nbsp;&nbsp;&nbsp;
</label>
<label>
<input type="checkbox" name="subjects[]" value="Développement" />
Développement &nbsp;&nbsp;&nbsp;
</label>
<label>
<input type="checkbox" name="subjects[]" value="Recherche" />
Recherche
</label><br />
<br />

Comment utilisez-vous votre ordinateur ?<br />
<label>
<input type="checkbox" name="uses[]" value="Internet" checked="checked" />
Internet &nbsp;&nbsp;&nbsp;
</label>
<label>
<input type="checkbox" name="uses[]" value="Développement" />
Développement &nbsp;&nbsp;&nbsp;
</label>
<label>
<input type="checkbox" name="uses[]" value="Jeu" />
Jeu
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-5.php

Le contrôle de saisie de texte long : <textarea></textarea>

La balise textarea permet de saisir une grande quantité de texte, éventuellement sur plusieurs lignes. Des barres de défilement sont ajoutées automatiquement par le navigateur lorsqu'elles sont nécessaires.

Code 2.6 : Exemple prévu pour un forum

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';

//
// Récupération normale des informations
//
echo '<b>Titre</b> : '.$_POST['title'].'<br />';
echo '<b>Message</b> : '.nl2br($_POST['message']).'<br />';
echo '<br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
<label>Titre : <input type="text" name="title" /></label><br />
<label>Message : <br />
<textarea name="message" cols="20" rows="7"></textarea></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-6.php

Ici, print_r() et la balise pre se chargent de toute la mise en page. En situation réelle, les passages à la ligne saisis dans le textarea ne sont pas visibles dans le navigateur. Il faut utiliser la fonction nl2br() pour cela.

Il faut coller la balise ouvrante <textarea> à la balise fermante </textarea> si l'on ne souhaite pas avoir quoi que ce soit dans la case, lors du chargement de la page. À l'inverse, si l'on veut avoir une valeur par défaut, alors c'est à cet endroit qu'il faut la mettre.

Code 2.7 : Textarea non vide

<textarea></textarea>

<textarea> </textarea>

<textarea>

</textarea>

<textarea>Texte par défaut</textarea>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-7.php

Le contrôle de choix alternatifs ou multiples : <select></select>

Ce contrôle est similaire aux boutons radio ou aux checkboxes, selon son utilisation. Il permet de répondre à une question par une ou plusieurs réponses.
Il est préférable de choisir select si le nombre d'options est dynamique, car il regroupe toutes les réponses possibles dans une case de taille fixe. Si le nombre d'options vient à changer, la structure visuelle de la page n'est pas nécessairement modifiée dans le navigateur (contrairement aux boutons radio).

Code 2.8 : Répondre à deux questions avec "select"

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
<label>
Quel est votre sport favori ?<br />
<select name="sport">
<option value="badminton">badminton</option>
<option value="basketball">basketball</option>
<option value="équitation">équitation</option>
<option value="football">football</option>
<option value="handball">handball</option>
<option value="natation">natation</option>
<option value="tennis">tennis</option>
<option value="tir à l arc">tir à l arc</option>
<option value="voile">voile</option>
</select>
</label><br /><br />

<label>
Quelle est votre couleur préférée ?<br />
<select name="color">
<option value="bleu">bleu</option>
<option value="jaune">jaune</option>
<option value="marron">marron</option>
<option value="orange">orange</option>
<option value="rouge">rouge</option>
<option value="vert">vert</option>
<option value="violet">violet</option>
</select>
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-8.php

Si ce formulaire utilisait des boutons radio plutôt qu'un select, la mise en page deviendrait rapidement difficile à organiser.

Il est possible de spécifier l'attribut size pour donner un nombre de lignes fixe au contrôle select, afin de ne pas obtenir une boîte déroulante.

Code 2.9 : Répondre à deux questions avec un "select" de taille fixe

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
<label>
Quel est votre sport favori ?<br />
<select name="sport" size="5">
<option value="badminton">badminton</option>
<option value="basketball">basketball</option>
<option value="équitation">équitation</option>
<option value="football">football</option>
<option value="handball">handball</option>
<option value="natation">natation</option>
<option value="tennis">tennis</option>
<option value="tir à l arc">tir à l arc</option>
<option value="voile">voile</option>
</select>
</label><br /><br />

<label>
Quelle est votre couleur préférée ?<br />
<select name="color" size="5">
<option value="bleu">bleu</option>
<option value="jaune">jaune</option>
<option value="marron">marron</option>
<option value="orange">orange</option>
<option value="rouge">rouge</option>
<option value="vert">vert</option>
<option value="violet">violet</option>
</select>
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-9.php

Il est également possible de spécifier l'attribut multiple afin de permettre à l'internaute de sélectionner plusieurs choix. Si l'attribut size est absent, le contrôle prend toute la place (verticale) dont il a besoin. Il faut utiliser les crochets [ ] pour permettre à PHP de récupérer toutes les réponses.

Code 2.10 : L'attribut "multiple" rend très utile le contrôle "reset"

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
<label>
Quels sont vos sports favoris ?<br />
<select name="sports[]" multiple="multiple">
<option value="badminton" selected="selected">badminton</option>
<option value="basketball">basketball</option>
<option value="équitation">équitation</option>
<option value="football">football</option>
<option value="handball">handball</option>
<option value="natation">natation</option>
<option value="tennis">tennis</option>
<option value="tir à l arc">tir à l arc</option>
<option value="voile" selected="selected">voile</option>
</select>
</label><br /><br />

<label>
Quelles sont vos couleurs préférées ?<br />
<select name="colors[]" size="5" multiple="multiple">
<option value="bleu">bleu</option>
<option value="jaune">jaune</option>
<option value="marron">marron</option>
<option value="orange">orange</option>
<option value="rouge" selected="selected">rouge</option>
<option value="vert">vert</option>
<option value="violet" selected="selected">violet</option>
</select>
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-10.php

Un contrôle select ne peut avoir plusieurs options pré sélectionnées que s'il dispose de l'attribut multiple.

Les menus déroulants peuvent devenir très longs, ce qui a tendance à noyer l'internaute dans trop d'informations. Un moyen de pallier ce problème est d'utiliser l'élément optgroup pour grouper les options.

Code 2.11 : Groupement des réponses par thème

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
<label>
Quel est votre sport favori ?
<select name="sport">
<optgroup label="Sports aquatiques">
<option value="natation">natation</option>
<option value="voile">voile</option>
</optgroup>
<optgroup label="Sports d'équipe">
<option value="badminton">badminton</option>
<option value="basketball">basketball</option>
<option value="football">football</option>
<option value="handball">handball</option>
<option value="tennis">tennis</option>
</optgroup>
<optgroup label="Sports de plein air">
<option value="équitation">équitation</option>
<option value="tir à l arc">tir à l arc</option>
</optgroup>
</select>
</label><br />

<label>
Quelle est votre couleur préférée ?
<select name="color">
<optgroup label="Primaires">
<option value="bleu">bleu</option>
<option value="jaune">jaune</option>
<option value="rouge">rouge</option>
</optgroup>
<optgroup label="Mélanges">
<option value="vert">vert</option>
<option value="violet">violet</option>
<option value="orange">orange</option>
<option value="marron">marron</option>
</optgroup>
</select>
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-11.php

Code 2.12 : Groupement par thème, réponses multiples et présélection

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre><br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>">
<label>
Quels sont vos sports favoris ?<br />
<select name="sports[]" size="10" multiple="multiple">
<optgroup label="Sports aquatiques">
<option value="natation">natation</option>
<option value="voile" selected="selected">voile</option>
</optgroup>
<optgroup label="Sports d'équipe">
<option value="badminton" selected="selected">badminton</option>
<option value="basketball">basketball</option>
<option value="football">football</option>
<option value="handball">handball</option>
<option value="tennis">tennis</option>
</optgroup>
<optgroup label="Sports de plein air">
<option value="équitation">équitation</option>
<option value="tir à l arc">tir à l arc</option>
</optgroup>
</select>
</label><br /><br />

<label>
Quelles sont vos couleurs préférées ?<br />
<select name="colors[]" size="7" multiple="multiple">
<optgroup label="Primaires">
<option value="bleu">bleu</option>
<option value="jaune">jaune</option>
<option value="rouge" selected="selected">rouge</option>
</optgroup>
<optgroup label="Mélanges">
<option value="vert">vert</option>
<option value="violet" selected="selected">violet</option>
<option value="orange">orange</option>
<option value="marron">marron</option>
</optgroup>
</select>
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

https://g-rossolini.developpez.com/tutoriels/php/formulaires/demo/2-12.php

III-B - Du texte + des fichiers : multipart/form-data

Cet enctype doit être utilisé pour tous les formulaires ne contenant pas uniquement du texte au format ASCII, ce qui le rend de plus en plus populaire en cette période de développement de l'UTF-8.

Les contrôles classiques

La gestion des contrôles déjà vus ci-dessus (input, textarea et select) ne change pas, je ne vais donc rien répéter. La seule différence est dans la gestion du contrôle file (qui ne devrait pas être utilisé avec un enctype différent de « multipart/form-data ».

Le contrôle de choix de fichier : <input type=« file » />

Ce contrôle permet à l'internaute de choisir un fichier de son ordinateur afin de l'envoyer au serveur Web grâce au formulaire. Le transfert est effectué au moyen du protocole HTTP (pas de FTP ici), ce qui signifie que rien n'est réellement prévu pour envoyer des fichiers (contrairement au FTP). Ne vous étonnez pas si vous avez du mal à transférer des fichiers de plusieurs Mio…

Le script PHP peut faire ce qu'il souhaite avec ce fichier : il est enregistré dans un dossier temporaire du serveur Web, ce qui signifie que sa durée de vie est limitée. Le serveur Web le supprimera certainement au bout d'un moment.
Il est ainsi possible d'utiliser le fichier comme pièce jointe d'un e-mail, de l'envoyer dans la base de données, de l'enregistrer sur le disque du serveur, etc.

Code 2.13 : Envoi de fichier par formulaire

<?php

//
// Provient de la documentation PHP de la fonction ini_get()
//
function return_bytes($val) {
$val = trim($val);
$last = strtolower($val{strlen($val)-1});
switch($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}

return $val;
}

define('MAX_FILE_SIZE', return_bytes(ini_get('post_max_size')));

if(!empty($_FILES)){
//
// Debug
//
echo '<pre>';
print_r($_FILES);
echo '</pre>';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_FILE_SIZE; ?>" />
<label>Fichier joint : <input type="file" name="file" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

L'exécution de ce code nous montre que PHP récupère des informations sur chaque fichier envoyé de cette manière.

Le champ MAX_FILE_SIZE sert uniquement d'indication au navigateur. Il n'est pas possible de faire confiance à $_POST['MAX_FILE_SIZE'] puisque, ainsi que je l'ai évoqué plus haut, l'internaute peut modifier cette information à sa convenance.

Son utilité est uniquement d'ordre pratique pour l'internaute. Ce champ indique au navigateur la taille maximale (en octets) que le serveur peut supporter avec un tel transfert. Cela lui permet d'éviter d'initier le transfert si la taille est dépassée, ainsi l'internaute ne perd pas son temps.

Utiliser ini_get('post_max_size') permet de toujours inclure dans le formulaire la valeur réelle de MAX_FILE_SIZE en l'extrayant directement du fichier php.ini, plutôt que de mettre une valeur arbitraire. Il nous faut appeler return_bytes() pour avoir l'équivalent numérique en octets. Attention, cette fonction n'est pas native de PHP : il faut la définir dans votre script d'une manière ou d'une autre.

Voici le contenu du tableau $_FILES :

- name : C'est le nom du fichier d'origine ; ce nom peut ne pas convenir au système de fichiers de notre serveur Web

- type : Le type MIME du fichier tel que le navigateur le précise (attention, ce n'est ni le serveur Web ni PHP qui s'en occupent) ; cette information peut être falsifiée

- tmp_name : Le nom attribué par le serveur Web lors de la réception du fichier dans le dossier temporaire

- error : S'il n'y a pas eu d'erreur, ce champ vaut « 0 » (zéro) ; c'est le serveur Web qui détermine cette valeur

- size : La taille du fichier, en octets ; cette information est fournie par le navigateur et peut être fausse

Un développeur soucieux de la sécurité de son application ne devrait se fier qu'à $_FILES['tmp_name'], $_FILES['error'] et dans une moindre mesure $_FILES['name']. Il est préférable de déterminer le reste par le script PHP, puisqu'un utilisateur averti est en mesure de falsifier les informations $_FILES['name'], $_FILES['type'] et $_FILES['size'].

Code 2.14 : Gestion des erreurs

<?php

//
// Fonction trouvée dans la documentation de la fonction ini_get()
//
function return_bytes($val) {
$val = trim($val);
$last = strtolower($val{strlen($val)-1});
switch($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}

return $val;
}

define('MAX_FILE_SIZE', return_bytes(ini_get('post_max_size')));

if(!empty($_FILES)){
//
// Debug
//
echo '<pre>';
print_r($_FILES);
echo '</pre>';

//
// Récupération normale des informations
//
if(is_uploaded_file($_FILES['attached_file']['tmp_name'])){
$name     = $_FILES['attached_file']['name'];
$tmp_name = $_FILES['attached_file']['tmp_name'];
$error    = $_FILES['attached_file']['error'];

//
// Type mime du fichier
//
if(function_exists('mime_content_type')){
// Notre système nous permet de déterminer le type réel
$type = mime_content_type($tmp_name);
}
else{
// Nous sommes contraints à faire confiance à l'internaute
$type = $_FILES['attached_file']['type'];
}

//
// Gestion des erreurs éventuelles
//
switch($error){
case UPLOAD_ERR_OK:
$error_string = 'Fichier correctement reçu';
break;

case UPLOAD_ERR_INI_SIZE:
$error_string = 'Fichier trop volumineux (php.ini)';
break;

case UPLOAD_ERR_FORM_SIZE:
$error_string = 'Fichier trop volumineux (MAX_FILE_SIZE)';
break;

case UPLOAD_ERR_PARTIAL:
$error_string = 'Fichier partiellement envoyé';
break;

case UPLOAD_ERR_NO_FILE:
$error_string = 'Fichier non envoyé';
break;

case UPLOAD_ERR_NO_TMP_DIR:
$error_string = 'Pas de répertoire temporaire';
break;

case UPLOAD_ERR_CANT_WRITE:
$error_string = "Impossible d'écrire sur le disque";
break;
}

//
// Debug
//
echo '<pre>';
print_r(array(
'name'  => $name,
'type'  => $type,
'error' => $error,
'size'  => filesize($tmp_name),
'tmp_name'     => $tmp_name,
'error_string' => $error_string
));
echo '</pre>';
}
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_FILE_SIZE; ?>" />
<label>Fichier joint : <input type="file" name="attached_file" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

Si nous souhaitons envoyer plusieurs fichiers à la fois, il faut à nouveau utiliser les crochets [ ] dans l'attribut name, mais, cette fois, le comportement est légèrement différent. En effet, ce n'est pas $_FILES['variable'] qui est transformé en tableau, mais chacun de ses indexes.

Code 2.15 : Envoyer plusieurs fichiers à la fois

<?php

//
// Fonction trouvée dans la documentation de la fonction ini_get()
//
function return_bytes($val) {
$val = trim($val);
$last = strtolower($val{strlen($val)-1});
switch($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}

return $val;
}

define('MAX_FILE_SIZE', return_bytes(ini_get('post_max_size')));

if(!empty($_FILES)){
//
// Debug
//
echo '<pre>';
print_r($_FILES);
echo '</pre>';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_FILE_SIZE; ?>" />
<label>Fichier joint : <input type="file" name="files[]" /></label><br />
<label>Fichier joint : <input type="file" name="files[]" /></label><br />
<label>Fichier joint : <input type="file" name="files[]" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

Nous pouvons remarquer que le tableau $_FILES['files'] se décompose ainsi (pour trois fichiers) :

- ['name'][0]

- ['name'][1]

- ['name'][2]

- ['type'][0]

- ['type'][1]

- ['type'][2]

- ['tmp_name'][0]

- ['tmp_name'][1]

- ['tmp_name'][2]

- ['error'][0]

- ['error'][1]

- ['error'][2]

- ['size'][0]

- ['size'][1]

- ['size'][2]

Il n'est pas pratique d'utiliser foreach avec une telle structure. Voyons comment gérer les erreurs de plusieurs fichiers avec une structure for.

Code 2.16 : Gestion des erreurs

<?php

//
// Fonction trouvée dans la documentation de la fonction ini_get()
//
function return_bytes($val) {
$val = trim($val);
$last = strtolower($val{strlen($val)-1});
switch($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}

return $val;
}

define('MAX_FILE_SIZE', return_bytes(ini_get('post_max_size')));

if(!empty($_FILES)){
//
// Debug
//
echo '<pre>';
print_r($_FILES);
echo '</pre>';

//
// Récupération normale des informations
//
$nb_of_files = count($_FILES['attached_files']['tmp_name']);
echo '<ul>';
for($i = 0; $i< $nb_of_files; ++$i){
if(is_uploaded_file($_FILES['attached_files']['tmp_name'][$i])){
$name     = $_FILES['attached_files']['name'][$i];
$tmp_name = $_FILES['attached_files']['tmp_name'][$i];
$error    = $_FILES['attached_files']['error'][$i];

//
// Type mime du fichier
//
if(function_exists('mime_content_type')){
// Notre système nous permet de déterminer le type réel
$type = mime_content_type($tmp_name);
}
else{
// Nous sommes contraints à faire confiance à l'internaute
$type = $_FILES['attached_files']['type'][$i];
}

//
// Gestion des erreurs éventuelles
//
switch($error){
case UPLOAD_ERR_OK:
echo 'Fichier correctement reçu';
break;

case UPLOAD_ERR_INI_SIZE:
echo 'Fichier trop volumineux (php.ini)';
break;

case UPLOAD_ERR_FORM_SIZE:
echo 'Fichier trop volumineux (MAX_FILE_SIZE)';
break;

case UPLOAD_ERR_PARTIAL:
echo 'Fichier partiellement envoyé';
break;

case UPLOAD_ERR_NO_FILE:
echo 'Fichier non envoyé';
break;

case UPLOAD_ERR_NO_TMP_DIR:
echo 'Pas de répertoire temporaire';
break;

case UPLOAD_ERR_CANT_WRITE:
echo "Impossible d'écrire sur le disque";
break;
}

//
// Debug
//
echo '<li>';
echo '<b>Fichier '.$i.'</b> :';
echo '<pre>';
print_r(array(
'name'  => $name,
'type'  => $type,
'error' => $error,
'size'  => filesize($tmp_name),
'tmp_name'     => $tmp_name,
'error_string' => $error_string
));
echo '</pre>';
echo '</li>';
}
}
echo '</ul>';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_FILE_SIZE; ?>" />
<label>Fichier joint : <input type="file" name="attached_files[]" /></label><br />
<label>Fichier joint : <input type="file" name="attached_files[]" /></label><br />
<label>Fichier joint : <input type="file" name="attached_files[]" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

Les fonctions PHP

PHP met à disposition un certain nombre de fonctions

- is_uploaded_file() : permet de savoir si le fichier est bien un fichier envoyé par HTTP ; il est possible que le dossier temporaire utilisé par le serveur Web soit utilisé par d'autres applications, d'où l'utilité de cette fonction

- move_uploaded_file() : déplace un fichier du répertoire temporaire vers un répertoire à préciser (attention aux doits en écriture - chmod 777) ; si cette fonction n'est pas utilisée à la suite d'un transfert, le serveur Web détruit le fichier temporaire

Code 2.17 : Enregistrer sur le disque du serveur

<?php

//
// Fonction trouvée dans la documentation de la fonction ini_get()
//
function return_bytes($val) {
$val = trim($val);
$last = strtolower($val{strlen($val)-1});
switch($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}

return $val;
}

define('MAX_FILE_SIZE', return_bytes(ini_get('post_max_size')));
define('UPLOAD_DIRECTORY', './uploads/');

if(!empty($_FILES)){
//
// Debug
//
echo '<b>$_FILES</b> :<pre>';
print_r($_FILES);
echo '</pre>';

//
// Récupération normale des informations
//
$nb_of_files = count($_FILES['attached_files']['tmp_name']);
echo '<ul>';
for($i = 0; $i< $nb_of_files; ++$i){
if(is_uploaded_file($_FILES['attached_files']['tmp_name'][$i])){
$name     = $_FILES['attached_files']['name'][$i];
$tmp_name = $_FILES['attached_files']['tmp_name'][$i];
$error    = $_FILES['attached_files']['error'][$i];
$clean_name = strtolower(basename($name));
$clean_name = preg_replace('/[^a-z0-9.-]/', '-', $clean_name);

//
// Type mime du fichier
//
if(function_exists('mime_content_type')){
// Notre système nous permet de déterminer le type réel
$type = mime_content_type($tmp_name);
}
else{
// Nous sommes contraints à faire confiance à l'internaute
$type = $_FILES['attached_files']['type'][$i];
}

//
// Déplacement hors du répertoire temporaire
//
if(!move_uploaded_file($tmp_name, UPLOAD_DIRECTORY.$clean_name)){
$error_string = "Le fichier <b>n'a pas</b> été déplacé correctement";
}
else{
$error_string = 'Le fichier a été déplacé correctement';
}

//
// Debug
//
echo '<li>';
echo '<b>Fichier '.$i.'</b> :';
echo '<pre>';
print_r(array(
'name'  => $name,
'type'  => $type,
'error' => $error,
'size'  => filesize($tmp_name),
'tmp_name'     => $tmp_name,
'clean_name'   => $clean_name,
'error_string' => $error_string
));
echo '</pre>';
echo '</li>';
}
}
echo '</ul>';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_FILE_SIZE; ?>" />
<label>Fichier joint : <input type="file" name="attached_files[]" /></label><br />
<label>Fichier joint : <input type="file" name="attached_files[]" /></label><br />
<label>Fichier joint : <input type="file" name="attached_files[]" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

IV - Formulaire réparti sur plusieurs pages

L'un des thèmes récurrents de nos forums est « j'ai un formulaire sur plusieurs pages, comment faire pour… » : après avoir lu ce tutoriel jusqu'ici, vous devriez savoir que la majorité des formulaires peuvent être présentés en une seule page (notamment à l'aide de la balise >fieldset<)..
Cependant, certaines situations nécessitent un formulaire tellement long qu'il est préférable de le découper en plusieurs étapes, chacune étant située sur sa propre page. Nous allons voir ici plusieurs méthodes pour y parvenir.

Prenons un exemple concret : un site de recherche d'emploi. Chaque postulant doit renseigner plusieurs dizaines d'informations, ce qui rendrait le tout impossible à gérer si nous ne proposions qu'un unique formulaire. Il est préférable d'y aller par étapes.

Voici ce sur quoi nous allons nous fonder :

1. Informations de connexion

2. Informations personnelles

3. Études

4. Expérience

5. Connaissances

6. Alertes

Je vous propose d'utiliser un fichier d'index et plusieurs fichiers à inclure. Cela permet d'avoir nos formulaires HTML dans des fichiers séparés du code PHP. Pour les connaisseurs, cette méthode est très similaire à l'utilisation d'un moteur de gabarits et vous n'aurez aucun mal à l'adapter.

Arborescence du site :

- /index.php

- /forms/1-connection-data.php

- /forms/2-personal-data.php

- /forms/3-studies.php

- /forms/4-summary.php

IV-A - Les formulaires

Mon objectif ici n'est pas de construire un site de recherche d'emploi, je ne vais donc pas étudier toutes les étapes en détail. Les trois premières suffiront et je ne vais proposer ici que des formulaires simples.

Informations de connexion

Ce premier formulaire concerne l'arrivée de l'internaute sur notre page : ses informations de connexion (nom d'utilisateur et mot de passe) et comment il nous a connus.

Code 3.1 : forms/1-connection-data.php

<form method="post" action="<?php echo CFG_FORM_ACTION; ?>?stage=<?php echo CFG_STAGE_ID+1; ?>">
<fieldset>
<legend>Informations de connexion</legend>
<label>
Adresse e-mail :
<input type="text" name="e-mail" value="<?php echo $_SESSION['forms'][CFG_STAGE_ID]['e-mail']; ?>" />
</label><br />
<label>
Mot de passe :
<input type="password" name="password-1" />
</label><br />
<label>
Mot de passe (répéter) :
<input type="password" name="password-2" />
</label>
</fieldset><br />

<fieldset>
<legend>Autres informations</legend>
<label>
Comment nous avez-vous connus ?
<select name="referer">
<option value="friend"<?php
if($_SESSION['forms'][CFG_STAGE_ID]['referer'] == 'friend'){echo ' selected="selected"';}
?>>Par un ami</option>
<option value="search-engine"<?php
if($_SESSION['forms'][CFG_STAGE_ID]['referer'] == 'search-engine'){echo ' selected="selected"';}
?>>Par un moteur de recherche</option>
<option value="press"<?php
if($_SESSION['forms'][CFG_STAGE_ID]['referer'] == 'press'){echo ' selected="selected"';}
?>>Par la presse</option>
<option value="other"<?php
if($_SESSION['forms'][CFG_STAGE_ID]['referer'] == 'other'){echo ' selected="selected"';}
?>>Autre</option>
</select>
</label><br />
<label>
Pouvez-vous préciser ?
<input type="text" name="details" value="<?php echo $_SESSION['forms'][CFG_STAGE_ID]['details']; ?>" />
</label>
</fieldset>

<input type="submit" value="Envoyer et continuer" />
</form>

Informations personnelles

C'est ici qu'il faut demander toutes les informations civiles à notre internaute. En situation réelle, il faudrait étoffer cette liste.

Code 3.2 : forms/2-personal-data.php

<form method="post" action="<?php echo CFG_FORM_ACTION; ?>?stage=<?php echo CFG_STAGE_ID+1; ?>">
<fieldset>
<legend>Informations personnelles</legend>
<label>
Prénom :
<input type="text" name="first-name" value="<?php
echo $_SESSION['forms'][CFG_STAGE_ID]['first-name'];
?>" />
</label><br />
<label>
Nom :
<input type="text" name="last-name" value="<?php
echo $_SESSION['forms'][CFG_STAGE_ID]['last-name'];
?>" />
</label><br />
<label>
Ville :
<input type="text" name="city" value="<?php
echo $_SESSION['forms'][CFG_STAGE_ID]['city'];
?>" />
</label><br />
<label>
Code postal :
<input type="text" name="postal-code" value="<?php
echo $_SESSION['forms'][CFG_STAGE_ID]['postal-code'];
?>" />
</label><br />
</fieldset>

<input type="submit" value="Envoyer et continuer" />
</form>

Études

Comme ce tutoriel n'est qu'un exemple d'un formulaire réparti sur plusieurs pages, j'ai simplifié ce formulaire à un simple contrôle <textarea>. Dans une situation réelle, il faudrait un formulaire bien plus flexible que cela.

Code 3.3 : forms/3-studies.php

<form method="post" action="<?php echo CFG_FORM_ACTION; ?>?stage=<?php echo CFG_STAGE_ID+1; ?>">
<fieldset>
<legend>Informations personnelles</legend>
<label>
Études :<br />
<textarea name="studies" cols="15" rows="10"><?php
echo $_SESSION['forms'][CFG_STAGE_ID]['studies'];
?></textarea>
</label>
</fieldset>

<input type="submit" value="Envoyer et continuer" />
</form>

Résumé

Je me contente ici d'afficher des informations de débogage, mais il est évident qu'en situation réelle il serait préférable de soigner la présentation.

Code 3.4 : forms/4-summary.php

<?php

echo '<pre>';
print_r($_SESSION);
echo '</pre>';

?>

IV-B - La page d'index

Les informations de chaque formulaire soumis sont intégralement conservées sur le serveur et nous les réutilisons chaque fois que l'internaute en a besoin. Le plus simple est d'utiliser des sessions, mais le plus flexible est bien sûr d'utiliser une base de données. Quoi qu'il en soit, la session est nécessaire pour passer les informations d'un formulaire à l'autre.

Pour simplifier les exemples, je vais me contenter d'une session PHP sans base de données.

Mise en place des variables

Le début de notre script d'index doit mettre en place l'environnement : la session et les variables de configuration.

Code 3.5

session_start();

// Configuration du script
define('CFG_FORM_ACTION', basename(__FILE__)); // Cela permet de changer le nom du script d'index
$forms = array( // Voici la liste des formulaires, pratique pour mettre en place le menu de navigation
1 => 'Informations de connexion',
2 => 'Informations personnelles',
3 => 'Études'
);

// Récupération du numéro de l'étape en cours
if(empty($_GET['stage']) or !is_numeric($_GET['stage']))
{
define('CFG_STAGE_ID', 1);
}
else
{
// En situation réelle, il faudrait vérifier l'existence de cette page
define('CFG_STAGE_ID', intval($_GET['stage']));
}

// Déclaration de la variable de session
if(empty($_SESSION['forms']))
{
$_SESSION['forms'] = array();
}

Les liens en haut de la page

Il est toujours intéressant de proposer un menu de navigation à notre utilisateur. Ici, cela lui évitera probablement d'utiliser les boutons de navigation de son navigateur, tellement agaçants par moments.

Les pages qui n'ont pas encore été visitées ne comportent pas de lien, afin d'éviter à l'utilisateur de sauter des étapes. Dans une situation réelle, il faudrait vérifier que l'utilisateur n'a pas modifié le paramètre de l'URI à la main.

Code 3.6

// Affichage du menu en haut de la page
$items = array();
foreach($forms as $form_id => $form_name)
{
if(empty($_SESSION['forms'][$form_id]))
{
$items[] = $form_name;
}
else
{
$items[] = '<a href="'.basename(__FILE__).'?stage='.$form_id.'">'.$form_name.'</a>';
}
}
$items[] = '<a href="'.basename(__FILE__).'?stage=4">Résumé</a>';
echo implode(' - ', $items).'<br /><br />';

Traitement des données utilisateur

Enfin, il nous faut traiter les données envoyées par notre internaute, c'est-à-dire qu'il faut les envoyer dans notre variable de session (ou dans la base de données, le cas échéant).

Puisque ce script est un peu long, je vous propose d'abord son squelette pour vous permettre de vous familiariser avec son fonctionnement.

Code 3.7 : Le squelette

// Récupération des informations, affichage du formulaire
switch(CFG_STAGE_ID)
{
case 4:
// Récupération des informations du formulaire précédent
if(!empty($_POST))
{
// Vérification des champs puis affectation à la variable de session
}

// Inclusion de la page de résumé
break;

case 3:
// Valeurs par défaut
if(empty($_SESSION['forms'][CFG_STAGE_ID]))
{
$_SESSION['forms'][CFG_STAGE_ID] = ...
}

// Récupération des informations du formulaire précédent
if(!empty($_POST))
{
// Vérification des champs puis affectation à la variable de session
}

// Inclusion du formulaire adéquat
break;

case 2:
// Valeurs par défaut
if(empty($_SESSION['forms'][CFG_STAGE_ID]))
{
$_SESSION['forms'][CFG_STAGE_ID] = ...
}

// Récupération des informations du formulaire précédent
if(!empty($_POST))
{
// Vérification des champs puis affectation à la variable de session
}

// Inclusion du formulaire adéquat
break;

case 1:
default:
// Valeurs par défaut
if(empty($_SESSION['forms'][CFG_STAGE_ID]))
{
$_SESSION['forms'][CFG_STAGE_ID] = ...
}

// Inclusion du formulaire adéquat
break;
}

La structure de ce script me semble suffisamment claire pour parler d'elle-même : selon l'étape en cours, nous effectuons un traitement ou un autre. Voyons maintenant le script réel.

Code 3.8 : Le script

// Récupération des informations, affichage du formulaire
switch(CFG_STAGE_ID)
{
case 4:
// Récupération des informations du formulaire précédent
if(!empty($_POST))
{
if(!empty($_POST['studies']))
{
$_SESSION['forms'][CFG_STAGE_ID-1] = array(
'studies'   => $_POST['studies']
);
}
}

// Affichage du formulaire
require('./forms/4-summary.php');
break;

case 3:
// Valeurs par défaut
if(empty($_SESSION['forms'][CFG_STAGE_ID]))
{
$_SESSION['forms'][CFG_STAGE_ID] = array(
'studies'   => ''
);
}

// Récupération des informations du formulaire précédent
if(!empty($_POST))
{
if(!empty($_POST['last-name'])
and !empty($_POST['first-name'])
and !empty($_POST['city'])
and !empty($_POST['postal-code'])
and is_numeric($_POST['postal-code']))
{
$_SESSION['forms'][CFG_STAGE_ID-1] = array(
'first-name'  => $_POST['first-name'],
'last-name'   => $_POST['last-name'],
'city'  => $_POST['city'],
'postal-code' => $_POST['postal-code']
);
}
}

// Affichage du formulaire
require('./forms/3-studies.php');
break;

case 2:
// Valeurs par défaut
if(empty($_SESSION['forms'][CFG_STAGE_ID]))
{
$_SESSION['forms'][CFG_STAGE_ID] = array(
'first-name'  => '',
'last-name'   => '',
'city'  => '',
'postal-code' => NULL
);
}

// Récupération des informations du formulaire précédent
if(!empty($_POST))
{
if(!empty($_POST['e-mail'])
and !empty($_POST['password-1'])
and !empty($_POST['password-2'])
and preg_match('/^([^@\s<&>]+)@(?:([-a-z0-9]+)\.)+([a-z]{2,})$/i', $_POST['e-mail'])
and $_POST['password-1'] == $_POST['password-2'])
{
$_SESSION['forms'][CFG_STAGE_ID-1] = array(
'e-mail'   => $_POST['e-mail'],
'password' => md5($_POST['password-1']),
'referer'  => $_POST['referer'],
'details'  => $_POST['details']
);
}
}

require('./forms/2-personal-data.php');
break;

case 1:
default:
// Valeurs par défaut
if(empty($_SESSION['forms'][CFG_STAGE_ID]))
{
$_SESSION['forms'][CFG_STAGE_ID] = array(
'e-mail'   => '',
'password' => '',
'referer'  => 'friend',
'details'  => ''
);
}

require('./forms/1-connection-data.php');
break;
}

IV-C - Fonctionnement

IV-C-1 - Mise en place

La mise en place de ces formulaires est simple : il suffit de créer l'arborescence évoquée plus haut.

Télécharger ces scripts par [ FTP ] ou bien par le miroir [ HTTP ].

IV-C-2 - Navigation

Ma foi, la navigation est intuitive… Il faut remplir les formulaires à partir du premier jusqu'au dernier et il est possible de revenir en arrière.

Testez ces scripts.

IV-C-3 - Sécurité

Comme toujours dans ce tutoriel, je n'ai effectué que des contrôles minimalistes. Ce n'est pas le sujet ici, mais, en situation réelle, il convient de s'y attarder davantage.

V - Améliorations de l'expérience utilisateur

Ce tutoriel ne traite pas des formulaires sous toutes leurs coutures. Je n'ai la prétention d'être un spécialiste ni de JavaScript (et ses déclinaisons) ni des feuilles de style. Cependant, il est toujours utile de savoir faire le minimum…
Notez que JavaScript, CSS et PHP sont des éléments totalement indépendants. Vous avez seulement besoin du code HTML pour le reste. La partie PHP est souvent indispensable, mais certaines situations permettent de s'en passer.

Vous pouvez l'habiller avec ce que vous voulez :

- HTML seul (nu)

- HTML + PHP

- HTML + CSS

- HTML + JS

- HTML + JS + CSS

- HTML + PHP + JS + CSS

- etc.

V-A - Les boutons « actualiser » et « précédent » du navigateur

Il faut prendre garde aux boutons « actualiser » et « précédent » du navigateur, car ils peuvent nous poser des problèmes. La solution la plus simple consiste à utiliser la méthode proposée par Ramazan Korkmaz (maximenet) dans son tutoriel : Commentez .
Il s'agit simplement de réafficher la page avec la méthode GET, ce qui supprime les données POST et remplace la page actuelle de l'historique du navigateur.

La fonction header()

La fonction header() nous permet d'ordonner au navigateur de demander une autre page Web. Il convient de lui expliquer pourquoi nous lui donnons cet ordre, ce qui peut être fait à l'aide des en-têtes HTTP. Nous allons utiliser le code HTTP 303 : « voir autre [ressource] ».

Code 4.1

header('HTTP/1.1 303 See Other');
header('Location: script.php');
exit;

Le mot clef exit nous assure que le navigateur ne chargera pas d'informations inutiles.

Dans les exemples de formulaire réparti sur plusieurs pages (présents dans de ce tutoriel), il faut empêcher l'exécution systématique de la commande echo implode() en la remplaçant par ceci :

Code 4.2

if(empty($_POST))
{
echo implode(' - ', $items).'<br /><br />';
}

Bien organiser son code

Afin d'obtenir un formulaire optimal, il est préférable d'effectuer dès que possible tous les traitements sur les données soumises par l'utilisateur. Cela permet d'enregistrer le contenu d'un formulaire dans une variable de session ou dans une base de données avant d'avoir envoyé quoi que ce soit au navigateur, nous permettant ainsi d'utiliser la fonction header().

V-B - Un peu de JS (JavaScript)

Dans certains cas, JavaScript peut être très utile dans les formulaires. D'après le W3C, un formulaire ne peut être envoyé que si tous les contrôles (ayant un nom et une valeur), ainsi que quelques exceptions, sont valides. Le bouton submit qui a été cliqué fait partie des contrôles qui doivent être valides pour que le navigateur envoie le formulaire.
Cela nous permet d'effectuer un traitement JavaScript en utilisant l'évènement onsubmit du bouton submit. La fonction JavaScript appelée permettra éventuellement d'invalider le bouton submit et ainsi de ne pas soumettre le formulaire. C'est particulièrement utilisé pour les mots de passe.

L'attribut HTML id est utilisé par la fonction JavaScript document.getElementById(). C'est celui que nous utiliserons, plutôt que l'attribut name qui est destiné à PHP (mais qui peut avoir la même valeur, ce qui est d'ailleurs assez courant).

Il faut toujours se souvenir que JavaScript est une technologie client, elle n'est donc pas exécutée dans l'environnement de confiance qu'est notre serveur Web. L'internaute peut désactiver le JavaScript ou s'arranger pour passer outre nos vérifications en JavaScript, ce qui fait que le script PHP ne devra absolument pas s'y fier. Les seules vérifications valides (point de vue sécurité) sont celles effectuées par le script PHP. Le script JavaScript ne permet que d'améliorer l'expérience utilisateur en évitant de soumettre un formulaire s'il n'est pas correctement rempli. Une vérification en JavaScript ne doit pas se substituer à une vérification en PHP, mais elle peut la compléter ou la renforcer. Il ne faut pas partir du principe qu'un formulaire reçu par PHP est un formulaire valide : l'internaute peut être en train de pirater (s'amuser avec) notre site.

Les évènements disponibles

Pour la balise : <form>

- onsubmit : Le formulaire est envoyé

- onreset : Le formulaire est réinitialisé à son état d'origine

Pour les contrôles : input et textarea

- onfocus : Gain du focus

- onblur : Perte du focus

- onselect : Du texte a été sélectionné

- onchange : La valeur a changé

Pour les contrôles : select, optgroup et option

- onfocus : Gain du focus

- onblur : Perte du focus

- onchange : La valeur a changé

Pour le contrôle : button

- onfocus : Gain du focus

- onblur : Perte du focus

Vérifier que le formulaire est complètement rempli

Code 4.3 : Le formulaire

<form method="post" action="<?php echo basename(__FILE__); ?>" enctype="multipart/form-data">
<label>
Nom d'utilisateur * :
<input type="text" name="login" />
</label><br />
<label>
Adresse e-mail * :
<input type="text" name="e_mail" />
</label><br />
<label>
Numéro de téléphone :
<input type="text" name="phone" />
</label><br />
<label>
Adresse :
<input type="text" name="address" />
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>
Les champs marqués d'une * sont obligatoires.

Code 4.4 : Vérification simple en PHP

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre>';

//
// Récupération normale des informations
//
$missing_fields = array();
if(empty($_POST['login'])){
$missing_fields[] = '"login"';
}

if(empty($_POST['e_mail'])){
$missing_fields[] = '"e_mail"';
}

if(empty($missing_fields)){
echo 'Tous les champs ont été renseignés';
}
else{
echo 'Les champs suivants doivent être remplis :<br />';
echo implode('<br />', $missing_fields);
}

echo '<br /><br />';
}

?>

Code 4.5 : Vérification en JavaScript et en PHP

<script type="text/javascript" language = "Javascript">

function is_filled(){
missing_fields = '';

if(document.getElementById("login").value == ""){
missing_fields += "\n 'login'";
}

if(document.getElementById("e_mail").value == ""){
missing_fields += "\n 'e_mail'";
}

if(missing_fields == ""){
return true;
}
else{
alert("Les champs suivants doivent être remplis :" + missing_fields);
return false;
}
}

</script>

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre>';

//
// Récupération normale des informations
//
$missing_fields = array();
if(empty($_POST['login'])){
$missing_fields[] = '"login"';
}

if(empty($_POST['e_mail'])){
$missing_fields[] = '"e_mail"';
}

if(empty($missing_fields)){
echo 'Tous les champs ont été renseignés';
}
else{
echo 'Les champs suivants doivent être remplis :<br />';
echo implode('<br />', $missing_fields);
}

echo '<br /><br />';
}

?>

<form method="post"
action="<?php echo basename(__FILE__); ?>"
enctype="multipart/form-data"
onsubmit="return is_filled();">
<label>
Nom d'utilisateur * :
<input type="text" name="login" id="login" />
</label><br />
<label>
Adresse e-mail * :
<input type="text" name="e_mail" id="e_mail" />
</label><br />
<label>
Numéro de téléphone :
<input type="text" name="phone" id="phone" />
</label><br />
<label>
Adresse :
<input type="text" name="address" id="address" />
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>
Les champs marqués d'une * sont obligatoires.

Contrôler que les deux mots de passe sont identiques

Code 4.6 : Vérification simple en PHP

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre>';

//
// Récupération normale des informations
//
if($_POST['password_1'] != $_POST['password_2']){
echo 'Erreur de mot de passe';
}
else{
echo 'Le mot de passe a été correctement saisi';
}
echo '<br /><br />';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>" enctype="multipart/form-data">
<label>Nom d utilisateur : <input type="text" name="login" /></label><br />
<label>Mot de passe : <input type="password" name="password_1" /></label><br />
<label>Vérification : <input type="password" name="password_2" /></label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

Code 4.7 : Vérification en JavaScript et en PHP

<script type="text/javascript" language = "Javascript">

function check_password(field_1, field_2){
if(document.getElementById(field_1).value != document.getElementById(field_2).value){
alert('Les mots de passe ne correspondent pas');
return false;
}
else{
return true;
}
}

</script>

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre>';

//
// Récupération normale des informations
//
if($_POST['password_1'] != $_POST['password_2']){
echo 'Erreur de mot de passe';
}
else{
echo 'Le mot de passe a été correctement saisi';
}
echo '<br /><br />';
}

?>

<form method="post"
action="<?php echo basename(__FILE__); ?>"
enctype="multipart/form-data"
onsubmit="javascript: return check_password('password_1', 'password_2');">
<label>
Nom d utilisateur :
<input type="text" name="login" id="login" />
</label><br />
<label>
Mot de passe :
<input type="password" name="password_1" id="password_1" />
</label><br />
<label>
Vérification :
<input type="password" name="password_2" id="password_2" />
</label><br /><br />

<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</form>

V-C - Un peu de CSS (feuilles de style)

Les formulaires HTML ont une interface qui varie selon le navigateur utilisé et selon la configuration de ce navigateur (thèmes-skins, etc.) et cela ne correspond pas toujours à la charte graphique de notre site Web.
Les feuilles de style (CSS) sont là pour nous aider.

Nous utiliserons l'attribut HTML class pour définir l'apparence de nos formulaires.

Code 4.8 : Un formulaire avec du style

<style type="text/css">

/* BODY */
body {
color: Black;
background-color: #DDEEFF;
font-family: Arial, Verdana, sans-serif;
font-size: 13px;
}

/* FORMULAIRE */

/* balises INPUT */
input {
background-color: #FAFAFA;
border-width: 1px;
border-color: #D1D7DC;
color: Black;
font-style: italic;
}

/* balises spéciales dans le DIV des boutons */
#submit input {
font-weight: bold;
font-style: normal;
background-color: #D1D7DC;
color: Black;
border-color: Black;
padding: 0px 10px 0px 10px;
border-width: 2px;
}

/* le reste du DIV des boutons */
#submit {
width: 400px;
text-align: center;
}

/* FIELDSET : bloc du formulaire */
.form fieldset {
border: solid;
border-width: 1px;
border-color: Black;
width: 400px;
padding: 15px 10px 10px 10px;
margin: 0px 0px 15px 0px;
background-color: White;
color: Black;
}

/* LEGEND : titre */
.form legend {
border: solid;
border-width: 1px;
border-color: Black;
padding: 5px;
background-color: #6389D8;
font-weight: bold;
color: White;
font-size: 14px;
}

/* LABEL : élements du formulaire */
.form label {
display: block;
margin: 5px 0px 5px 0px;
text-align: right;
}

</style>

<?php

if(!empty($_POST)){
//
// Debug
//
echo '<pre>';
print_r($_POST);
echo '</pre>';
}

?>

<form method="post" action="<?php echo basename(__FILE__); ?>" class="form">
<fieldset>
<legend>Informations personnelles</legend>
<label>Prénom : <input type="text" name="personal[first_name]" /></label>
<label>Nom : <input type="text" name="personal[last_name]" /></label>
</fieldset>
<fieldset>
<legend>Informations virtuelles</legend>
<label>Pseudonyme : <input type="text" name="virtual[nickname]" /></label>
<label>Site Web : <input type="text" name="virtual[website]" /></label>
<label>Messagerie instantanée : <input type="text" name="virtual[instant_messenger]" /></label>
</fieldset>

<div id="submit">
<input type="submit" value="Envoyer" />
<input type="reset" value="Rétablir" />
</div>
</form>

VI - Conclusion

VI-A - Épilogue

Nous avons vu comment utiliser les formulaires en HTML et comment les agrémenter de quelques fonctionnalités supplémentaires (style et script). Attention, certains navigateurs ne respectent pas la norme : avec certains d'entre eux, le <label> peut ne pas donner le focus à son contrôle associé. Par ailleurs, n'accordez pas toute votre confiance aux vérifications effectuées au moyen d'un script côté client, car il est très facile de déjouer ce genre de méthodes : implémentez les mêmes vérifications côté serveur. Enfin, limitez au maximum les informations à transmettre par formulaire, à cause de la simplicité de falsification : préférez une session PHP ou, mieux, une base de données.

VI-B - Liens

Liens de Developpez

- Vérifier une saisie utilisateur (formulaire)

- La FAQ JavaScript de Developpez.com

- Les cours HTML, XHTML et CSS de Developpez.com

- Tutoriel : Utiliser une base de données pour sécuriser vos sessions, par Adrien Pellegrini

Liens externes

- HTML 4 : Documentation du W3C sur les formulaires

- Manuel PHP : Chapitre 38. Gestion des chargements de fichier