5.2 Gestion des erreurs 223 initialisées. Dans beaucoup de cas, l’interpréteur PHP essaie de corriger automa- tiquement les imprécisions ou erreurs de syntaxe légères dans un script. Voici un exemple d’un script contenant beaucoup de minimes incorrections syntaxiques. En supposant que PHP est configuré dans un mode où la non-déclaration des variables est tolérée, la correction s’effectue silencieusement, avec des résultats parfois insatis- faisants. Exemple 5.5 exemples/TestErreur.php : Un script avec des erreurs minimes de code. <?php // Script montrant l ’usage du contrôle des erreurs header ("Content−type : text / plain"); define(ma_constante , 5) ; $tableau = array ("1" => "Valeur 1" , "second_element" => "Valeur 2" , "ma_constante" => "Valeur 3"); $texte = "Un texte à afficher"; echo "Affichage de la variable \$texte : $texTe\n"; echo " Premier élément = " . $tableau [1] . "\n" ; echo "Second élément = " . $tableau [ second_element ] . "\n" ; echo " Dernier élément = " . $tableau [ ma_constante ]; ?> Ce script se contente de produire du texte non HTML. Voici ce qui s’affiche dans la fenêtre du navigateur : Affichage de la variable $texte : Premier ´el´ement = Valeur 1 Second ´el´ement = Valeur 2 Dernier ´el´ement = Ce n’est pas tout à fait ce qui était souhaité. Le contenu de la variable $texte et celui du dernier élément du tableau ne s’affichent pas (voyez-vous d’où vient le problème ?). Ce genre d’anomalie peut passer inaperçu, ou être très difficile à détecter. Il est possible de régler le niveau des messages d’erreur produits par PHP avec la fonction error_reporting() qui prend en argument un ou plusieurs des niveaux de messages du tableau 5.1. Ces niveaux sont des constantes prédéfinies qui peuvent être combinées par des opérateurs de bits (voir page 429). L’appel à la fonction error_reporting() avec l’argument E_ERROR | E_WARNING demande l’affichage des deux types d’erreur. La valeur par défaut 3 est généralement E_ALL | ˜E_NOTICE ce qui signifie que toutes 3. Elle dépend de l’installation de PHP. 224 Chapitre 5. Organisation du développement Tableau 5.1 — Niveau des messages d’erreur dans PHP Valeur Niveau d’erreur Description E_ALL Tous les avertissements et erreurs ci-dessous. 1 E_ERROR Erreurs fatales (interruption du script). 2 E_WARNING Erreurs légères (le script continue). 4 E_PARSE Erreur de compilation/analyse. 8 E_NOTICE Avertissements (une erreur légère qui peut être intentionnelle, comme la non-initialisation d’une variable). 16 E_CORE_ERROR Erreurs fatales pendant le lancement de PHP. 32 E_CORE_WARNING Avertissement pendant le lancement de PHP. 64 E_COMPILE_ERROR Erreur fatale pendant la compilation. 128 E_COMPILE_WARNING Avertissement pendant la compilation. 256 E_USER_ERROR Erreur fatale engendrée par le programmeur. 512 E_USER_WARNING Erreur légère engendrée par le programmeur. 1024 E_USER_NOTICE Avertissement engendré par le programmeur. 1024 E_STRICT Avertissement indiquant une syntaxe PHP 4 qui risque de ne plus être supportée à l’avenir. les erreurs sont signalées, sauf les « avertissements ». Voici ce que l’on obtient avec le script précédent en plaçant au début un appel à error_reporting() avec la valeur E_ALL : <b>Notice</b>: Use of undefined constant ma_constante - assumed ’ma_constante’ in <b>TestErreur.php</b> on line <b>8</b> <b>Notice</b>: Undefined variable: texTe in <b>TestErreur.php</b> on line <b>15</b> Affichage de la variable $texte : Premier ´el´ement = Valeur 1 <b>Notice</b>: Use of undefined constant second_element - assumed ’second_element’ in <b>TestErreur.php</b> on line <b>17</b> Second ´el´ement = Valeur 2 <b>Notice</b>: Undefined offset: 5 in <b>TestErreur.php</b> on line <b>18</b> Dernier ´el´ement = Quatre erreurs de niveau E_NOTICE ont été détectées. La première indique l’oubli des apostrophes dans la définition de la constante ma_constante. PHP les a remises, ce qui est correct. La deuxième erreur concerne la variable $texTe (avec un « T » majuscule) qui n’est pas définie, d’où l’absence d’affichage. Ce genre de problème survient facilement et est très difficile à détecter. Troisième erreur : on a oublié les 5.2 Gestion des erreurs 225 apostrophes dans l’expression $tableau[second_element]. PHP n’a pas trouvé de constante nommée second_element et suppose donc – à raison – qu’il suffit de remettre les apostrophes. Enfin la dernière erreur est la même que précédemment, mais cette fois la constante existe et PHP la remplace par sa valeur, 5. L’entrée 5 du tableau n’existe pas et un message est donc produit, expliquant l’absence d’affichage pour le dernier élément du tableau. 5.2.2 Gestion des erreurs en PHP Les erreurs rencontrées ci-dessus sont engendrées par PHP qui se base sur des règles syntaxiques plus ou moins strictes selon le niveau choisi. Ces erreurs sont alors transmises au gestionnaire d’erreurs qui détermine comment les traiter. Une erreur PHP est décrite par quatre informations : 1. le niveau d’erreur (voir tableau 5.1) ; 2. le message d’erreur ; 3. le nom du script ; 4. le numéro de la ligne fautive dans le script. Le gestionnaire d’erreurs par défaut affiche ces informations à l’écran dès que l’erreur survient. On aura donc par exemple : <b>Notice</b>: Undefined offset: 5 in <b>TestErreur.php</b> on line <b>18</b> Ce fonctionnement est très pratique durant la phase de développement d’une application. En plaçant le niveau d’erreur à E_ALL (ou même à E_ALL | E_STRICT si on développe en PHP 5 « pur »), on affiche tous les messages PHP et on obtient le code le plus propre possible après avoir éliminé leur cause. Ce niveau d’erreur maxi- mal peut être obtenu globalement en modifiant le paramètre error_reporting dans le fichier php.ini, ou spécifiquement en appelant error_reporting() avec la valeur E_ALL. Quand l’application est mise en production, il est plus délicat d’afficher systémati- quement des messages qui peuvent correspondre à des erreurs anodines. L’alternative est de rediriger ces messages vers un fichier (error logging) en modifiant les paramètres de configuration suivants dans le fichier php.ini : • display_errors passe à Off ; • log_errors passe à On ; • error_log passe à stderr ou au nom du fichier de stockage. Un directive associée, ignore_repeated_errors, permet d’éviter (en la posi- tionnant à On) la répétition des messages relatifs à une même ligne dans un même fichier. Cela peut servir à ne pas donner l’occasion à un internaute malveillant d’engendrer un très gros fichier par répétition ad nauseam de la même manipulation engendrant une erreur. 226 Chapitre 5. Organisation du développement Quand on utilise Apache, stderr est redirigé vers le fichier error_log.Onpeut choisir d’utiliser un fichier comme /tmp/erreurs-php.log. On y trouvera donc toutes les erreurs engendrées par les applications PHP, qui ne seront plus affichées à l’écran si display_errors est positionné à Off. Cela suppose bien entendu un suivi régulier de ce fichier pour détecter rapidement les erreurs qui surviennent et ne pas laisser un site « planté » pendant des heures ou des jours. Signalons que la fonction error_log() peut être utilisée d’une part pour écrire directement dans le fichier des erreurs, d’autre part pour être averti par e-mail si on le souhaite. Il semble cependant préférable de mettre en place ce genre de politique grâce aux outils de personnalisation du traitement des erreurs, présentés plus loin, qui offrent l’avantage de pouvoir être redéfinis facilement pour un site particulier, indépendamment du reste de l’application. Erreurs engendrées par l’application Bien entendu PHP ne peut pas détecter les erreurs internes correspondant à la rupture de règles propres à l’application. Traditionnellement, on gère ces erreurs tant bien que mal en envoyant un message de détresse à l’écran et en interrompant le script avec exit ou die. Il est possible de faire mieux en intégrant ces erreurs applicatives dans le système de gestion des erreurs de PHP avec la fonction trigger_error() qui prend deux paramètres : 1. le message d’erreur ; 2. le niveau d’erreur parmi E_USER_NOTICE (valeur par défaut), E_USER_WARNING et E_USER_ERROR. L’utilisation du troisième niveau (E_USER_ERROR) provoque de plus l’interrup- tion du script si l’erreur est rencontrée, ce qui revient donc (mais de manière plus propre) à un exit. L’avantage de cette solution est que les erreurs sont alors traitées comme des erreurs de syntaxe PHP, ce qui permet de les gérer beaucoup plus souple- ment en les faisant entrer dans le cadre de la gestion d’erreurs décrite précédemment. Concrètement, on peut, en jouant seulement sur le paramétrage, faire varier le comportement de l’ensemble des scripts en demandant à ce que l’affichage ne se fasse plus à l’écran mais dans un fichier de journalisation, y compris pour les erreurs engendrées par l’application (et gérées explicitement par le programmeur). La fonction ci-dessous montre quelques exemples d’utilisation de trigger_error() pour une fonction de gestion des fichiers transférés d’un client au serveur (voir page 91). function CopieFichierTransmis ($fichier , $destination ) { // On récupère le code d’erreur éventuel $code_erreur = $fichier [ ’ error ’ ]; if ($code_erreur == UPLOAD_ERR_OK) { if (!copy($fichier [ ’tmp_name ’] , $destination)) trigger_error("Impossible de copier le fichier !" , E_USER_ERROR) ; 5.2 Gestion des erreurs 227 } else { // Une erreur quelque part ? switch ($code_erreur) { case UPLOAD_ERR_INI_SIZE : trigger_error("Le fichier dépasse la taille max. autorisée par PHP" , E_USER_ERROR) ; break ; case UPLOAD_ERR_FORM_SIZE : trigger_error("Le fichier dépasse la taille max. ". "autorisée par le formulaire" , E_USER_ERROR) ; break ; case UPLOAD_ERR_PARTIAL : trigger_error("Le fichier a été transféré partiellement ", E_USER_ERROR) ; break ; } } } 5.2.3 Les exceptions PHP Les exceptions existent depuis PHP 5, et sont étroitement associées aux améliora- tions de la programmation orientée-objet. Le principe des exceptions a été présenté page 124. Rappelons-le brièvement ici, dans une optique de mise en place d’une gestion des erreurs 4 . Les exceptions sont des objets, instanciés par le programmeur, et placés dans un espace réservé de PHP grâce à l’instruction throw. Le fait de disposer d’un espace spécifique pour stocker les exceptions évite de les gérer dans la programmation en réservant des variables pour transmettre les codes et les messages d’erreur d’une fonction à l’autre. On peut, à tout moment, « attraper » les exceptions « lancées » précédemment par un script avec l’instruction catch. Comme les erreurs, les exceptions fournissent quatre informations: un message, un code d’erreur (optionnel), le fichier et le numéro de la ligne de l’instruction PHP qui a déclenché l’erreur. Ces informations sont respectivement obtenues par les méthodes getMessage(), getCode(), getFile() et getLine() de la classe prédéfinie Exception. 4. La discussion qui suit suppose acquises les bases de la programmation objet, telles qu’elles sont présentées dans le chapitre 3. . numéro de la ligne de l’instruction PHP qui a déclenché l’erreur. Ces informations sont respectivement obtenues par les méthodes getMessage(), getCode(), getFile() et getLine() de la classe prédéfinie. L’avantage de cette solution est que les erreurs sont alors traitées comme des erreurs de syntaxe PHP, ce qui permet de les gérer beaucoup plus souple- ment en les faisant entrer dans le cadre de la. optique de mise en place d’une gestion des erreurs 4 . Les exceptions sont des objets, instanciés par le programmeur, et placés dans un espace réservé de PHP grâce à l’instruction throw. Le fait de