Cours complet de programmation en PHP enjeux et pratique
...
Analyser des chaînes formatées
sscanf($date, '%d/%d/%d')
sscanf() est une autre fonction liée à printf(), qui ana-lyse une chaîne et tente d'établir une correspondance avec un motif contenant des espaces réservés. Dans l'exemple suivant, la chaîne $date contient une date. Elle est analysée en utilisant la chaîne '%d-%d-%d' qui contient plusieurs espaces réservés.
La fonction retourne un tableau avec toutes les valeurs correspondant aux emplacements des espaces réservés. Ce tableau est ensuite passé à vprintf(), qui l'imprime.
Analyse de chaînes formatées (sscanf.php)
Une liste de noms de variable peut aussi être fournie pour les paramètres supplémentaires de sscanf(). La fonction écrit alors les sous-chaînes qui correspondent aux espaces réservés dans ces variables, comme le montre le code suivant :
Analyse de chaînes formatées ()
Obtenir des informations détaillées concernant des variables
var_dump(false);
Les valeurs des variables peuvent être transmises directement au client en utilisant print() ou echo(). Quelquefois, cette approche peut cependant se révéler problématique, comme l'illustre bien l'exemple des valeurs booléennes. echo(true) imprime 1, mais echo(false) n'imprime rien. Il est alors bien préférable d'utiliser var_dump(), une fonction qui imprime également le type de la variable. Le code précédent retourne ainsi la chaîne bool(false).
Cette méthode fonctionne également pour les objets et les tableaux : var_dump() répond ainsi parfaitement aux besoins des développeurs qui souhaitent effectuer des opérations de débogage sans utiliser de débogueur.
Info
var_export() est une autre variante proche de var_dump(). Elle fonctionne de manière similaire, à deux différences près :
Rechercher des sous-chaînes dans des chaînes
if (strpos($string, $substring) === false) {
echo 'No match found.';
} else {
echo 'Match found.';
}
Pour la recherche de sous-chaînes dans les chaînes, le PHP propose strpos() (et strrpos(), pour les recherches à partir de la fin de la chaîne). Cette fonction a la particularité de retourner l'indice de la première occurrence de la sous-chaîne et false si aucune occurrence n'est trouvée.
Si $string commence par $substring, strpos() retourne 0, qui est évalué à false. Il convient donc d'utiliser une comparaison avec === ou !== afin de tenir compte du type des données. Le code d'exemple au début de cette section montre comment utiliser strpos().
Comprendre les expressions régulières
Les expressions régulières désignent des motifs qui peuvent être retrouvés dans des chaînes. Deux types d'expressions régulières sont disponibles en PHP : les expressions régulières POSIX et les expressions régulières PHP. Les premières peuvent être installées lors de la configuration de PHP grâce au commutateur --with-regex. Les utilisateurs de Windows n'ont pas à se donner cette peine, la prise en charge de POSIX Regex étant dans leur cas activée par défaut.
Parmi les alternatives, on peut aussi citer les expressions régulières compatibles Perl (PCRE, Perl-Compatible Regular Expressions). Les PCRE sont souvent considérées comme plus rapides et proposent de fait plus de fonctionnalités. Elles sont activées par défaut, mais si vous compilez l'installation PHP par vous-même, vous pouvez les désactiver avec le commutateur --without-pcre-regex.
Les motifs des expressions régulières permettent de rechercher une sous-chaîne dans une chaîne. strpos() le fait cependant aussi, et plus rapidement. L'avantage des expressions régulières tient à ce qu'elles proposent des fonctionnalités spéciales, comme les caractères de remplacement ou jokers. Le Tableau 1.2 présente les différents caractères spéciaux et leur signification correspondante.
Tableau 1.2 : Caractères spéciaux dans les expressions régulières
Caractère Description Exemple spécial
^ Début de la chaîne ^a signifie : une chaîne qui commence par a.
$ Fin de la chaîne a$ signifie : une chaîne qui se termine par a.
? 0 ou 1 fois (en référence
au précédent caractère ou à l'expression précédente)
* 0, 1 ou plusieurs fois
(en référence au précédent caractère ou à l'expression précédente)
+ 1 ou plusieurs fois (en référence au précédent caractère ou à l'expression précédente) ab? signifie : a ou ab.
ab* signifie : a ou ab ou abb, etc.
ab+ signifie : ab ou abb ou abbb, etc.
[...] Caractères alternatifs PHP[45] signifie : PHP4 ou PHP5
...
Utiliser des expressions régulières POSIX
La recherche d'une correspondance au moyen d'expres-sions régulières POSIX s'opère avec la fonction ereg(). Vous devez fournir un motif, une chaîne dans laquelle la recherche doit s'effectuer et un nom de variable pour récupérer les résultats sous forme de tableau. Le premier élément dans le tableau se rapporte à la correspondance complète. Les éléments suivants correspondent aux différents sous-motifs trouvés (définis entre parenthèses), en avançant des motifs internes vers les motifs externes, de gauche à droite.
Le code suivant propose un exemple utilisant la fonction phpversion() pour retourner le numéro de la version installée de PHP. La Figure 1.4 présente la sortie de ce script avec PHP 5.0.4.
<?php
$string = 'This site runs on PHP ' . phpversion(); ereg('PHP (([0-9])\.[0-9]\.[0-9]+)', $string, $matches); vprintf('Match: %s<br /> Version: %s; Major: %d.', ➥$matches);
?>
Recherche dans des chaînes avec les expressions régulières POSIX (ereg.php)
Le symbole + après la dernière expression [0–9] de cet exemple permet de tenir compte des versions de PHP du type 4.3.11. Si vous ne souhaitez pas que la recherche tienne compte de la casse des caractères, ajoutez i au nom de la fonction : eregi().
Utiliser des expressions régulières compatibles Perl
preg_match('/php ((\d)\.\d\.\d+)/i', $string, $matches)
La mise en correspondance de motifs avec des expressions régulières compatibles Perl (PCRE) s'opère en utilisant preg_match() pour ne rechercher qu'une seule occurrence et preg_match_all() s'il peut falloir trouver plusieurs occurrences.
La syntaxe est la même que pour ereg() : le motif, puis la chaîne, puis le tableau résultant. Le motif est cependant fourni différemment. Vous devez utiliser des délimiteurs. En général, on utilise des barres obliques (slash). Des instructions supplémentaires peuvent être fournies après le délimiteur.
L'instruction g permet d'effectuer une recherche globale (pour plusieurs correspondances). L'instruction i désactive la sensibilité à la casse.
<?php
$string = 'This site runs on PHP ' . phpversion(); preg_match('/php ((\d)\.\d\.\d+)/i', $string, $matches); vprintf('Match: %s<br /> Version: %s; Major: %d.', `$matches);
?>
Recherche de chaînes avec PCRE (preg_match.php)
La fonction preg_match_all() fonctionne exactement de la même manière. Le tableau résultant est multidimensionnel. Chaque entrée dans ce tableau est un tableau de correspondances tel que l'aurait retourné preg_match(). Le code suivant propose un exemple.
<?php
$string = 'This site runs on PHP ' . phpversion(); preg_match('/php ((\d)\.\d\.\d+)/i', $string, $matches); vprintf('Match: %s<br /> Version: %s; Major: %d.', `$matches);
?>
Retrouver des balises avec des expressions régulières
preg_match_all('/<.*?>/', $string, $matches)
L'une des forces de PCRE ou de POSIX tient au fait qu'elles acceptent certains types de constructions spéciales. Considérons un exemple. Habituellement, les expressions régulières établissent des correspondances dites "gourmandes". Considérez ainsi l'expression régulière suivante :
<.*>
Qu'obtient-on si l'on essaie de retrouver des correspondances dans la chaîne suivante ?
<p>Sex, drugs and <b>PHP</b>.</p>
La chaîne complète. Evidemment, la balise <p>, prise isolément, correspond elle aussi au motif, mais les expressions régulières cherchent habituellement à obtenir la correspondance la plus étendue. Pour récupérer la balise seule, il faut alors employer un stratagème astucieux, du genre : <[^>]*>. On peut pourtant faire plus simple, en utilisant le modificateur ? après le quantificateur * pour activer les correspondances "non gourmandes".
<?php
$string = '<p>Sex, drugs and <b>PHP</b>.</p>';
preg_match_all('/<.*?>/', $string, $matches);
foreach ($matches[0] as $match) {
echo htmlspecialchars("$match ");
}
?>
Produit en sortie : <p> <b> </b> </p>
Valider les saisies obligatoires
function checkNotEmpty($s) { return (trim($s) !== ''); }
Lors de la validation des champs de formulaire (pour plus d'informations sur les formulaires HTML, voir le Chapitre 4), plusieurs vérifications peuvent être opérées. Ces tests doivent cependant être minimisés autant que possible. Si un utilisateur français qui tente de commander un produit aux Etats-Unis n'y parvient pas parce que la validation du numéro de téléphone ne fonctionne qu'avec les numéros américains, l'objectif est manqué.
La meilleure vérification que l'on puisse opérer consiste à s'assurer qu'une saisie a bien été réalisée. Qu'appelle-t-on cependant "une saisie" ? Si quelqu'un n'entre qu'un espace blanc (autrement dit des caractères d'espace ou d'autres caractères non textuels), peut-on considérer que le champ est rempli correctement ?
La meilleure méthode consiste dans ce cas à utiliser trim() avant de vérifier si la variable ou l'expression contient quelque chose. La fonction trim() supprime tous les types de caractères d'espace blanc, dont les caractères d'espace normal, les tabulations horizontales et verticales, les retours chariot et les sauts de ligne. Si après cette suppression la chaîne n'est pas vide, on peut en conclure que le champ obligatoire a bien été rempli.
Info
Le fichier check.php contient des appels d'exemple et tous les appels aux fonctions de validation du fichier check.inc.
Valider des nombres
(et d'autres types de données)
Plusieurs possibilités sont offertes en PHP pour vérifier si une valeur correspond à un nombre (ou peut être convertie en un nombre). Pour commencer, on peut utiliser les fonctions d'assistance suivantes, qui vérifient le type de données des variables :
n is_array() – vérifie s'il s'agit d'un tableau.
n is_bool() – vérifie s'il s'agit d'une valeur booléenne.
n is_float() – vérifie s'il s'agit d'une valeur flottante.
n is_int() – vérifie s'il s'agit d'un entier.
n is_null() – vérifie si la valeur vaut null.
n is_numeric() – vérifie s'il s'agit d'un entier ou d'une valeur flottante.
n is_object() – vérifie s'il s'agit d'un objet.
n is_string() – vérifie s'il s'agit d'une chaîne.
Il convient de noter que les fonctions numériques (is_float(), is_int() et is_numeric()) tentent également de convertir les données de leur type d'origine vers le type numérique.
L'autre approche pour la conversion des types de données est empruntée du Java et des autres langages à typage fort comme le C. La variable ou l'expression peuvent être préfixées par le type de données entre parenthèses :
$numericVar = (int)$originalVar;
Dans ce cas, le moteur PHP tente véritablement de con-vertir les données à tout prix. (int)'3DoorsDown' retourne ainsi 3, alors que is_numeric('3DoorsDown') retourne false. A l'inverse, (int)'ThreeDoorsDown' retourne 0.
En général, is_numeric() (et is_int()/is_float()) semble préférable, car (int) retourne une valeur entière même pour une saisie invalide. Tout dépend de l'usage que l'on veut en faire.
Le code qui suit combine en quelque sorte le meilleur des deux mondes. Une valeur entrée est testée afin de vérifier s'il s'agit d'un nombre avec is_numeric(). Si c'est le cas, elle est ensuite convertie en un entier avec (int). Les variantes de ce code pour les autres types de données sont triviales.
function getIntValue($s) {
if (!is_numeric($s)) {
return false;
} else {
return (int)$s;
}
}
Génération de valeurs entières (check.inc.php; extrait)
Valider des adresses e-mail
Le test qui consiste à vérifier si une chaîne contient une adresse e-mail valide est à la fois très courant et très complexe. Le livre précédemment cité sur les expressions régulières consacre plusieurs pages à concevoir un ensemble d'expressions régulières permettant de réaliser cette opération.
...
2 Travailler avec des tableaux
Lorsque les variables simples ne suffisent pas, il faut passer aux tableaux (ou aux objets, mais nous laisserons pour l'instant ce sujet de côté). La section consacrée aux tableaux du manuel PHP (http://php.net/array) liste près de 80 fonctions utiles. Ce livre pourrait donc être tout entier consacré aux tableaux. Toutes ces fonctions ne sont pourtant pas souvent utilisées. Dans ce chapitre, nous présenterons les problèmes les plus importants que vous aurez à résoudre lorsque vous travaillerez avec des tableaux – et, bien évidemment aussi, les solutions à ces problèmes !
Il existe deux types de tableaux. Les noms qu'on leur donne diffèrent souvent, mais en général, on opère une distinction entre les tableaux numériques et les tableaux associatifs. Les premiers utilisent des clés numériques ; les seconds peuvent aussi utiliser des chaînes comme clés.
Les tableaux peuvent être créés de deux manières :
n en utilisant la fonction array() : $a = array('I', 'II', 'III', 'IV');
n en ajoutant successivement des valeurs à un tableau en utilisant le nom de la variable et des crochets :
$a[] = 'I'; $a[] = 'II'; $a[] = 'III'; $a[] = 'IV';
Avec les tableaux associatifs, les deux mêmes méthodes peuvent être utilisées, mais il faut cette fois fournir les clés et les valeurs :
$a1 = array('one' => 'I', 'two' => 'II', 'three' => r'III', 'four' => 'IV');
$a2['one'] = 'I'; $a2['two'] = 'II'; $a2['three'] = 'III'; $a2['four'] = 'IV';
Les tableaux peuvent également être imbriqués, lorsqu'un élément de tableau se trouve être lui-même un tableau :
$a = array( 'Roman' =>
array('one' => 'I', 'two' => 'II', 'three' =>
r'III', 'four' => 'IV'), 'Arabic' =>
array('one' => '1', 'two' => '2', 'three' => r '3', 'four' => '4')
);
Bien évidemment, les tableaux peuvent être créés dans un script, mais également provenir d'autres sources, dont des formulaires HTML (voir le Chapitre 4, "Interagir avec des formulaires Web") et des cookies et des sessions (voir le Chapitre 5, "Mémoriser les utilisateurs (cookies et des sessions)"). Mais une fois que le tableau est là, que fait-on ensuite ? Les sections qui suivent offrent quelques suggestions.
Accéder à tous les éléments des tableaux numériques
array('I', 'II', 'III', 'IV');
Pour parcourir les tableaux numériques, le plus simple est d'utiliser une boucle foreach parce que, à chaque itération de la boucle, l'élément courant du tableau est automatiquement écrit dans une variable.
<?php
$a = array('I', 'II', 'III', 'IV');
foreach ($a as $element) {
echo htmlspecialchars($element) . '<br />';
}
?>
Parcours d'un tableau en boucle avec foreach (foreach-n.php)
Il est cependant aussi possible d'utiliser une boucle for. Le premier élément du tableau possède l'indice 0. Le nombre d'indices du tableau peut être récupéré avec la fonction count().
<?php
$a = array('I', 'II', 'III', 'IV');
for ($i = 0; $i < count($a); $i++) {
echo htmlspecialchars($a[$i]) . '<br />';
}
?>
Parcours d'un tableau en boucle avec for (for-n.php)
Ces deux méthodes sont aussi bonnes (ou mauvaises) l'une que l'autre, quoique foreach s'avère souvent plus pratique. Il existe pourtant encore une troisième possibilité : la fonction PHP each(), qui retourne l'élément courant d'un tableau. La valeur de retour de each() est elle-même un tableau, dans lequel on peut accéder à la valeur en utilisant l'indice numérique 1 ou l'indice chaîne 'value'. Le tableau entier peut ainsi être parcouru avec une boucle while. Le code suivant imprime de nouveau tous les éléments du tableau, mais en utilisant cette fois la fonction each().
<?php
$a = array('I', 'II', 'III', 'IV');
while ($element = each($a)) {
echo htmlspecialchars($element['value']) .
➥ '<br />'; //or: $element[1]
}
?>
Parcours d'un tableau en boucle avec each() (each-n.php)
La sortie des trois listings est évidemment la même à chaque fois.
Accéder à tous les éléments des tableaux associatifs
foreach ($array as $key => $value) {
Lorsque vous utilisez un tableau associatif et que vous souhaitez accéder à toutes les données qu'il contient, les clés sont également importantes. La boucle foreach doit alors aussi fournir un nom de variable pour la clé de l'élément et non seulement pour sa valeur.
<?php
foreach ($array as $key => $value) = array('one'
r=> 'I', 'two' => 'II', 'three' => 'III', 'four'
r=> 'IV');
foreach ($array as $key => $value) {
echo htmlspecialchars("$key: $value") . '<br/>';
}
?>
Parcours d'un tableau associatif en boucle avec foreach (foreach-a)
Il est possible d'utiliser count(), qui retourne alors le nombre de valeurs dans le tableau et non le nombre d'éléments. Il n'est en revanche pas possible de parcourir en boucle tous les éléments avec for. each() et while peuvent être utilisés conjointement, comme le montre le code suivant. Notez que le nom de la clé peut être récupéré en utilisant l'indice 0 ou l'indice chaîne 'key'.
<?php
$a = array('one' => 'I', 'two' => 'II', 'three' => r'III', 'four' => 'IV');
while ($element = each($a)) {
' ' ' '
echo htmlspecialchars($element['key'] . ': ' . r$element['value']) . '<br />'; //ou : $element[0] / $element[1] }
?>
Parcours d'un tableau en boucle avec each() (each-a.php)
Accéder à tous les éléments des tableaux imbriqués
print_r($a);
Les tableaux imbriqués peuvent être très aisément imprimés avec print_r(). Examinez la sortie du listing à la Figure 2.1.
<pre>
<?php
$a = array(
'Roman' =>
array('one' => 'I', 'two' => 'II', 'three' =>
r'III', 'four' => 'IV'),
'Arabic' =>
array('one' => '1', 'two' => '2', 'three' =>
r '3', 'four' => '4')
);
print_r($a);
?>
</pre>
Figure 2.1 : Impression du contenu d'un tableau avec print_r().
Astuce
Si vous positionnez le deuxième paramètre de print_r() à true, le contenu du tableau n'est pas transmis au client mais retourné depuis la fonction, afin que vous puissiez enregistrer les informations dans une variable et les traiter ensuite.
La sortie du code précédent est cependant difficilement utilisable, hormis peut-être à des fins de débogage (voir Figure 2.1). Il convient donc de trouver un moyen habile d'accéder à toutes les données. Le plus logique semble être de faire appel à une fonction récursive. Tous les éléments du tableau sont alors imprimés, la sortie complète étant indentée en utilisant l'élément HTML <blockquote>. Si la valeur d'un élément de tableau est elle-même un tableau, la fonction l'appelle alors de manière récursive, ce qui génère un niveau supplémentaire d'indentation. La fonction PHP is_array() peut être utilisée pour déterminer si chaque valeur correspond ou non à un tableau. C'est ce que fait le code suivant. Le résultat produit en sortie est présenté à la Figure 2.2.
<?php
function printNestedArray($a) {
echo '<blockquote>';
foreach ($a as $key => $value) {
echo htmlspecialchars("$key: ");
if (is_array($value)) {
printNestedArray($value);
} else {
echo htmlspecialchars($value) . '<br />';
}
}
echo '</blockquote>';
}
$arr = array(
'Roman' =>
array('one' => 'I', 'two' => 'II', 'three' =>
r'III', 'four' => 'IV'),
'Arabic' =>
array('one' => 'I', 'two' => 'II', 'three' =>
r'III', 'four' => 'IV')
);
printNestedArray($arr);
?>
Impression d'un tableau imbriqué à l'aide d'une fonction récursive (printNestedArray)
Figure 2.2 : Impression du contenu d'un tableau à l'aide d'une fonction récursive.
Transformer un tableau en variables
while (list($key, $value) = each($a))
list() peut être très utile à chaque fois que l'on se sert de la fonction each(). Son principe est simple : vous devez fournir des noms de variable pour toutes les valeurs avec des indices numériques dans le tableau retourné par each(). Les boucles while/each() deviennent ainsi encore plus faciles à utiliser, comme le montre le code suivant. Les noms des variables sont fournis à l'intérieur des parenthèses.
…
4 Interagir avec des formulaires Web
Les formulaires HTML sont l'un des ingrédients clés de tout site Web dynamique, car ils permettent aux utilisateurs du site d'interagir avec lui. En leur absence, les sites Web sont plus ou moins statiques : leur contenu peut être généré à partir d'une base de données et changer régulièrement, mais il reste identique pour chaque visiteur. Les formulaires HTML outrepassent cette limitation. Le traitement des données de formulaires en PHP est donc un sujet d'importance.
La lecture des informations de formulaire est très simple : les données de formulaire transmises avec la méthode GET (autrement dit, par le biais de l'URI de la page demandée) peuvent être retrouvées dans $_GET[<valeur de l'attribut name du champ de formulaire>]. Ce n'est pourtant là qu'un début.
Supposons qu'un utilisateur remplisse un formulaire mais oublie de compléter un champ. Au lieu d'afficher un message d'erreur et de demander à l'utilisateur de cliquer sur le bouton Précédent dans son navigateur, vous pouvez proposer à l'utilisateur un nouveau formulaire dans lequel tous les champs sont remplis avec les valeurs précédemment fournies. Bon nombre d'ouvrages de programmation négligent ce point ou, pire, le traitent de manière incorrecte. Vous devez veiller à coder correctement les valeurs des champs de formulaire. Sans cela, le formulaire pourrait faire l'objet d'attaques XSS (Cross-Site Scripting) ou s'afficher de manière incorrecte.
La Figure 4.1 en offre l'illustration : deux boutons s'affichent avec la même légende. Pourtant, seule la légende du premier bouton a été correctement codée en HTML.
Figure 4.1 : Le codage approprié des caractères spéciaux est impératif.
Parmi les autres sujets d'importance, on retiendra le télé-chargement ascendant des fichiers via HTTP (HyperText Transfer Protocol) et la gestion des différents paramètres (notamment dans php.ini) susceptibles de contrarier le développeur.
Renvoyer des données au script courant
<form action="<?php echo htmlspecial-chars($_SERVER['PHP_SELF']); ?>"> </form>
Pratiquement tous les navigateurs renvoient les données de formulaire à la page courante si aucun attribut action n'est fourni dans l'élément <form>. Les spécifications HTML et XHTML (eXtensible HyperText Markup Ianguage) stipulent cependant toutes deux que l'attribut action est obligatoire (il porte la mention #REQUIRED dans les DTD). Le comportement de l'agent utilisateur n'est pas défini, comme l'explique la spécification HTML (http://w3.org/ TR/html4/interact/forms.html#adef-action). Il est donc judicieux de fournir l'URL spécifique du script courant comme action du formulaire. Le code précédent réalise exactement cette tâche et, par mesure de sécurité, opère au passage un échappement des caractères spéciaux dans $_SERVER['PHP_SELF'].
Lire des données de formulaire
Au départ, la lecture des données de formulaire était très simple : si le champ de formulaire possédait un attribut name (ou, dans les versions XHTML plus récentes, un attribut id) dont la valeur était "bidule", le PHP créait une variable $bidule de portée globale. Cette approche était bien pratique, mais d'un point de vue architectural, elle n'était pas judicieuse. Elle a donc été désactivée par défaut depuis la version 4.2, avec la directive php.ini suivante :
register_globals = Off
Les tableaux globaux suivants sont proposés depuis la version 3 du PHP pour les données de formulaire :
n $HTTP_GET_VARS, pour toutes les données fournies avec GET ;
n $HTTP_POST_VARS, pour toutes les données fournies avec POST ;
n $HTTP_REQUEST_VARS, pour toutes les données fournies avec GET ou POST ou via des cookies (déconseillés).
Ces tableaux sont globaux. Vous devez donc utiliser le mot clé global pour les élever à la portée globale si vous les utilisez à l'intérieur d'une fonction :
function processData() {
global $HTTP_POST_VARS;
// vous pouvez maintenant accéder à $HTTP_POST_VARS }
Ces tableaux peuvent cependant être désactivés (à partir de PHP 5) également avec la directive php.ini suivante :
register_long_arrays = Off
La méthode suivante est donc la seule recommandée pour accéder aujourd'hui à des données de formulaire en PHP :
n $_GET pour les données GET ;
n $_POST pour les données POST ;
n $_REQUEST pour les données POST, les données GET et les cookies (déconseillés).
Les clés de ces tableaux sont les noms des valeurs de formulaire. Les tableaux $_* sont appelés tableaux superglobaux, parce que vous n'avez pas à utiliser le mot clé global pour leur donner une portée globale. Ils sont automatiquement disponibles à l'intérieur des fonctions.
Lorsque vous avez choisi le tableau superglobal à utiliser (en fonction de la méthode d'envoi du formulaire), vous pouvez aisément accéder aux données : $_GET[<nomchampformulaire>] et $_POST[<nomchampformulaire>] récupèrent la valeur de l'élément de formulaire pour les méthodes GET ou POST. Le Tableau 4.1 montre quelles données sont retournées pour les différents types de champs de formulaire.
Tableau 4.1 : Types de champs de formulaire et données retournées par $ GET/$ POST
Type de champ Données retournées de formulaire
Champ texte Texte dans le champ
Champ de Texte dans le champ
mot de passe (texte clair, non crypté)
Champ texte Texte dans le champ
multiligne
Champ masqué Attribut value du champ
Bouton radio Attribut value du bouton radio sélectionné
Case à cocher Attribut value de la case à cocher si cochée
(ou "on" si aucune valeur n'est définie)
Tableau 4.1 : Types de champs de formulaire et données retournées par $ GET/$ POST(suite)
Type de champ Données retournées de formulaire
Liste de sélection
Liste de sélections multiples
Bouton
Envoyer Attribut value de l'élément de liste sélectionné (ou légende de l'élément de liste sélectionné, si aucune valeur n'est définie)
Attributs value des éléments de liste sélectionnés sous forme de tableau (ou légendes des éléments de liste sélectionnés sous forme de tableau, si les valeurs ne sont pas définies)
Attribut value du bouton, s'il a été utilisé pour envoyer le formulaire (important s'il y a plusieurs boutons Envoyer)
Astuce
Deux autres types de champs de formulaire spécifiques seront traités de manière spécifique plus loin dans ce chapitre : les boutons d'envoi de formulaire graphiques et les téléchargements de fichiers.
Gérer les "guillemets magiques"
if (get_magic_quotes_gpc()) {
$_GET = stripFormSlashes($_GET);
$_POST = stripFormSlashes($_POST);
}
Lorsque le paramètre de configuration magic_quotes est positionné à "On", toutes les données provenant de sources externes (et notamment de formulaires ou de cookies) bénéficient d'un traitement particulier. Tous les guillemets et toutes les apostrophes (" et ') sont échappés avec un caractère de barre oblique inversée (\). Si l'utilisateur tape C'est ma vie dans un champ texte, la valeur récupérée dans $_GET ou $_POST est donc C\'est ma vie. Ce traitement a été implémenté pour éviter les injections SQL (pour plus de détails à ce sujet, voir le Chapitre 8, "Utiliser le XML"), mais il est particulièrement irritant et plus encore pour les programmeurs non expérimentés, parce qu'il faut supprimer ces barres obliques inversées manuellement pour chaque champ de formulaire.
<?php
function stripFormSlashes($arr) {
if (!is_array($arr)) {
return stripslashes($arr);
} else {
return array_map('stripFormSlashes', $arr);
}
}
if (get_magic_quotes_gpc()) { $_GET = stripFormSlashes($_GET);
$_POST = stripFormSlashes($_POST); }
?>
Suppression des barres obliques inversées ajoutées par magic_quotes (stripFormSlashes.inc.php)
La fonction PHP stripslashes() supprime les barres obliques inversées d'échappement dans les chaînes. Cette fonction ne peut cependant être appelée que si les "guillemets magiques" ont été appliqués. Sans cela, elle détruit aussi les barres obliques inversées qui ont été volontairement ajoutées. Pour déterminer si les "guillemets magiques" sont actifs, vous pouvez appeler la fonction booléenne get_magic_quotes_gpc(). Si elle retourne true, toutes les barres obliques inversées peuvent être supprimées. Par souci pratique, elle a été placée dans l'exemple précédent à l'intérieur d'une fonction universelle appelée stripFormSlashes(). Avec array_map(), tous les éléments d'un tableau sont débarrassés des barres obliques inversées.
Ce fichier peut être inclus dans tous les fichiers qui traitent des données de formulaire, afin de prendre automatiquement en charge tous les "guillemets magiques".
Vérifier si un formulaire a été transmis
if (isset($_POST['Submit'])) { „. } else { „.
}
Lorsque le formulaire HTML et le code de traitement PHP se trouvent sur la même page (ce qui est conseillé pour le préremplissage des formulaires), il est important de déterminer si le formulaire est simplement appelé dans le navigateur (en utilisant GET) ou s'il a été transmis (auquel cas, les données doivent être traitées).
<?php
if (isset($_POST['Submit'])) { ...
echo '<h1>Thank you for filling out this form!</h1>'; } else { ...
?>
<form method="post" action="<?php echo htmlspecialchars
r ($_SERVER['PHP_SELF']); ?>"><input type="submit"
rname="Submit" value="Submit form" />
</form>
<?php
}
?>
Vérification de l'envoi d'un formulaire (submit.php – extrait)
Plusieurs approches sont possibles pour réaliser cette tâche. Il est possible d'attribuer au bouton d'envoi un nom puis de vérifier si ce nom est présent lorsque le formulaire est envoyé.
Astuce
Si votre formulaire inclut plusieurs boutons d'envoi, donnez à chacun un nom différent. Vous pourrez alors vérifier spécifi-quement la présence du nom et déterminer ainsi quel bouton a été utilisé.
Enregistrer des données de formulaire dans un cookie
setcookie('formdata', serialize($formdata), time()+30*24*60*60);
Une fois que le formulaire a été transmis, il faut que ses données aillent quelque part, par exemple dans une base de données (voir Chapitre 8) ou un e-mail. Si le site Web contient plusieurs formulaires semblables (par exemple, des formulaires qui requièrent tous que l'utilisateur fournissent son nom et ses informations de contact), il est judicieux d'enregistrer les données une fois que l'utilisateur les fournit. Puisque le HTTP est un protocole sans état, vous devez utiliser des cookies (voir le Chapitre 6, "Utiliser des fichiers dans le système de fichiers") – les sessions ne servent à rien dans ce cas parce qu'elles expirent lorsque l'utilisateur ferme le navigateur.
function setCookieData($arr) {
$formdata = getCookieData();
if ($formdata == null) {
$formdata = array();
}
foreach ($arr as $name => $value) {
$formdata[$name] = $value;
}
setcookie('formdata', serialize($formdata),
➥time()+30*24*60*60);
}
Enregistrement de données dans un cookie (getFormData.inc.php – extrait)
L'agent utilisateur ne peut enregistrer que 20 cookies par domaine au maximum. Il est donc judicieux de stocker l'ensemble des informations de formulaire dans un unique cookie, sous le formulaire d'un tableau. Les cookies n'acceptent cependant que des valeurs de chaîne, ce qui oblige à sérialiser le tableau. La fonction présentée dans le listing précédent écrit dans le cookie le contenu du tableau fourni en paramètre. La fonction getCookieData() que nous présenterons plus loin dans ce chapitre retourne les données existantes du cookie (si elles sont disponibles) et les désérialise en un tableau.
La seule chose qui reste à faire est d'écrire les données de formulaire requises dans ce tableau. Vous pouvez n'envoyer spécifiquement que certaines valeurs ou transmettre le tableau $_GET ou $_POST complet, comme le montre le code suivant.
<?php
require_once 'stripFormSlashes.inc.php'; require_once 'getFormData.in'; if (isset($_POST['Submit'])) { setCookieData($_POST);
Cours complet de programmation en PHP enjeux et pratique
...
Analyser des chaînes formatées
sscanf($date, '%d/%d/%d')
sscanf() est une autre fonction liée à printf(), qui ana-lyse une chaîne et tente d'établir une correspondance avec un motif contenant des espaces réservés. Dans l'exemple suivant, la chaîne $date contient une date. Elle est analysée en utilisant la chaîne '%d-%d-%d' qui contient plusieurs espaces réservés.
La fonction retourne un tableau avec toutes les valeurs correspondant aux emplacements des espaces réservés. Ce tableau est ensuite passé à vprintf(), qui l'imprime.
Analyse de chaînes formatées (sscanf.php)
Une liste de noms de variable peut aussi être fournie pour les paramètres supplémentaires de sscanf(). La fonction écrit alors les sous-chaînes qui correspondent aux espaces réservés dans ces variables, comme le montre le code suivant :
Analyse de chaînes formatées ()
Obtenir des informations détaillées concernant des variables
var_dump(false);
Les valeurs des variables peuvent être transmises directement au client en utilisant print() ou echo(). Quelquefois, cette approche peut cependant se révéler problématique, comme l'illustre bien l'exemple des valeurs booléennes. echo(true) imprime 1, mais echo(false) n'imprime rien. Il est alors bien préférable d'utiliser var_dump(), une fonction qui imprime également le type de la variable. Le code précédent retourne ainsi la chaîne bool(false).
Cette méthode fonctionne également pour les objets et les tableaux : var_dump() répond ainsi parfaitement aux besoins des développeurs qui souhaitent effectuer des opérations de débogage sans utiliser de débogueur.
Info
var_export() est une autre variante proche de var_dump(). Elle fonctionne de manière similaire, à deux différences près :
Rechercher des sous-chaînes dans des chaînes
if (strpos($string, $substring) === false) {
echo 'No match found.';
} else {
echo 'Match found.';
}
Pour la recherche de sous-chaînes dans les chaînes, le PHP propose strpos() (et strrpos(), pour les recherches à partir de la fin de la chaîne). Cette fonction a la particularité de retourner l'indice de la première occurrence de la sous-chaîne et false si aucune occurrence n'est trouvée.
Si $string commence par $substring, strpos() retourne 0, qui est évalué à false. Il convient donc d'utiliser une comparaison avec === ou !== afin de tenir compte du type des données. Le code d'exemple au début de cette section montre comment utiliser strpos().
Comprendre les expressions régulières
Les expressions régulières désignent des motifs qui peuvent être retrouvés dans des chaînes. Deux types d'expressions régulières sont disponibles en PHP : les expressions régulières POSIX et les expressions régulières PHP. Les premières peuvent être installées lors de la configuration de PHP grâce au commutateur --with-regex. Les utilisateurs de Windows n'ont pas à se donner cette peine, la prise en charge de POSIX Regex étant dans leur cas activée par défaut.
Parmi les alternatives, on peut aussi citer les expressions régulières compatibles Perl (PCRE, Perl-Compatible Regular Expressions). Les PCRE sont souvent considérées comme plus rapides et proposent de fait plus de fonctionnalités. Elles sont activées par défaut, mais si vous compilez l'installation PHP par vous-même, vous pouvez les désactiver avec le commutateur --without-pcre-regex.
Les motifs des expressions régulières permettent de rechercher une sous-chaîne dans une chaîne. strpos() le fait cependant aussi, et plus rapidement. L'avantage des expressions régulières tient à ce qu'elles proposent des fonctionnalités spéciales, comme les caractères de remplacement ou jokers. Le Tableau 1.2 présente les différents caractères spéciaux et leur signification correspondante.
Tableau 1.2 : Caractères spéciaux dans les expressions régulières
Caractère Description Exemple spécial
^ Début de la chaîne ^a signifie : une chaîne qui commence par a.
$ Fin de la chaîne a$ signifie : une chaîne qui se termine par a.
? 0 ou 1 fois (en référence
au précédent caractère ou à l'expression précédente)
* 0, 1 ou plusieurs fois
(en référence au précédent caractère ou à l'expression précédente)
+ 1 ou plusieurs fois (en référence au précédent caractère ou à l'expression précédente) ab? signifie : a ou ab.
ab* signifie : a ou ab ou abb, etc.
ab+ signifie : ab ou abb ou abbb, etc.
[...] Caractères alternatifs PHP[45] signifie : PHP4 ou PHP5
...
Utiliser des expressions régulières POSIX
La recherche d'une correspondance au moyen d'expres-sions régulières POSIX s'opère avec la fonction ereg(). Vous devez fournir un motif, une chaîne dans laquelle la recherche doit s'effectuer et un nom de variable pour récupérer les résultats sous forme de tableau. Le premier élément dans le tableau se rapporte à la correspondance complète. Les éléments suivants correspondent aux différents sous-motifs trouvés (définis entre parenthèses), en avançant des motifs internes vers les motifs externes, de gauche à droite.
Le code suivant propose un exemple utilisant la fonction phpversion() pour retourner le numéro de la version installée de PHP. La Figure 1.4 présente la sortie de ce script avec PHP 5.0.4.
<?php
$string = 'This site runs on PHP ' . phpversion(); ereg('PHP (([0-9])\.[0-9]\.[0-9]+)', $string, $matches); vprintf('Match: %s<br /> Version: %s; Major: %d.', ➥$matches);
?>
Recherche dans des chaînes avec les expressions régulières POSIX (ereg.php)
Utiliser des expressions régulières compatibles Perl
preg_match('/php ((\d)\.\d\.\d+)/i', $string, $matches)
La mise en correspondance de motifs avec des expressions régulières compatibles Perl (PCRE) s'opère en utilisant preg_match() pour ne rechercher qu'une seule occurrence et preg_match_all() s'il peut falloir trouver plusieurs occurrences.
La syntaxe est la même que pour ereg() : le motif, puis la chaîne, puis le tableau résultant. Le motif est cependant fourni différemment. Vous devez utiliser des délimiteurs. En général, on utilise des barres obliques (slash). Des instructions supplémentaires peuvent être fournies après le délimiteur.
L'instruction g permet d'effectuer une recherche globale (pour plusieurs correspondances). L'instruction i désactive la sensibilité à la casse.
<?php
$string = 'This site runs on PHP ' . phpversion(); preg_match('/php ((\d)\.\d\.\d+)/i', $string, $matches); vprintf('Match: %s<br /> Version: %s; Major: %d.', `$matches);
?>
Recherche de chaînes avec PCRE (preg_match.php)
La fonction preg_match_all() fonctionne exactement de la même manière. Le tableau résultant est multidimensionnel. Chaque entrée dans ce tableau est un tableau de correspondances tel que l'aurait retourné preg_match(). Le code suivant propose un exemple.
<?php
$string = 'This site runs on PHP ' . phpversion(); preg_match('/php ((\d)\.\d\.\d+)/i', $string, $matches); vprintf('Match: %s<br /> Version: %s; Major: %d.', `$matches);
?>
Retrouver des balises avec des expressions régulières
preg_match_all('/<.*?>/', $string, $matches)
L'une des forces de PCRE ou de POSIX tient au fait qu'elles acceptent certains types de constructions spéciales. Considérons un exemple. Habituellement, les expressions régulières établissent des correspondances dites "gourmandes". Considérez ainsi l'expression régulière suivante :
<.*>
<p>Sex, drugs and <b>PHP</b>.</p>
La chaîne complète. Evidemment, la balise <p>, prise isolément, correspond elle aussi au motif, mais les expressions régulières cherchent habituellement à obtenir la correspondance la plus étendue. Pour récupérer la balise seule, il faut alors employer un stratagème astucieux, du genre : <[^>]*>. On peut pourtant faire plus simple, en utilisant le modificateur ? après le quantificateur * pour activer les correspondances "non gourmandes".
<?php
$string = '<p>Sex, drugs and <b>PHP</b>.</p>';
preg_match_all('/<.*?>/', $string, $matches);
foreach ($matches[0] as $match) {
echo htmlspecialchars("$match ");
}
?>
Produit en sortie : <p> <b> </b> </p>
Valider les saisies obligatoires
function checkNotEmpty($s) { return (trim($s) !== ''); }
Lors de la validation des champs de formulaire (pour plus d'informations sur les formulaires HTML, voir le Chapitre 4), plusieurs vérifications peuvent être opérées. Ces tests doivent cependant être minimisés autant que possible. Si un utilisateur français qui tente de commander un produit aux Etats-Unis n'y parvient pas parce que la validation du numéro de téléphone ne fonctionne qu'avec les numéros américains, l'objectif est manqué.
La meilleure vérification que l'on puisse opérer consiste à s'assurer qu'une saisie a bien été réalisée. Qu'appelle-t-on cependant "une saisie" ? Si quelqu'un n'entre qu'un espace blanc (autrement dit des caractères d'espace ou d'autres caractères non textuels), peut-on considérer que le champ est rempli correctement ?
Info
Le fichier check.php contient des appels d'exemple et tous les appels aux fonctions de validation du fichier check.inc.
Valider des nombres
(et d'autres types de données)
Plusieurs possibilités sont offertes en PHP pour vérifier si une valeur correspond à un nombre (ou peut être convertie en un nombre). Pour commencer, on peut utiliser les fonctions d'assistance suivantes, qui vérifient le type de données des variables :
n is_array() – vérifie s'il s'agit d'un tableau.
n is_bool() – vérifie s'il s'agit d'une valeur booléenne.
n is_float() – vérifie s'il s'agit d'une valeur flottante.
n is_int() – vérifie s'il s'agit d'un entier.
n is_null() – vérifie si la valeur vaut null.
n is_numeric() – vérifie s'il s'agit d'un entier ou d'une valeur flottante.
n is_object() – vérifie s'il s'agit d'un objet.
n is_string() – vérifie s'il s'agit d'une chaîne.
Il convient de noter que les fonctions numériques (is_float(), is_int() et is_numeric()) tentent également de convertir les données de leur type d'origine vers le type numérique.
L'autre approche pour la conversion des types de données est empruntée du Java et des autres langages à typage fort comme le C. La variable ou l'expression peuvent être préfixées par le type de données entre parenthèses :
$numericVar = (int)$originalVar;
Dans ce cas, le moteur PHP tente véritablement de con-vertir les données à tout prix. (int)'3DoorsDown' retourne ainsi 3, alors que is_numeric('3DoorsDown') retourne false. A l'inverse, (int)'ThreeDoorsDown' retourne 0.
En général, is_numeric() (et is_int()/is_float()) semble préférable, car (int) retourne une valeur entière même pour une saisie invalide. Tout dépend de l'usage que l'on veut en faire.
function getIntValue($s) {
if (!is_numeric($s)) {
return false;
} else {
return (int)$s;
}
}
Génération de valeurs entières (check.inc.php; extrait)
Valider des adresses e-mail
Le test qui consiste à vérifier si une chaîne contient une adresse e-mail valide est à la fois très courant et très complexe. Le livre précédemment cité sur les expressions régulières consacre plusieurs pages à concevoir un ensemble d'expressions régulières permettant de réaliser cette opération.
...
2 Travailler avec des tableaux
Lorsque les variables simples ne suffisent pas, il faut passer aux tableaux (ou aux objets, mais nous laisserons pour l'instant ce sujet de côté). La section consacrée aux tableaux du manuel PHP (http://php.net/array) liste près de 80 fonctions utiles. Ce livre pourrait donc être tout entier consacré aux tableaux. Toutes ces fonctions ne sont pourtant pas souvent utilisées. Dans ce chapitre, nous présenterons les problèmes les plus importants que vous aurez à résoudre lorsque vous travaillerez avec des tableaux – et, bien évidemment aussi, les solutions à ces problèmes !
Il existe deux types de tableaux. Les noms qu'on leur donne diffèrent souvent, mais en général, on opère une distinction entre les tableaux numériques et les tableaux associatifs. Les premiers utilisent des clés numériques ; les seconds peuvent aussi utiliser des chaînes comme clés.
Les tableaux peuvent être créés de deux manières :
n en utilisant la fonction array() : $a = array('I', 'II', 'III', 'IV');
n en ajoutant successivement des valeurs à un tableau en utilisant le nom de la variable et des crochets :
$a[] = 'I'; $a[] = 'II'; $a[] = 'III'; $a[] = 'IV';
Avec les tableaux associatifs, les deux mêmes méthodes peuvent être utilisées, mais il faut cette fois fournir les clés et les valeurs :
$a1 = array('one' => 'I', 'two' => 'II', 'three' => r'III', 'four' => 'IV');
Les tableaux peuvent également être imbriqués, lorsqu'un élément de tableau se trouve être lui-même un tableau :
$a = array( 'Roman' =>
array('one' => 'I', 'two' => 'II', 'three' =>
r'III', 'four' => 'IV'), 'Arabic' =>
array('one' => '1', 'two' => '2', 'three' => r '3', 'four' => '4')
);
Bien évidemment, les tableaux peuvent être créés dans un script, mais également provenir d'autres sources, dont des formulaires HTML (voir le Chapitre 4, "Interagir avec des formulaires Web") et des cookies et des sessions (voir le Chapitre 5, "Mémoriser les utilisateurs (cookies et des sessions)"). Mais une fois que le tableau est là, que fait-on ensuite ? Les sections qui suivent offrent quelques suggestions.
Accéder à tous les éléments des tableaux numériques
array('I', 'II', 'III', 'IV');
Pour parcourir les tableaux numériques, le plus simple est d'utiliser une boucle foreach parce que, à chaque itération de la boucle, l'élément courant du tableau est automatiquement écrit dans une variable.
<?php
$a = array('I', 'II', 'III', 'IV');
foreach ($a as $element) {
echo htmlspecialchars($element) . '<br />';
}
?>
Parcours d'un tableau en boucle avec foreach (foreach-n.php)
Il est cependant aussi possible d'utiliser une boucle for. Le premier élément du tableau possède l'indice 0. Le nombre d'indices du tableau peut être récupéré avec la fonction count().
<?php
$a = array('I', 'II', 'III', 'IV');
for ($i = 0; $i < count($a); $i++) {
echo htmlspecialchars($a[$i]) . '<br />';
}
?>
Parcours d'un tableau en boucle avec for (for-n.php)
<?php
$a = array('I', 'II', 'III', 'IV');
while ($element = each($a)) {
echo htmlspecialchars($element['value']) .
➥ '<br />'; //or: $element[1]
}
?>
Parcours d'un tableau en boucle avec each() (each-n.php)
La sortie des trois listings est évidemment la même à chaque fois.
Accéder à tous les éléments des tableaux associatifs
foreach ($array as $key => $value) {
Lorsque vous utilisez un tableau associatif et que vous souhaitez accéder à toutes les données qu'il contient, les clés sont également importantes. La boucle foreach doit alors aussi fournir un nom de variable pour la clé de l'élément et non seulement pour sa valeur.
<?php
foreach ($array as $key => $value) = array('one'
r=> 'I', 'two' => 'II', 'three' => 'III', 'four'
r=> 'IV');
foreach ($array as $key => $value) {
echo htmlspecialchars("$key: $value") . '<br/>';
}
?>
Parcours d'un tableau associatif en boucle avec foreach (foreach-a)
Il est possible d'utiliser count(), qui retourne alors le nombre de valeurs dans le tableau et non le nombre d'éléments. Il n'est en revanche pas possible de parcourir en boucle tous les éléments avec for. each() et while peuvent être utilisés conjointement, comme le montre le code suivant. Notez que le nom de la clé peut être récupéré en utilisant l'indice 0 ou l'indice chaîne 'key'.
<?php
$a = array('one' => 'I', 'two' => 'II', 'three' => r'III', 'four' => 'IV');
while ($element = each($a)) {
' ' ' '
echo htmlspecialchars($element['key'] . ': ' . r$element['value']) . '<br />'; //ou : $element[0] / $element[1] }
?>
Parcours d'un tableau en boucle avec each() (each-a.php)
Accéder à tous les éléments des tableaux imbriqués
print_r($a);
Les tableaux imbriqués peuvent être très aisément imprimés avec print_r(). Examinez la sortie du listing à la Figure 2.1.
<pre>
<?php
'Roman' =>
array('one' => 'I', 'two' => 'II', 'three' =>
r'III', 'four' => 'IV'),
'Arabic' =>
array('one' => '1', 'two' => '2', 'three' =>
r '3', 'four' => '4')
);
print_r($a);
?>
</pre>
Figure 2.1 : Impression du contenu d'un tableau avec print_r().
Astuce
Si vous positionnez le deuxième paramètre de print_r() à true, le contenu du tableau n'est pas transmis au client mais retourné depuis la fonction, afin que vous puissiez enregistrer les informations dans une variable et les traiter ensuite.
La sortie du code précédent est cependant difficilement utilisable, hormis peut-être à des fins de débogage (voir Figure 2.1). Il convient donc de trouver un moyen habile d'accéder à toutes les données. Le plus logique semble être de faire appel à une fonction récursive. Tous les éléments du tableau sont alors imprimés, la sortie complète étant indentée en utilisant l'élément HTML <blockquote>. Si la valeur d'un élément de tableau est elle-même un tableau, la fonction l'appelle alors de manière récursive, ce qui génère un niveau supplémentaire d'indentation. La fonction PHP is_array() peut être utilisée pour déterminer si chaque valeur correspond ou non à un tableau. C'est ce que fait le code suivant. Le résultat produit en sortie est présenté à la Figure 2.2.
<?php
function printNestedArray($a) {
echo '<blockquote>';
foreach ($a as $key => $value) {
echo htmlspecialchars("$key: ");
if (is_array($value)) {
printNestedArray($value);
} else {
echo htmlspecialchars($value) . '<br />';
}
}
echo '</blockquote>';
}
$arr = array(
'Roman' =>
array('one' => 'I', 'two' => 'II', 'three' =>
r'III', 'four' => 'IV'),
'Arabic' =>
array('one' => 'I', 'two' => 'II', 'three' =>
r'III', 'four' => 'IV')
);
printNestedArray($arr);
?>
Figure 2.2 : Impression du contenu d'un tableau à l'aide d'une fonction récursive.
Transformer un tableau en variables
while (list($key, $value) = each($a))
list() peut être très utile à chaque fois que l'on se sert de la fonction each(). Son principe est simple : vous devez fournir des noms de variable pour toutes les valeurs avec des indices numériques dans le tableau retourné par each(). Les boucles while/each() deviennent ainsi encore plus faciles à utiliser, comme le montre le code suivant. Les noms des variables sont fournis à l'intérieur des parenthèses.
…
4 Interagir avec des formulaires Web
Les formulaires HTML sont l'un des ingrédients clés de tout site Web dynamique, car ils permettent aux utilisateurs du site d'interagir avec lui. En leur absence, les sites Web sont plus ou moins statiques : leur contenu peut être généré à partir d'une base de données et changer régulièrement, mais il reste identique pour chaque visiteur. Les formulaires HTML outrepassent cette limitation. Le traitement des données de formulaires en PHP est donc un sujet d'importance.
La lecture des informations de formulaire est très simple : les données de formulaire transmises avec la méthode GET (autrement dit, par le biais de l'URI de la page demandée) peuvent être retrouvées dans $_GET[<valeur de l'attribut name du champ de formulaire>]. Ce n'est pourtant là qu'un début.
La Figure 4.1 en offre l'illustration : deux boutons s'affichent avec la même légende. Pourtant, seule la légende du premier bouton a été correctement codée en HTML.
Figure 4.1 : Le codage approprié des caractères spéciaux est impératif.
Parmi les autres sujets d'importance, on retiendra le télé-chargement ascendant des fichiers via HTTP (HyperText Transfer Protocol) et la gestion des différents paramètres (notamment dans php.ini) susceptibles de contrarier le développeur.
Renvoyer des données au script courant
<form action="<?php echo htmlspecial-chars($_SERVER['PHP_SELF']); ?>"> </form>
Pratiquement tous les navigateurs renvoient les données de formulaire à la page courante si aucun attribut action n'est fourni dans l'élément <form>. Les spécifications HTML et XHTML (eXtensible HyperText Markup Ianguage) stipulent cependant toutes deux que l'attribut action est obligatoire (il porte la mention #REQUIRED dans les DTD). Le comportement de l'agent utilisateur n'est pas défini, comme l'explique la spécification HTML (http://w3.org/ TR/html4/interact/forms.html#adef-action). Il est donc judicieux de fournir l'URL spécifique du script courant comme action du formulaire. Le code précédent réalise exactement cette tâche et, par mesure de sécurité, opère au passage un échappement des caractères spéciaux dans $_SERVER['PHP_SELF'].
Lire des données de formulaire
Au départ, la lecture des données de formulaire était très simple : si le champ de formulaire possédait un attribut name (ou, dans les versions XHTML plus récentes, un attribut id) dont la valeur était "bidule", le PHP créait une variable $bidule de portée globale. Cette approche était bien pratique, mais d'un point de vue architectural, elle n'était pas judicieuse. Elle a donc été désactivée par défaut depuis la version 4.2, avec la directive php.ini suivante :
register_globals = Off
n $HTTP_GET_VARS, pour toutes les données fournies avec GET ;
n $HTTP_POST_VARS, pour toutes les données fournies avec POST ;
n $HTTP_REQUEST_VARS, pour toutes les données fournies avec GET ou POST ou via des cookies (déconseillés).
Ces tableaux sont globaux. Vous devez donc utiliser le mot clé global pour les élever à la portée globale si vous les utilisez à l'intérieur d'une fonction :
function processData() {
global $HTTP_POST_VARS;
// vous pouvez maintenant accéder à $HTTP_POST_VARS }
Ces tableaux peuvent cependant être désactivés (à partir de PHP 5) également avec la directive php.ini suivante :
register_long_arrays = Off
La méthode suivante est donc la seule recommandée pour accéder aujourd'hui à des données de formulaire en PHP :
n $_GET pour les données GET ;
n $_POST pour les données POST ;
n $_REQUEST pour les données POST, les données GET et les cookies (déconseillés).
Les clés de ces tableaux sont les noms des valeurs de formulaire. Les tableaux $_* sont appelés tableaux superglobaux, parce que vous n'avez pas à utiliser le mot clé global pour leur donner une portée globale. Ils sont automatiquement disponibles à l'intérieur des fonctions.
Lorsque vous avez choisi le tableau superglobal à utiliser (en fonction de la méthode d'envoi du formulaire), vous pouvez aisément accéder aux données : $_GET[<nomchampformulaire>] et $_POST[<nomchampformulaire>] récupèrent la valeur de l'élément de formulaire pour les méthodes GET ou POST. Le Tableau 4.1 montre quelles données sont retournées pour les différents types de champs de formulaire.
Tableau 4.1 : Types de champs de formulaire et données retournées par $ GET/$ POST
Type de champ Données retournées de formulaire
Champ texte Texte dans le champ
Champ de Texte dans le champ
mot de passe (texte clair, non crypté)
Champ texte Texte dans le champ
multiligne
Champ masqué Attribut value du champ
Case à cocher Attribut value de la case à cocher si cochée
(ou "on" si aucune valeur n'est définie)
Tableau 4.1 : Types de champs de formulaire et données retournées par $ GET/$ POST(suite)
Type de champ Données retournées de formulaire
Liste de sélection
Liste de sélections multiples
Bouton
Envoyer Attribut value de l'élément de liste sélectionné (ou légende de l'élément de liste sélectionné, si aucune valeur n'est définie)
Attributs value des éléments de liste sélectionnés sous forme de tableau (ou légendes des éléments de liste sélectionnés sous forme de tableau, si les valeurs ne sont pas définies)
Attribut value du bouton, s'il a été utilisé pour envoyer le formulaire (important s'il y a plusieurs boutons Envoyer)
Astuce
Deux autres types de champs de formulaire spécifiques seront traités de manière spécifique plus loin dans ce chapitre : les boutons d'envoi de formulaire graphiques et les téléchargements de fichiers.
Gérer les "guillemets magiques"
if (get_magic_quotes_gpc()) {
$_GET = stripFormSlashes($_GET);
$_POST = stripFormSlashes($_POST);
}
Lorsque le paramètre de configuration magic_quotes est positionné à "On", toutes les données provenant de sources externes (et notamment de formulaires ou de cookies) bénéficient d'un traitement particulier. Tous les guillemets et toutes les apostrophes (" et ') sont échappés avec un caractère de barre oblique inversée (\). Si l'utilisateur tape C'est ma vie dans un champ texte, la valeur récupérée dans $_GET ou $_POST est donc C\'est ma vie. Ce traitement a été implémenté pour éviter les injections SQL (pour plus de détails à ce sujet, voir le Chapitre 8, "Utiliser le XML"), mais il est particulièrement irritant et plus encore pour les programmeurs non expérimentés, parce qu'il faut supprimer ces barres obliques inversées manuellement pour chaque champ de formulaire.
<?php
function stripFormSlashes($arr) {
return stripslashes($arr);
} else {
return array_map('stripFormSlashes', $arr);
}
}
if (get_magic_quotes_gpc()) { $_GET = stripFormSlashes($_GET);
$_POST = stripFormSlashes($_POST); }
?>
Suppression des barres obliques inversées ajoutées par magic_quotes (stripFormSlashes.inc.php)
La fonction PHP stripslashes() supprime les barres obliques inversées d'échappement dans les chaînes. Cette fonction ne peut cependant être appelée que si les "guillemets magiques" ont été appliqués. Sans cela, elle détruit aussi les barres obliques inversées qui ont été volontairement ajoutées. Pour déterminer si les "guillemets magiques" sont actifs, vous pouvez appeler la fonction booléenne get_magic_quotes_gpc(). Si elle retourne true, toutes les barres obliques inversées peuvent être supprimées. Par souci pratique, elle a été placée dans l'exemple précédent à l'intérieur d'une fonction universelle appelée stripFormSlashes(). Avec array_map(), tous les éléments d'un tableau sont débarrassés des barres obliques inversées.
Ce fichier peut être inclus dans tous les fichiers qui traitent des données de formulaire, afin de prendre automatiquement en charge tous les "guillemets magiques".
Vérifier si un formulaire a été transmis
if (isset($_POST['Submit'])) { „. } else { „.
}
Lorsque le formulaire HTML et le code de traitement PHP se trouvent sur la même page (ce qui est conseillé pour le préremplissage des formulaires), il est important de déterminer si le formulaire est simplement appelé dans le navigateur (en utilisant GET) ou s'il a été transmis (auquel cas, les données doivent être traitées).
<?php
if (isset($_POST['Submit'])) { ...
echo '<h1>Thank you for filling out this form!</h1>'; } else { ...
?>
<form method="post" action="<?php echo htmlspecialchars
r ($_SERVER['PHP_SELF']); ?>"><input type="submit"
rname="Submit" value="Submit form" />
<?php
}
?>
Vérification de l'envoi d'un formulaire (submit.php – extrait)
Plusieurs approches sont possibles pour réaliser cette tâche. Il est possible d'attribuer au bouton d'envoi un nom puis de vérifier si ce nom est présent lorsque le formulaire est envoyé.
Astuce
Si votre formulaire inclut plusieurs boutons d'envoi, donnez à chacun un nom différent. Vous pourrez alors vérifier spécifi-quement la présence du nom et déterminer ainsi quel bouton a été utilisé.
Enregistrer des données de formulaire dans un cookie
setcookie('formdata', serialize($formdata), time()+30*24*60*60);
Une fois que le formulaire a été transmis, il faut que ses données aillent quelque part, par exemple dans une base de données (voir Chapitre 8) ou un e-mail. Si le site Web contient plusieurs formulaires semblables (par exemple, des formulaires qui requièrent tous que l'utilisateur fournissent son nom et ses informations de contact), il est judicieux d'enregistrer les données une fois que l'utilisateur les fournit. Puisque le HTTP est un protocole sans état, vous devez utiliser des cookies (voir le Chapitre 6, "Utiliser des fichiers dans le système de fichiers") – les sessions ne servent à rien dans ce cas parce qu'elles expirent lorsque l'utilisateur ferme le navigateur.
function setCookieData($arr) {
$formdata = getCookieData();
if ($formdata == null) {
$formdata = array();
}
foreach ($arr as $name => $value) {
$formdata[$name] = $value;
}
setcookie('formdata', serialize($formdata),
➥time()+30*24*60*60);
}
Enregistrement de données dans un cookie (getFormData.inc.php – extrait)
La seule chose qui reste à faire est d'écrire les données de formulaire requises dans ce tableau. Vous pouvez n'envoyer spécifiquement que certaines valeurs ou transmettre le tableau $_GET ou $_POST complet, comme le montre le code suivant.
<?php
require_once 'stripFormSlashes.inc.php'; require_once 'getFormData.in'; if (isset($_POST['Submit'])) { setCookieData($_POST);