[Plan du site]
Vous êtes ici ---
> Le Site du Zéro
> Les tutoriels
> Non-Officiels
> Site Web
> PHP
> Points particuliers
> Lecture du tutoriel
PHP, sa documentation et les points qui fâchent
Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Vous êtes développeur en PHP ?
Vous ne savez pas quoi faire et vous voulez lire un peu ?
Ce tutoriel est fait pour vous, il parle de choses et d'autres qui ont un rapport avec le PHP.
Il n'y a pas un sujet dans ce tutoriel, il y a plusieurs idées, concepts ou notions.
Bonne lecture.
Tout bon développeur doit connaître sur les bouts des doigts cette adresse :
http://fr.php.net/manual/fr/.
C'est
la référence absolue que tout développeur se doit de comprendre et de maîtriser.
Si vous cherchez une fonction, des explications sur les structures de langages, ou n'importe quoi d'autre en rapport avec PHP, c'est par là que vous devez commencer vos recherches.
Cette page est la table des matières de la documentation française : à partir de là, vous avez accès à la totalité des informations.
Son utilisation est extrêmement simple (comme toutes les bonnes tables des matières), vous n'avez qu'à lire les titres et vous trouverez les précieuses informations tant convoitées.
Imaginons que vous souhaitiez parfaire vos connaissances sur une fonction,
mysql_real_escape_string() par exemple. Que sait-on ?
- C'est une fonction ;
- ça se rapporte à MySQL ;
- ça se rapporte à une base de données.
Sachant que c'est une fonction, on se dirige tout naturellement vers le point VI,
la référence des fonctions.
Une fois cela fait, on va parcourir le point VI jusqu'à arriver à ce qui nous intéresse, la section XCVI,
MySQL.
En cliquant sur le lien de cette section, vous arriverez sur l'index des pages associées à MySQL. Vous y trouverez des informations diverses, comme les méthodes d'installation sur différents systèmes d'exploitation en fonction de la version de PHP que vous utilisez, les possibilités de configuration via le php.ini, ... et surtout, dans l'optique d'une recherche de fonction, une table des matières des fonctions MySQL. Cette table est très utile, elle réunit l'ensemble des fonctions tout en offrant une description succincte de chacune d'entre elles. Si vous recherchez une fonction sans en connaître le nom mais en connaissant son comportement (ce qu'elle fait), c'est par cette table des matières qu'il faut passer.
Mais revenons à nos moutons : on est intéressés par
mysql_real_escape_string(), après tout. On la cherche donc dans la table des matières (les fonctions sont listées par ordre alphabétique, ça va nous aider) et après avoir cliqué sur le lien, on arrive enfin à la page tant convoitée qui nous explique à peu près tout sur la fonction.
Vous avez tout d'abord la description succincte de la fonction, ainsi que les versions de PHP où elle est utilisable (il est important de vérifier que la fonction est définie dans votre version de PHP, sinon vous aurez droit à une belle erreur d'
undefined function 
).
Vient ensuite le prototype de la fonction ; dans notre cas, c'est :
Code : Console | string mysql_real_escape_string ( string unescaped_string [, resource link_identifier] ) |
Le mot avant le nom de la fonction vous indique le
type de la valeur retournée par la fonction (ici une
string, ou une chaîne en français).
Entre les parenthèses, ce sont les paramètres de la fonction.
Vous remarquerez que certains paramètres sont entourés de crochets : cela veut dire que les paramètres en question sont facultatifs, qu'ils ont des valeurs prédéfinies.
Devant chaque paramètre, vous avez encore ce petit mot qui vous indique quel est le type attendu pour le paramètre en question.
Il est très important de donner des paramètres qui ont le type attendu, sinon vous aurez des erreurs en cascade.
Après le prototype, vous avez une description plus complète de la fonction, elle vous indiquera très précisément ce que fait la fonction.
Vient ensuite la liste des paramètres. Dans cette section, on vous indique à quoi servent les différents paramètres que vous pouvez donner à la fonction (c'est très important de savoir à quoi sert tel ou tel paramètre).
Après cela, la valeur de retour de la fonction est expliquée. Il est extrêmement utile de connaître les différentes valeurs de retour des fonctions que vous utilisez ! Ça vous permet de mieux gérer les éventuelles erreurs (et surtout, de savoir d'où elles proviennent).
Juste en dessous, vous pourrez voir d'autres sections utiles comme des exemples (parfois des astuces, de bons conseils), des notes (ou précisions) qui vous expliqueront des points particuliers, exceptions ou autres, une liste de fonctions associées, proches ou en rapport avec la fonction qui vous intéresse et enfin, des messages de développeurs PHP.
Ces messages sont souvent en anglais ; il y a parfois de bonnes idées, mais parfois de mauvais conseils. Ainsi, ne vous y fiez pas trop.
Voilà pour ce que j'appelle la partie linéaire de la documentation (depuis le début, on suit un fil conducteur pour arriver à l'information désirée). Il y a différentes astuces pour faciliter l'accès aux informations, mais comme je suis méchant, je ne vous le dis que maintenant

.
Vous pouvez accéder à la description de n'importe quelle fonction via le lien :
http://fr.php.net/%s.
%s représente le nom de la fonction ; ainsi, pour la fonction
strpos() par exemple, on a :
http://fr.php.net/strpos.
Mais ça ne s'arrête pas là : tapez
math au lieu du nom d'une fonction, boum, vous tombez sur l'index des pages associées aux fonctions mathématiques

.
Petit bonus pour ceux qui utilisent Firefox : un raccourci bien pratique. Commencez par aller dans le menu
Marque-pages, puis dans l'option
Organiser les marque-pages. Ajoutez ensuite un marque-page via le bouton prévu à cet effet, donnez-lui le nom qui vous plaît, mettez
http://fr.php.net/%s comme adresse Web et pour le mot-clé, écrivez quelque chose d'équivoque et de pas trop long, comme
doc par exemple. Enregistrez le tout et tapez
doc strpos dans la barre d'adresses de Firefox, et miracle, vous tombez sur la page qui décrit la fonction
strpos().
Comme vous l'avez sans doute deviné, il vous suffit de changer ce qui suit le mot-clé
doc pour atterrir sur la page concernée.
Vous avez aussi à votre disposition un moteur de recherche interne en haut à droite de votre écran ; il vous permet de rechercher des fonctions même si vous ne connaissez pas leur nom complet, mais ça vous permet aussi de retomber sur diverses pages (tapez
math, juste pour voir).
Si vous suivez bien cette démarche, vous trouverez
toujours ce qui vous intéresse ; alors plus d'excuse pour demander si telle ou telle fonction existe, comment la fonction lambda s'utilise ou autre

.
Si l'on devait résumer la sécurité des applications PHP en une seule phrase, ça serait celle-là :
never trust user input (ne jamais faire confiance à l'utilisateur).
Il existe beaucoup de failles qui découlent de la non-vérification des saisies des utilisateurs : include, injections SQL, XSS, ...
Et pourtant, elles sont si simples à éviter quand on utilise une ou deux fonctions (et quand on réfléchit).
Il y a trois grands types de données que vous pouvez demander à l'utilisateur :
- des nombres ;
- des chaînes formatées ;
- des chaînes quelconques.
Pour chaque type, il y a des méthodes de protection qui varient en fonction de ce vous voulez faire de ces entrées (ces types découlent d'un choix personnel).
Le cas des nombres
Pour débuter en douceur, parlons des nombres.
Il y a deux types de nombres en PHP : les entiers (
int,
integer) et les flottants (
float).
Je me contente ici de la notation décimale.
Lorsque vous attendez un nombre venant de l'utilisateur, que ce soit par
$_POST,
$_GET ou
$_COOKIE, la méthode est la même, et très simple.
Premier cas, un entier
Pour recevoir un entier, vous avez deux façons de faire : la vérification du format ou le transtypage.
La vérification du format passe par
ctype_digit() (cf. la doc

) qui retourne
true si la chaîne passée en argument ne contient que des chiffres.
Exemple :
Code : PHP 1
2
3
4
5
6
7
8
9
10 | $a = '54';
if(ctype_digit($a))
echo 'Un entier';
else
echo 'Pas entier';
$a = '45.lala';
if(ctype_digit($a))
echo 'Un entier';
else
echo 'Pas entier';
|
Vous remarquerez que j'ai mis
$a = '54' et non
$a = 54, eh bien, même si ça peut paraître anodin, ça a son importance. Testez donc le code suivant :
Code : PHP 1
2
3
4
5
6
7
8
9
10 | $a = 54;
if(is_int($a))
echo 'Un entier';
else
echo 'Pas entier';
$a = '54';
if(is_int($a))
echo 'Un entier';
else
echo 'Pas entier';
|
Qu'est-ce que vous en concluez ? Eh bien oui,
'54' et
54 sont différents.
Pourquoi ? Tout simplement parce que le premier est une chaîne de caractères et que le second est un entier.
ctype_digit() renverra
true dans les deux cas, mais
is_int() ne renverra
true que dans le second cas, et si vous voulez mon avis, la doc vous dira pourquoi. En effet,
is_int() vérifie le type de la variable, alors que
ctype_digit() vérifie que la valeur de la variable est un entier.
Si je vous dis ça, ce n'est pas pour meubler le tuto, mais parce que quoi que vous fassiez,
is_int() vous renverra
toujours false si vous l'utilisez sur un
$_POST,
$_GET ou
$_COOKIE. La raison est simple et logique : toutes les variables des tableaux GPC (
_GET,
_POST,
_COOKIE) sont des chaînes de caractères. Et par conséquent, pour vérifier si un nombre est un entier, vous ne devrez jamais utiliser
is_int(), mais toujours
ctype_digit().
La deuxième méthode, le transtypage, a le mérite d'être beaucoup plus simple tout en étant aussi efficace.
L'idée est simple : transformer notre variable de type chaîne de caractères en variable de type entier.
Pour ce faire, on peut soit utiliser la fonction
intval(), soit l'
opérateur (int).
Exemple :
Code : PHP1
2
3
4
5
6 | $a = '54';
echo intval($a); // affiche 54
echo (int)$a; // affiche également 54
$b = '56lala';
echo intval($b); //affiche 56
echo (int)$b; // affiche également 56
|
Pour plus d'informations sur le transtypage, vous pouvez aller sur
cette page (méthode de recherche pour cette page :
Références du langage >
Les types >
Les notes >
Transtypage).
Deuxième cas, un nombre à virgule
Il arrive qu'on ait besoin de faire passer des nombres à virgules par GPC. C'est tout aussi simple que pour les entiers, sauf qu'ici on remplace
intval() par
floatval() et
(int) par
(float) 
.
Je ne décris pas la méthode de vérification, je préfère le transtypage, mais sachez qu'il existe une autre fonction,
is_numeric() qui peut être utile.
Les chaînes formatées
Pour ce genre de chaîne, pas de secret, c'est à vous de jouer. Je ne vais pas m'étendre là-dessus étant donné le nombre hallucinant de possibilités, mais je vous donnerai juste un exemple.
Si je souhaite récupérer via un formulaire quelque chose qui a un format précis, comme par exemple une adresse e-mail : dans ce cas, j'utiliserai une regex (plus d'informations sur
expreg.com ou encore sur
cet excellent tuto) pour vérifier que le format est correct.
Je veux une adresse mail, c'est-à-dire des caractères alphanumériques, suivis d'un @, suivis de caractères alphanumériques, d'un point et enfin de caractères alphanumériques.
Ainsi, pour vérifier que la chaîne est de la forme attendue :
Code : PHP 1
2
3
4
5
6
7
8
9
10 | $str = 'lala@lala.lala';
if(preg_match('`[[:alnum:]]+@[[:alnum:]]{2,6}\.[[:alnum:]]{2,4}`', $str))
echo 'Ok';
else
echo 'Mauvais format';
$str = 'vilain truc OR 1';
if(preg_match('`[[:alnum:]]+@[[:alnum:]]{2,6}\.[[:alnum:]]{2,4}`', $str))
echo 'Ok';
else
echo 'Mauvais format';
|
Les chaînes quelconques
Comme son nom l'indique, l'utilisateur peut entrer... ce qu'il veut. Beaucoup de cas existent, mais je vais commencer par vous parler d'un petit gadget bien ennuyant : les
magic_quotes !
Les
magic_quotes, ou
guillemets magiques sont une protection, un processus de PHP qui va rajouter un
addslashes() automatiquement sur toutes les variables GPC s'ils sont activés. A la base, c'était pour pallier au manque de sécurité induit par le manque d'attention de certains développeurs PHP. Mais en réalité, c'est une véritable plaie (enfin, pour moi

) qui sera tout simplement supprimée avec PHP6.
Mais toujours est-il qu'on doit en tenir compte. Tout d'abord, il faut savoir si les
magic_quotes sont activés ou pas. Heureusement, une fonction est là pour ça :
get_magic_quotes_gpc().
S'ils sont activés, la fonction retournera 1, sinon 0.
Ainsi, s'ils sont activés, on devra d'abord procéder à un
stripslashes() pour contrer les effets d'
addslashes().
Un exemple :
Code : PHP1
2
3
4 | if(get_magic_quotes_gpc()) {
$_POST = array_map('stripslashes', $_POST);
// idem pour $_GET et $_COOKIE
}
|
De cette façon, on a anéanti l'effet des
magic_quotes 
.
Retournons à nos chaînes quelconques maintenant.
Ce que je vais dire ici n'est pas une loi universelle, c'est une façon de faire.
Lorsque vous devez employer des chaînes quelconques dans une requête MySQL, il existe une fonction qui protège vos chaînes :
mysql_real_escape_string().
Avec cette fonction, vos requêtes sont protégées.
Seulement, ça ne suffit pas. En effet, si on n'utilise que cette fonction, rien n'empêche de mettre de l'HTML, du Javascript, et si vous affichez la chaîne telle quelle, l'utilisateur pourra déformer votre design, accèder à vos cookies, etc.
Pour empêcher l'utilisation de l'HTML et d'une partie du Javascript, il y a
htmlspecialchars(), qui remplacera les < et > par leur équivalent HTML, rendant impossible l'emploi d'HTML ou de Javascript via les balises
<script></script>.
Le
htmlspecialchars() peut se faire soit à l'insertion, soit à l'affichage. Mais en règle générale, mettez-le à l'affichage sauf dans le cas unique où vous êtes certains de ne jamais avoir à modifier le texte (ou alors, que le nombre de lectures soit très largement supérieur au nombre d'éditions).
Maintenant, imaginez que vous ajoutiez un pseudo-code, style BBCode ou zCode. Vous aurez peut-être une regex qui transformera quelque chose du style :
Code : Zcode1 | <lien url="http://machin.com">machin</lien>
|
En un :
Code : HTML1 | <a href="machin.com">machin</a>
|
Si par hasard on venait à mettre ceci :
Code : Zcode1 | <lien url="javascript:alert(document.cookie);">machin</lien>
|
Ça donnerait après parsage :
Code : HTML1 | <a href="javascript:alert(document.cookie);">machin</a>
|
Ce qui aura pour effet d'afficher l'ensemble des cookies dans une boîte de dialogue... Dans le pire des cas, un pirate pourrait vous rediriger vers une page piégée où il pourrait sauver vos cookies et souvent un hash de mot de passe : il pourra donc sans mal se connecter à votre compte sur le site où l'attaque a eu lieu. Pour éviter cela, il faudra penser à sécuriser ces balises,
preg_replace_callback() vous y aidera ; ensuite, vous devrez faire en sorte que l'utilisation du Javascript soit impossible (prenez aussi garde aux images, vous devriez toujours utiliser la fonction
getimagesize() pour vous assurer que l'élément vers lequel pointe le lien est bel et bien une image, et non une attaque sournoise).
Ces rappels ne sont pas exhaustifs, j'ai sans doute oublié de mentionner certaines choses

.
Toutefois, c'est une bonne base pour commencer à sécuriser vos applications.
Combien de fois avez-vous déjà eu ce joli message d'erreur ?
Citation : PHPWarning: mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource in C:\wamp\www\Dev\NGU\index.php on line 106
Quand on débute, c'est symptomatique, on impute l'erreur à
mysql_fetch_assoc(). Ce qui n'est pas faux, étant donné que c'est bien cette fonction qui génère l'erreur ; toutefois, la véritable cause de cette erreur est toujours un peu plus haut dans votre script. C'est ce que j'appelle une erreur en cascade (si si, rappelez-vous la partie sur la documentation

).
Revoyons le prototype de
mysql_fetch_assoc() (ou
mysql_fetch_array(),
mysql_fetch_object(), ... c'est strictement la même chose) :
Code : Console | array mysql_fetch_assoc ( resource result ) |
On voit que la fonction retourne un array (un tableau), mais ce n'est pas ce qui nous intéresse.
Le seul et unique paramètre de cette fonction est une
ressource, une ressource MySQL pour être plus précis. Les ressources sont des types de variables particuliers, et il y a
beaucoup de ressources.
Vous vous souvenez du moment où j'ai dit qu'il était absolument indispensable de connaître le type de variable à utiliser comme paramètre dans une fonction ?
Pour ceux qui en doutaient, voilà la preuve que je ne mentais pas

.
Cette erreur,
supplied argument is not a valid MySQL result resource, est une illustration de ce que j'ai dit.
Imaginons que vous ayez ce code :
Code : PHP1
2
3
4 | $req = mysql_query('SELECT col, col2, col3, FROM t_table');
while($res = mysql_fetch_assoc($req)) {
//blabla
}
|
Je peux vous assurer que vous aurez la même erreur que j'ai citée plus haut

.
Comme je l'ai également dit, la cause vient d'un peu plus haut, de
mysql_query() pour être précis.
Revoyons les valeurs de retour possibles de cette fonction :
Citation : DocumentationPour les requêtes du type SELECT, SHOW, DESCRIBE ou EXPLAIN, mysql_query() retournera une ressource en cas de succès, ou FALSE en cas d'erreur.
Vous voyez ce
FALSE, cette deuxième valeur de retour possible : c'est elle, la cause de mon erreur.
Repassons donc le film. J'envoie une requête à MySQL via
mysql_query(), seulement les gens attentifs auront remarqué que ma requête comporte une erreur : l'erreur, c'est d'avoir mis une virgule après
col3. Ma requête est donc erronée ; MySQL va l'analyser et renvoyer une erreur disant que la requête n'est pas correcte. On peut donc dire que la requête n'est pas un succès, donc
mysql_query() renverra
FALSE 
.
Or, on envoie le résultat de ce
mysql_query() à
mysql_fetch_assoc() qui attend une ressource MySQL valide, il n'a pas ce qu'il veut, il renvoie donc une erreur.
Dans un cas plus général, quand vous êtes confrontés à une erreur de
supplied argument is not, c'est toujours qu'un des paramètres n'est pas du bon type, et c'est donc presque toujours qu'il y a une erreur plus haut dans votre script (ou que vous avez fait une erreur dans le nom d'une variable, que vous avez oublié le
$, etc.).
Dans le cas particulier de mon exemple, il existe une solution très simple pour savoir quand une requête plante :
or die(mysql_error()).
Ces quelques mots vont vous sauver la vie ; reprenons l'exemple :
Code : PHP1
2
3
4 | $req = mysql_query('SELECT col, col2, col3, FROM t_table') or die(mysql_error());
while($res = mysql_fetch_assoc($req)) {
//blabla
}
|
C'est magique, voilà ce qui s'affiche :
Citation : mysql_error()You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM t_table' at line 1
Ça me dit bien que j'ai une erreur de syntaxe, c'est gagné, il n'y a plus qu'à corriger

.
Je ne vais pas vous décrire toutes les erreurs possibles, il y en a trop ; mais en voici quelques-unes assez fréquentes (si vous voulez que j'en ajoute, n'hésitez pas) :
Code : Console | Table 'ngu.t_table' doesn't exist |
La table en question n'existe pas.
Code : Console | Unknown column 'col' in 'field list' |
La colonne
'col' n'existe pas dans la liste des colonnes possibles.
Code : Console | Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause |
Vous avez mélangé des noms de colonnes et des
fonctions d'agrégats sans clause
GROUP BY (
select col, count(*) from t_table).
Code : Console | Column 'id' in where clause is ambiguous |
Cette erreur se produit souvent lors d'une jointure, quand vous avez une colonne dans deux ou plusieurs tables qui portent le même nom et que vous oubliez de préfixer le nom de la colonne par le nom de la table.
Il y en a beaucoup d'autres, mais comme vous l'aurez remarqué, quiconque bredouille l'anglais peut les comprendre sans difficulté.
Comme vous le voyez, ces erreurs sont toujours logiques et s'expliquent parfois en faisant simplement attention aux valeur de retour des fonctions, et aux types des paramètres des fonctions.
Ce tutoriel peut changer : je peux ajouter, supprimer ou modifier des informations ; donc n'hésitez pas à revenir si vous avez un peu de temps à perdre

.
Par ailleurs, si vous voyez des points à améliorer, à ajouter, n'hésitez surtout pas à m'en faire part.