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é :

exemple1.php
<?php
$toto = 3; $titi = 4; $somme = $toto + $titi; echo $somme;
?>


Voici ce même exemple, bien aéré :

exemple2.php
<?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 :

exemple3.php
<?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 :

exemple4.php
<?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 :

exemple5.php
<?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 <br /> 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).

exemple6.php
<?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 :

exemple7.php
<?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 :

exemple8.php
<?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 :

exemple9.php
<?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 :

exemple10.php
<?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.

ErreurRemède
Parse error: parse error in xxxx.php on line yIl 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 yVous 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 yErreur 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 yVous 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 yVous 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 yVous 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 yPHP 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 zPHP 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 zLa 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 zVous 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 yVé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 descriptorProblè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).
LoadingChargement en cours