Le débuggage
Maintenant que vous êtes assez familier avec le PHP, il vous est sûrement
déjà arrivé de rester coince par un bug pendant (et oui, ça arrive
^^) des heures entières.
La partie de débuggage est une phase
essentielle de la programmation d'une application, ne serait que pour
faire un rapport de tests : tester chaque fonction afin de voir ce qu'il
se passe dans tous les cas.
Le débuggage est également nécessaire lorsque l'on bloque sur un truc
que l'on ne comprend pas.
Avant de tomber dans un tel cas de bug où l'on ne peut plus avancer, il
existe de nombreuses règles à respecter afin de minimiser le risque de
bug.
Aérer le code
Aérer le code est très important.
Cela ne sert strictement à rien de vouloir faire de la compression de
code en ne sautant pas de lignes dans son code, et ce, en écrivant des
instructions les unes à la suite des autres sans sauter de lignes (si
si, j'ai déjà vu de tels cas ^^).
Exemple de code non aéré :
<?php
$toto = 3; $titi = 4; $somme = $toto + $titi; echo $somme;
?>
Voici ce même exemple, bien aéré :
<?php
$toto = 3;
$titi = 4;
$somme = $toto + $titi;
echo $somme;
?>
Naturellement, cet exemple peut paraître tout bête, mais lorsque votre
code commence à faire des centaines de lignes, le premier exemple (non
aéré) devient vite très énervent : vous ne vous retrouvez plus.
Indenter le code
Bien indenter le code vous permet de voir rapidement de voir un aperçu
de la structure de votre code.
L'indentation consiste à placer certains éléments clés du code
(comme les accolades par exemple) à un endroit bien précis et de s'y
tenir afin de lire aisément votre code.
Il existe plusieurs techniques (toutes défendables) pour indenter un
code, mais seules deux techniques sont réellement utilisées.
Une première forme d'indentation, consiste à placer une seule
instruction par ligne (la dessus, en général, tout le monde est
d'accord ^^), et lorsque l'on place une conditionnelle ou bien une
boucle, et bien nous plaçons l'accolade ouvrante en fin de ligne de
conditionnelle ou de boucle, puis nous utilisons une tabulation sur les
instructions contenues dans cette conditionnelle ou dans cette boucle.
Enfin, l'accolade fermante de notre conditionnelle ou de notre boucle se
placera au niveau de la conditionnelle ou de la boucle.
Exemple :
<?php
$toto = 2;
if ($toto == 2) {
echo '$toto vaut 2';
}
elseif ($toto == 3) {
echo '$toto vaut 3';
}
else {
echo '$toto n\'est pas égal à 2 ou 3';
}
?>
Pour les partisans de l'autre technique, les grandes lignes restent les
mêmes, mais la différence se joue au niveau de l'accolade ouvrante des
conditionnelles ou des boucles.
En effet, certains mettent l'accolade ouvrante, non pas à la fin de la
ligne de conditionnelle ou de boucle, mais à la ligne suivante, et
l'accolade étant au même niveau que la conditionnelle ou de la boucle.
En reprenant l'exemple précèdent, nous aurons donc :
<?php
$toto = 2;
if ($toto == 2)
{
echo '$toto vaut 2';
}
elseif ($toto == 3)
{
echo '$toto vaut 3';
}
else
{
echo '$toto n\'est pas égal à 2 ou 3';
}
?>
Tout comme pour l'aération du code, vous devez bien vous rendre compte
qu'un code bien indenté sera beaucoup plus lisible qu'un code mal
indenté où il faut à chaque fois deviner où se terminent les
conditionnelles et les boucles.
Alors que là, en regardant votre éditeur de texte, pour voir où se
termine une conditionnelle, vous n'avez qu'à suivre des yeux le niveau
(de tabulation) de votre conditionnelle et de faire défiler le texte.
Dès que vous tomberez sur une accolade fermante, et bien c'est cette
accolade qui représente la fin de votre conditionnelle (vous n'avez pas
besoin réfléchir de l'endroit où se trouve l'accolade fermant cette
conditionnelle).
Commenter le code
Commenter votre code !!!
Je me rappelle encore d'un professeur qui me disait qu'un code sans
commentaire ne servait strictement à rien.
En effet, sur le moment (lorsque l'on est bien chaud ^^), on écrit des
dizaines de lignes de code (et des fois, vraiment pas évidentes au
premier abord) que l'on comprend parfaitement (parce que l'on a
l'algorithme en tête).
Mais dans un mois ? Dans un an ?
Seriez vous aussi sur de comprendre en 2 minutes ce que vous avez écrit
quelques mois plus tôt ?
Pas sur...
Sans commentaires, votre code est pauvre.
Imaginons également qu'un autre programmeur lise votre code.
Sera-t-il capable de comprendre le cheminement de votre pensée ?
Pour toutes ces raisons, je vous invite chaudement à commenter votre
code.
Et j'en ai même personnellement fait les frais.
Combien de fois je ne me suis jamais demandé ce que j'avais dans la tête
le jour où j'ai pondu ce code x, et ce, même pour des langages où je
me sens à l'aise.
Attention aussi à ne pas tomber dans l'excès de commentaires.
En effet, cela ne sert strictement à rien de mettre un commentaire de
ce genre :
<?php
// on affiche la somme
echo $somme;
?>
Il ne faut pas non plus prendre tous les programmeurs (ainsi que vous au
passage ^^) pour des cruches :)
La documentation PHP existe. Si la personne qui lit votre code ne connaît
pas l'utilité de la fonction echo, il lui
suffit d'ouvrir son manuel PHP et de voir le rôle cette fonction.
Placer des commentaires sur vos fonctions (2 / 3 lignes de commentaires
avant le code de la fonction décrivant les paramètres de la fonction
et son rôle ne peut être qu'utile), sur vos sections critiques dans
votre code (par exemple sur une difficulté algorithmique), dans vos entêtes
de classes pour décrire le rôle de votre classe, le genre d'objets
qu'elle génère.
Comment débugger
Si, malgré toutes les précautions que nous avons vu précédemment,
vous rester bloquer avec un script récalcitrant qui ne fonctionne pas
comme vous le souhaitez (alors qu'il le devrait selon vous ^^), il va
falloir débugger.
Pour débugger, il faut déjà notamment retirer tout ce qui est inutile
au fonctionnement du script.
Nettoyer notamment le code PHP de tous ses echo
de code html (laisser juste des
histoire d'y voir un peu clair quand même ^^).
En effet, pour le moment, votre script bug. Taper directement dans le
vif.
Pour le design, on verra après.
D'ailleurs, pour éviter d'avoir ce genre de problème, je vous
conseille de faire tous vos scripts sans aucun artifice de design (une
fois que votre script fonctionnera sans problème, vous pourrez alors
vous occuper de sa mise en page).
Toujours dans le but de débugger, prenez l'habitude lors de la phase de
conception d'un script, d'afficher le contenu de vos
variables (afin de bien voir ce qu'elles ont dans le ventre lors
de l'exécution du script).
Une petite astuce pour afficher tout ce qui est variable de type chaîne
de caractères.
Lorsque vous voulez afficher leur contenu, afficher leur contenu entre
deux points par exemple (afin de voir si la variable ne contient pas en
début ou en fin de chaîne un espace qui peut être source de bug).
<?php
$chaine = " test";
echo '.'.$chaine.'.';
?>
En ce qui concerne les variables de type tableau (array), vous pouvez
visualiser leur contenu à l'aide de la fonction print_r.
Exemple :
<?php
$tablo = array ('a' => 'pomme', 'b' => 'banane', 'c' => array ('x', 'y', 'z'));
print_r ($tablo);
?>
Ce qui affichera :
Array
(
[a] => pomme
[b] => banane
[c] => Array
(
[0] => x
[1] => y
[2] => z
)
)
Un conseil, lorsque vous faites un print_r,
visualiser le en affichant la source de votre page (sous Internet
Explorer : Menu affichage Source). Vous verrez ainsi le contenu de votre
tableau tout indenté ce qui est beaucoup plus lisible.
PS : print_r peut être utilisé sur tous vos
types de variables.
Cas des conditionnelles
Il peut arriver que les conditionnelles n'aient pas le comportement
souhaité à l'origine.
En effet, quelques fois, votre script ne rentre pas dans le if
mais dans le else alors qu'il devrait faire le
contraire.
Prenez la même méthode que précédemment en affichant le contenu de
vos variables afin de voir ce qui cloche.
Prenez également attention aux tests de votre conditionnelle.
En effet, en écrivant par exemple :
<?php
$toto = 5;
if ($toto = 4) {
echo '$toto vaut 4';
}
?>
Et bien votre code passera toujours dans le if, et il affichera toujours
$toto vaut 4.
Ceci est du à l'utilisation d'un seul = pour faire votre test (vous
faites en fait une affectation au lieu d'une comparaison qui elle se
fait avec ==).
Cas des boucles
Le cas des boucles est plus ou moins similaire à celui des
conditionnelles.
En effet, si votre boucle ne démarre pas du tout, vérifier la valeur
de votre compteur à l'initialisation de la boucle.
De même, si votre boucle semble tourner à l'infini, vérifier bien que
la valeur pour la sortie de la boucle arrivera à coup sur.
Si par contre votre boucle effectue des traitements non voulus sur vos
données, prenez l'habitude de placer un echo
dans votre boucle afin de voir la valeur de vos variables à chaque
passage de boucle.
Pour pourrez ainsi mieux apprécier le comportement de votre boucle sur
vos variables.
Exemple :
<?php
$toto = 2;
for ($i = 0; $i < 5; $i++) {
$resultat = $toto * $i;
echo 'Passage numéro '.$i.' => multiplication = '.$resultat;
echo '<br />';
}
?>
Cas des fichiers
Les erreurs arrivent assez facilement avec l'utilisation des fichiers si
l'on ne prend pas garde à certains points.
Lorsque vous avez des erreurs en utilisant des fichiers, vérifier
toujours :
- d'une part le chemin pour accéder à votre fichier (chemins relatifs
/ absolus)
- d'autre part le chmod de ce fichier (afin de voir si vous avez les
droits pour accéder à ce fichier).
Problèmes avec MySQL
Voici plusieurs conseils qui vous permettront d'éviter certaines
erreurs incompréhensibles avec MySQL.
Tout d'abord, prenez l'habitude de placer vos requêtes SQL dans des
variables.
Cela peut paraître rédhibitoire au départ, mais cela a plusieurs
avantages.
En effet, en plaçant vos requêtes SQL dans une variable (par exemple $sql,
au lieu de faire directement un mysql_query)
vous allez pouvoir afficher votre requête SQL (via un echo
$sql), ce qui constitue un réel avantage dans le cas de requêtes
contenant des variables gérées par PHP (cela vous permet de bien voir
si la requête contient les bonnes valeurs pour chacun des éléments gérés
par PHP).
Un autre avantage découle aussi de ce premier conseil.
En effet, si votre requête à l'air de bien passer mais que, a priori,
la récupération des éléments de la requête pose problème, il
arrive souvent que l'on se demande si c'est la requête qui s'est bien déroulée
et qui ne retourne aucun résultat ou bien si c'est notre code de récupération
qui pose problème.
Afin d'en avoir le coeur net, faites un echo
de votre $sql, et copier coller votre requête
dans votre PHPMyAdmin : si PHPMyAdmin sort
bien un résultat, c'est que votre code de récupération n'est pas
fameux.
En revanche, si votre PHPmyAdmin ne retourne rien, c'est bien que requête
ne retourne aucun résultat.
De même, en avançant toujours dans cette direction, compter toujours
le nombre de résultat retourné de votre requête SQL à l'aide d'un mysql_num_rows,
ce qui va vous permettre d'afficher un texte au lieu de ne rien avoir
sur l'écran et de ne pas comprendre pourquoi il n'y a rien sur l'écran.
Exemple :
<?php
$sql = 'SELECT toto FROM table WHERE test="ok"';
$req = mysql_query($sql) or die('Erreur SQL !<br />'.$sql.'<br />'.mysql_error());
$nb = mysql_num_rows ($req);
if ($nb == 0) {
echo 'Aucun résultat retourné.';
}
else {
// Récupération des résultats et affichage
}
mysql_free_result ($req);
?>
Prenez également l'habitude de mettre un or die
muni de la fonction mysql_error sur vos
lancement de requêtes SQL afin de voir (si la requête ne passe pas) ce
qui pose problème.
Faites aussi toujours un mysql_free_result sur
votre requête de type SELECT lorsque celle-ci est terminée afin de libérer
la mémoire nécessaire à l'exécution de votre requête.
En effet, d'une part, cela soulage le serveur, et d'autre part, cela évite
de récupérer les résultats d'une autre requête faite précédemment.
Les messages d'erreurs fréquents
Enfin, si malgré toutes ces précautions, il vous arrive de bloquer sur
une erreur, voici un tableau regroupant les erreurs les plus communes
que l'on peut avoir en programmant avec PHP accompagnées de petits
indices vous permettant de les résoudre.
|
:: Erreur ::
|
:: Remède ::
|
|
Parse
error: parse error in xxxx.php on line y
|
Il s'agit d'une erreur de syntaxe. Vérifiez
si vous n'avez pas oublié un ; marquant la fin d'une instruction.
Verifier également si il ne manque pas un $ (dollar) devant le
nom d'une variable. N'hésitez pas à contrôler les lignes précédentes.
L'erreur se trouve souvent juste au-dessus.
|
|
Warning:
php_SetCookie called after header has been sent in xxxx.php on
line y
|
Vous avez tenté d'initialiser un cookie après
que l'entête HTTP soit envoyé au client. Vérifiez si une sortie
(echo, print, message d'erreur, ligne blanche, code html avant les
tags php) ne se fait pas avant votre initialisation de cookie
|
|
Warning:
MySQL Connection Failed: Access denied for user: ....
|
Erreur de connexion à la base MySQL. Vérifiez
vos paramètres de connexion
|
|
Warning:
Unable to create [chemin] No such file or directory in your script
on line [numero]
|
Le chemin vers le répertoire sensé contenir
le fichier ou bien le chemin du répertoire dans lequel le fichier
doit être crée est incorrect
|
|
Warning:
0 is not a MySQL result index in xxxx.php on line y
|
Erreur probable au niveau de la requête SQL.
Vérifiez votre requête SQL : en particulier les champs manipulés,
le nom de ou des tables impliquées, etc...
|
|
Warning:
Variable $zzzz is not an array or string in xxxx.php on line y
|
Vous tentez de manipuler une valeur numérique
avec une fonction dédiée aux chaînes ou aux tableaux.
|
|
Warning:
Variable $zzzz is not an array or object in xxxx.php on line y
|
Vous tentez de manipuler une valeur numérique
avec une fonction dédiée aux tableaux ou aux objets.
|
|
Warning:
Cannot add header information headers already sent in xxxx.php on
line y
|
Vous avez tenté d'effectuer un Header après
que l'entête HTTP ait envoyé au client. Vérifiez si une sortie
(echo, print, message d'erreur, voir même du code html) ne s'exécute
pas avant votre Header
|
|
Fatal
error: Maximum execution time exceeded in xxxx.php on line y
|
PHP dispose d'un mécanisme permettant de se
prémunir des scripts susceptibles d'engendrer un temps d'exécution
trop important pouvant saturer un serveur. Par défaut, ce temps
est de 30 secondes.
|
|
Fatal
error: Allowed memory size of 8388608 bytes exhausted (tried to
allocate x bytes) in yyyy.php on line z
|
PHP dispose d'un mécanisme permettant de se
prémunir des scripts susceptibles d'engendrer une consommation mémoire
trop importante pouvant saturer un serveur. Par défaut, une
limite est fixée à environ 8 Mo (8388608 octets).
|
|
Fatal
Error: Call to undefined function: xxxx() in yyy.php on line z
|
La fonction que vous appelez n'existe pas. Ce
peut-être une fonction liée à une librairie externe (GD, Zlib,
PDF, etc.). Dans ce cas, un simple phpinfo() vous renseignera sur
les paramètres de compilation de votre version de PHP. Peut-être
s'agit-il sinon d'une de vos propres fonctions. Vérifiez alors
qu'elle existe (notamment si votre script y accéde bien si elle
se trouve dans un autre fichier). Et dans tous les cas, contrôlez
de plus près le nom de la fonction appelée (orthographe, etc.).
Une erreur de frappe est vite arrivée.
|
|
Fatal
Error: Cannot redeclare xxxx() in yyy.php on line z
|
Vous avez certainement déclaré plusieurs
fois la même fonction. Contrôlez à nouveau l'ensemble des
fonctions que vous avez créées. Et n'hésitez pas à vérifier
également dans les éventuels fichiers inclus. C'est souvent dans
un script secondaire que vous trouverez le doublon. Veillez aussi
à ne pas utiliser le nom d'une fonction propre à PHP ou à l'une
de ses librairies.
|
|
Fatal
error: Input in flex scanner failed in xxxx on line y
|
Vérifiez vos include et require. Il y a fort
à croire que vous avez indiqué un chemin incomplet (genre /usr/local/
sans préciser de fichier).
|
|
Failed
opening '%s' for inclusion (include_path='%s')
|
Le fichier n'a pas pu être inclus dans votre
script, car PHP n'a pas pu y accéder : vérifiez les droits
(utilisateur PHP, droits du fichier), les noms et chemins du
fichier inclus.
|
|
file("%s")
- Bad file descriptor
|
Problème d'accès à un fichier avec la
fonction file(). Vérifiez bien que l'URL est valide. (l'URL
"http://www.super.php") est invalide alors qu'une erreur
de type 404 sera valide.
|
|
Wrong
parameter count for %s()
|
La fonction est appelée avec un nombre
insuffisant de paramètre, ou bien avec trop de paramètres.
Certaines fonctions ont besoin d'un minimum de paramètres (array()),
et généralement d'un maximum.
|
|
stat
failed for %s (errno=%d - %s)
|
Impossible d'accéder au fichier (problème
de droits ou de chemin d'accès).
|
La portée des variables
Voyons maintenant un caractère propre à beaucoup de langages de
programmation fonctionnels, dits, de haut niveaux : la
portée des variables.
En PHP, jusqu'à présent, lorsque vous déclariez une variable dans
votre script, vous aviez l'habitude d'accéder à cette variable dans
tout le reste de votre page.
En effet, d'après le code suivant :
<?php
$toto = 5;
// fin du script
?>
$toto est une variable qui sera accessible par
votre script PHP une fois sa déclaration faite, c'est-à-dire juste après
l'instruction $toto = 5.
On peut observer le même comportement lorsque l'on utilise des include.
En effet, une fois que l'on a déclaré une variable, cette variable est
accessible directement dans tous les scripts que l'on inclut à notre
script courant.
Exemple :
<?php
$toto = 5;
include ('script.php');
?>
Ici, $toto sera accessible, c'est à dire que
l'on pourra l'appeler directement (elle contiendra alors sa valeur : 5)
dans le script script.php.
Vous savez également, qu'une variable déclarée dans le corps d'une
fonction n'est accessible que dans le corps de cette même fonction.
Par exemple :
<?php
$toto = 5;
function ma_fonction () {
echo $toto;
}
ma_fonction();
?>
Dans ce cas, l'exécution de ce code PHP n'affichera rien vu que $toto
(contenu dans le code de la fonction ma_fonction())
n'a strictement aucun rapport avec la variable $toto
contenu dans le script courant.
On appellera la variable $toto (celle contenue
dans le code de la fonction) comme étant une variable
locale à la fonction ma_fonction()
(elle n'est pas globale au script).
Le mot clé global
Sachez qu'il est possible d'utiliser dans le code même de vos fonctions
des variables que vous avez déclarées dans votre script courant à
l'aide du mot clé global.
Reprenons notre exemple précédent :
<?php
$toto = 5;
function ma_fonction () {
global $toto;
echo $toto;
}
ma_fonction();
?>
Cet exemple affichera :
5
En effet, dans ce script, nous déclarons la variable (qui était jusque
là locale) $toto comme étant une variable globale
du script.
PHP sait alors qu'il doit récupérer la valeur de cette variable dans
le script courant.
Il n'y a aucune limite au nombre de variables globales qui peuvent être
manipulées par une fonction.
Vous pouvez également effectuer la même opération en utilisant le
tableau associatif $_GLOBALS.
Le code suivant donnera le même résultat que le code précédent :
<?php
$toto = 5;
function ma_fonction () {
echo $_GLOBALS["toto"];
}
ma_fonction();
?>
Comme vous le voyez, le tableau $GLOBALS est
un tableau associatif avec le nom des variables globales comme clef et
les valeurs des éléments du tableau comme valeur des variables, ce qui
ressemble un peu aux tableau associatifs que vous connaissez déjà
comme $_POST ou $_GET
qui vous permettent de récupérer la valeur des champs de vos
formulaires.
Le mot clé static
Les amateurs de programmation par objets doivent bien connaître ce
terme de static :)
Une variable dite static est une variable
locale à une fonction mais qui a la particularité de se souvenir
de sa valeur.
Prenons comme exemple le code suivant :
<?php
function ma_fonction () {
$toto = 1;
echo $toto;
$toto++;
}
?>
Si je lance cette fonction 50 fois, vous allez tous penser que cette
fonction affichera 50 fois la valeur 1, et vous aurez raison :)
Dans ce cas, l'incrémentation ne sert à rien puisque lorsque l'on a
fini d'exécuter cette fonction PHP "perd connaissance" de la
valeur de la variable $toto.
En revanche, si on déclare la variable $toto
comme étant une variable statique (grâce au mot clé static),
le comportement de cette fonction sera totalement différent.
En effet, vu que $toto est statique, PHP se
souviendra de sa valeur lors de la dernière exécution de la fonction.
Avec le code suivant :
<?php
function ma_fonction () {
static $toto = 1;
echo $toto;
$toto++;
}
?>
Si je lance 50 fois ma fonction, et bien nous verrons sur l'écran, la
suite des nombres entre 1 et 50.
Explication : à chaque appel, PHP se souvient de la précédente valeur
de la variable $toto (parce qu'elle est static)
et notre fonction incrémente alors cette valeur pour ensuite
l'afficher.
Le passage par référence
Dans nos exemples précédents, vous avez que grâce au mot clé static,
les fonctions pouvaient récupérer les valeurs des variables globales.
Sachez qu'il est également possible de modifier la valeur d'une
variable d'un script (une variable globale) grâce à une fonction en
utilisant un passage de variable par référence.
Le passage par référence se caractérise par l'utilisation d'un &
avant le nom de votre variable dans les arguments de votre fonction.
Exemple :
<?php
$toto = 5;
function ma_fonction ($var) {
$var++;
}
ma_fonction(&$toto);
echo $toto;
?>
Ce script affichera :
6
Pour les personnes qui ont fait du C, le passage par référence
s'assimile à la notion de pointeur.
En passant une variable par référence à une fonction, nous
ne passons pas en fait la variable en elle-même (la preuve :
notre fonction utilise une variable $var alors
qu'elle modifie tout de même la valeur de la variable $toto
du script courant) mais une référence vers la zone
mémoire où est stockée la valeur de notre variable $toto.
Notre fonction modifie alors directement cette zone mémoire en lui
donnant une nouvelle valeur, ce qui explique que dans le script courant,
et bien la valeur de $toto a changé.