Qui dit « site web dynamique » dit généralement « formulaires » et donc traitement de ces derniers. PHP a notamment été inventé pour ce type de tâche et c'est ce que nous allons étudier dans ce nouveau tutoriel. Nous apprendrons à exploiter les formulaires par le biais des tableaux superglobaux $_GET et $_POST. Nous déterminerons aussi la différence qui existe dans l'utilisation de chacun d'eux.
Remarques :
[1] Pour la suite du tutoriel, nous considèrerons que les bases concernant les formulaires HTML sont acquises. Nous n'aborderons que synthétiquement les points essentiels de ces derniers à savoir les noms, valeurs, enctypes et methodes.
[2] Par ailleurs nous considèrerons que tous les tutoriels précédents des bases de PHP sont compris et acquis.
Il est bien évident que TOUS les formulaires doivent être traités en priorité avec PHP. Toutefois, rien n'empêche l'utilisation de Javascript en tant que surcouche au travail de PHP. Pourquoi PHP en priorité alors ? Il y'a plusieurs raisons à cela :
Remarque : dans la mesure où les informations proviennent de personnes anonymes, nous ne pouvons garantir la véracité et la dangerosité de ces dernières. C'est pourquoi tout doit être vérifié. Le principe numéro 1 lorsque l'on interagit avec un utilisateur est de ne jamais lui faire confiance.
Introduction
Afin de pouvoir faire dialoguer correctement un formulaire HTML avec un script PHP, il faut s'assurer que les points suivants soient présents :
A titre d'information, voici un exemple tout simple de formulaire d'upload qui présente tous les éléments obligatoires évoqués ici.
Exemple de formulaire d'upload
<!-- Debut du formulaire -->
<form enctype="multipart/form-data" action="./upload.php" method="post">
<fieldset>
<legend>Formulaire</legend>
<p>
<label>Envoyer le fichier :</label>
<input name="fichier" type="file" />
<input type="submit" name="submit" value="Uploader" />
</p>
</fieldset>
</form>
<!-- Fin du formulaire -->
Détaillons ici les méthodes GET et POST. Quelle différence les sépare ? Pour laquelle opter ?
La méthode HTTP GET
La méthode GET (celle qui est utilisée par défaut si rien n'est renseigné) fait circuler les informations du formulaire en clair dans la barre d'adresse en suivant le format ci-après :
Exemple d'url créée à partir de la méthode GET d'un formulaire
http://www.unsite.com/chemin/scriptphp.php?var1=valeur1&var2=valeur2
Cette adresse signifie que nous allons transmettre à la page scriptphp.php les couples variable / valeur transmis en paramètre. La première variable d'une url est toujours précédée du symbôle ? alors que les autres seront précédées du symbôle &. Les noms des variables correspondent aux attributs name des éléments du formulaire et les valeurs aux attributs value.
Note : contrairement à ce que l'on peut lire fréquemment sur la toile, la limite maximale d'une URL n'est pas de 255 caractères. Il n'existe en réalité aucune limite standard. En effet, la taille maximale d'une URL peut-être configurée à la fois côté serveur ou côté client. Un administrateur de serveur web peut à sa guise augmenter ou diminuer la longueur maximale des URLs. Quant aux navigateurs, eux aussi fixent par défaut une taille maximale. Il est donc recommandé de ne pas abuser de la longueur d'une URL lorsque l'on ne maîtrise pas l'intégralité de son environnement de production (serveur Web et clients).
La méthode HTTP POST
La méthode POST, quant à elle, transmet les informations du formulaire de manière masquée mais non cryptée. Le fait de ne pas afficher les données ne signifie en rien qu'elles sont cryptées. Rappelons nous d'ailleurs que ces informations utilisent le protocole HTTP et non HTTPS qui lui crypte les données.
Quelle est la meilleure méthode à adopter alors ? Et bien la réponse est : « ça dépend ». Le choix de l'une ou de l'autre se fera en fonction du contexte. Si par exemple, nous souhaitons mettre en place un moteur de recherches alors nous pourrons nous contenter de la méthode GET qui transmettra les mots-clés dans l'url. Cela nous permettra aussi de fournir l'url de recherches à d'autres personnes. C'est typiquement le cas des URLs de Google :
Exemple d'une URL du moteur de recherches Google
http://www.google.fr/search?q=php
La méthode POST est préférée lorsqu'il y'a un nombre important de données à transmettre ou bien lorsqu'il faut envoyer des données sensibles comme des mots de passe. Dans certains cas, seule la méthode POST est requise : un upload de fichier par exemple.
$_GET et $_POST sont des tableaux de données associatifs et superglobaux. Voici leurs principales caractéristiques :
Le tableau $_GET contient tous les couples variable / valeur transmis dans l'url. Pour accéder à la valeur d'une variable dont le nom est prenom, on l'appelle ainsi :
Le tableau $_POST contient tous les couples variable / valeur transmis en POST, c'est à dire les informations qui ne proviennent ni de l'url, ni des cookies et ni des sessions. Pour accéder à la valeur d'une variables dont le nom est prenom, on l'appelle ainsi :
La casse des variables est importante. Il faut bien penser à mettre $_GET et $_POST en majuscules. Dans le cas contraire, il sera impossible d'obtenir une valeur et une erreur de type undefined variable sera retournée.
Note : il existe aussi le tableau associatif superglobal $_REQUEST qui regroupe les 3 tableaux $_GET, $_POST et $_COOKIE que nous verrons au prochain cours. Il fonctionne exactement comme tous les autres tableaux.
Dans cette partie, nous allons utiliser un exemple simple de traitement de formulaire. Nous récupèrerons et vérifierons les données en provenance d'un formulaire d'authentification. Les principes de session seront évincés car ils ne constituent pas l'objet majeur du cours. Nous nous intéresserons uniquement à la saisie et la réception des données du formulaire.
Voici les pré-requis nécessaire à l'élaboration de notre exemple :
Passons au script complet. Il est entièrement commenté donc seules quelques petites explications seront apportées ensuite :
Script complet d'identification : exemple de traitement de formulaire
<?php
/*****************************************
* Constantes et variables
*****************************************/
define('LOGIN','Rasmus'); // Login correct
define('PASSWORD','lerdorf'); // Mot de passe correct
$message = ''; // Message à afficher à l'utilisateur
/*****************************************
* Vérification du formulaire
*****************************************/
// Si le tableau $_POST existe alors le formulaire a été envoyé
if(!empty($_POST))
{
// Le login est-il rempli ?
if(empty($_POST['login']))
{
$message = 'Veuillez indiquer votre login svp !';
}
// Le mot de passe est-il rempli ?
elseif(empty($_POST['motDePasse']))
{
$message = 'Veuillez indiquer votre mot de passe svp !';
}
// Le login est-il correct ?
elseif($_POST['login'] !== LOGIN)
{
$message = 'Votre login est faux !';
}
// Le mot de passe est-il correct ?
elseif($_POST['motDePasse'] !== PASSWORD)
{
$message = 'Votre mot de passe est faux !';
}
else
{
// L'identification a réussi
$message = 'Bienvenue '. LOGIN .' !';
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" >
<head>
<title>Formulaire d'identification</title>
</head>
<body>
<?php if(!empty($message)) : ?>
<p><?php echo $message; ?></p>
<?php endif; ?>
<form action="<?php echo htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES); ?>" method="post">
<fieldset>
<legend>Identifiant</legend>
<p>
<label for="login">Login :</label>
<input type="text" name="login" id="login" value="<?php if(!empty($_POST['login'])) { echo htmlspecialchars($_POST['login'], ENT_QUOTES); } ?>" />
</p>
<p>
<label for="password">Mot de passe :</label>
<input type="password" name="motDePasse" id="password" value="" />
<input type="submit" name="submit" value="Identification" />
</p>
</fieldset>
</form>
</body>
</html>
D'un point de vue général, nous avons structuré la page de cette façon :
Ce schéma de conception fait partie des bonnes pratiques de développement à adopter. On traite toujours les informations avant de les présenter. Avec cette méthode, nous pourrons par exemple effectuer des redirections sans risque d'erreur d'entêtes déjà envoyés.
Quelques lignes du code méritent tout de même des explications :
La première condition vérifie que le tableau $_POST existe et n'est pas vide. Si c'est le cas, alors elle renverra vrai (TRUE) et sera franchie pour accéder aux tests suivants.
On effectue ensuite une série de tests pour contrôler que les champs du formulaire ont bien été remplis et que les valeurs transmises correspondent aux constantes définies en tête du fichier. La fonction empty() vérifie que la variable passée en paramètre existe et qu'elle est vide ou nulle. Si l'on détecte une erreur, on l'enregistre dans la variable $message.
Si aucune erreur n'est détectée, cela signifie que les idenfiants sont corrects. On entre alors dans la clause else. Pour information, nous aurions du, en réalité, ouvrir une session et rediriger l'utilisateur vers la page protégée.
Dans le corps de la page, nous ajoutons une condition qui vérifie si la variable $message existe et qu'elle est bien remplie. Si c'est le cas, on franchit la condition et l'on affiche le message dans un bloc de paragraphe HTML.
La fonction htmlspecialchars() protège les variables en transformant les chevrons (< et >) et certains caractères HTML en entités HTML équivalentes. On utilise cette fonction pour se protéger d'éventuels actes de piratage par injection de code Javascript ou HTML (attaques de Cross Site Scripting ou XSS).
Nous appelons la variable d'environnement $_SERVER['REQUEST_URI'] qui contient l'url qui mène jusqu'à la page courante. Grâce à elle, nous précisons que le fichier qui va recevoir les données est lui même. Il est nécessaire de protéger cette variable car elle est sensible au piratage par injection de code HTML.
Enfin, dans l'attribut value du champ texte login, nous replaçons la dernière valeur postée par l'utilisateur. On la protège aussi car si le visiteur poste du code html, celui-ci sera directement interprêté. Il faut donc s'assurer de la sécurité de la variable que l'on souhaite réafficher.
Si la directive magic_quotes_gpc (GPC pour GET / POST et COOKIE) du php.ini est activée sur le serveur web, alors PHP va automatiquement protéger les chaînes en échappant par un antislash certains caractères spéciaux (apostrophes, guillemets...). Si par exemple nous postons la chaîne L'école est finie alors nous obtiendrons le résultat suivant :
Résultat d'une chaine de caractères échappée automatiquement
L\'école est finie
Cette protection agit donc comme la fonction addslashes(). L'antislash protège ici l'apostrophe. Ce caractère peut-être dangereux dans une requête SQL s'il n'est pas échappé. On parle de risque de piratage par injection de code SQL. Il faut alors faire attention à ne pas échapper une nouvelle fois les données avec cette fonction si les apostrophes magiques sont activées car les antislashes seraient alors eux aussi échapper par d'autres antislashes. Notre exemple précédent pourrait alors ressembler à cela si l'on échappe les données plusieurs fois :
Résultat d'une chaine de caractères échappée automatiquement
L\\\\\\\\\'école est finie
Cette initiative de protection a suscité de nombreux débats dans la communauté PHP car elle était en effet prévue pour combler les trous de sécurité créés par des programmeurs débutants et peu soucieux de la sécurité. Le passage à PHP 6 entraînera la désactivation de cette directive, ce qui forcera les développeurs à protéger eux mêmes leurs applications.
Nous recommandons de désactiver cette directive soit directement dans le php.ini si l'on a la main mise sur le serveur web ou bien en ajoutant cette ligne en haut de chacune des pages PHP :
Configuration temporaire du serveur web
<?php
// Désactivation des magic_quotes_gpc
ini_set('magic_quotes_gpc', 0);
?>
Nous sommes arrivés au terme de ce tutoriel. Nous avons tout d'abord démontré en quoi PHP devait être utilisé en priorité par rapport à Javascript pour traiter les formulaires. Puis, nous avons déterminé les éléments essentiels d'un formulaire HTML qui permettent un traitement par un script PHP. Dans cette lancée, nous avons expliqué les caractéristiques de chacune des deux méthodes HTTP GET et POST. Nous nous sommes ensuite arrêtés sur les tableaux superglobaux $_GET, $_POST et $_REQUEST qui permettent de récupérer les informations issues d'un formulaire ou d'une URL. Nous avons d'ailleurs illustré leur emploi au moyen d'un exemple de script réel modélisant un système d'identification, avant de conclure sur des principes de sécurité tels que les failles par injection de code HTML ou Javascript (XSS) et l'injection de commandes SQL.