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.
<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 eté 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.
<?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.
<?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.
<?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
<
/label
>
<
label>
<
input type
=
"
radio
"
name
=
"
civilite
"
value
=
"
Mme
"
/
>
Mme
<
/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
<
/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.
<?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
<
/label
>
<
label>
<
input type
=
"
checkbox
"
name
=
"
subjects[]
"
value
=
"
Développement
"
/
>
Développement
<
/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
<
/label
>
<
label>
<
input type
=
"
checkbox
"
name
=
"
uses[]
"
value
=
"
Développement
"
/
>
Développement
<
/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.
<?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.
<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).
<?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.
<?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.
<?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.
<?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
<?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.
<?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.
- 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'].
<?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.
<?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
>
- ['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.
<?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▲
- 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
<?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
>