Manuel Pear

Introduction à l'utilisation de PEAR_ErrorStack pour une gestion avancée des erreurs

Introduction à l'utilisation de PEAR_ErrorStack pour une gestion avancée des erreurs  --  Utiliser PEAR_ErrorStack pour effectuer une gestion des erreurs simples ou avancées.

Synopsis

Introduction a l'utilisation de PEAR_ErrorStack

Introduction

Cette classe est disponible en tant que partie du package PEAR. Fonctionnalités incluse :

  • Complètement documentée et fournie avec des tests unitaires complets

  • étonnamment rapide - noie complètement PEAR_Error

  • Erreurs spécifiques au package

  • Niveaux d'erreurs (notice/avertissement/erreur/exception)

  • Les données contextuelles de l'erreur sont sauvées indépendamment du message d'erreur

  • Erreurs en cascade - les erreurs parentes peuvent être spécifiées.

  • Génération dynamique des messages d'erreur, autorisant la génération de messages d'erreur multiples et distincts à partir du même objet erreur

  • Des fonctions de rappel (callbacks) sophistiquées sont disponibles, permettant la génération des messages d'erreur, de contextes d'erreurs, voir : Error Context Display, Custom Error Message Generation, et controlling error generation

PEAR_ErrorStack implémente une levée et une gestion d'erreurs utilisant le concept de pile. Ceci a d'énormes avantages comparer à l'implémentation PEAR_Error. PEAR_Error centralise toutes les créations et gestions d'erreurs dans le constructeur de l'objet PEAR_Error. Une fois qu'un objet a été créé, tous les traitements doivent être terminés, soit en contrôlant la valeur retournée par la méthode, soit en utilisant une fonction de rappel locale. De plus, il est quasiment impossible de déterminer la source d'une erreur ainsi que les goulots d'étranglement des méthodes lentes de la classe de base PEAR accompagnant chaque création d'erreur.

  1. <?php
  2. // utilisation classique de PEAR_Error
  3. require_once 'PEAR.php';  
  4. class myobj  
  5. { 
  6.    // il n'est pas possible de savoir d'où viennent les erreurs
  7.    function errorCallback($err) 
  8.    { 
  9.       $this->display($err->getMessage()); 
  10.       $this->log($err->getMessage()); 
  11.    } 
  12.  
  13.    function log($msg) 
  14.    { 
  15.       error_log($msg, 3, 'somefile.log') 
  16.    } 
  17.  
  18.    function display($msg) 
  19.    { 
  20.       echo $msg . '<br />'; 
  21.    }  
  22. }  
  23.  
  24. $myobj = new myobj;  
  25.  
  26. // utilisation d'une fonction de Callback
  27. PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array(&$myobj, 'errorCallback'));  
  28.  
  29. $ret = SomePackage::doSomething();  
  30. if (PEAR::isError($ret)) { 
  31.    // gestion de l'erreur - cette erreur est aussi affichée et loguée
  32. }  
  33. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);  
  34.  
  35. $ret = SomePackage::doSomething();  
  36. if (PEAR::isError($ret)) { 
  37.    // gère l'erreur - cette erreur n'est pas affichée ni loggée
  38. }  
  39. PEAR::popErrorHandling();  
  40.  
  41. ?> 

La classe PEAR_ErrorStack ayant hérité du savoir du package Log, peut facilement différencier et éventuellement reconditionner des erreurs sans grandes difficultés.

  1. <?php
  2. // Gestion d'erreurs PEAR_ErrorStack
  3. require_once 'PEAR/ErrorStack.php';  
  4. require_once 'Log.php';  
  5. define('MON_PACKAGE_ERROR_DBERROR', 1);  
  6. class myobj  
  7. { 
  8.    var $_stack; 
  9.    function myobj() 
  10.    { 
  11.       $this->_stack = &PEAR_ErrorStack::singleton('monpackage'); 
  12.    } 
  13.  
  14.    function errorCallback($err) 
  15.    { 
  16.       switch($err['package']){ 
  17.          case 'MyPackage': 
  18.             // Dit à la pile d'erreurs de seulement logger l'erreur
  19.             // Elle ne sera pas poussée sur la pile
  20.             return PEAR_ERRORSTACK_LOG; 
  21.             break; 
  22.          case 'InternalDbPackage': 
  23.             // rempaquète ces erreurs comme des erreurs mypackag
  24.             // à l'attention des utilisateurs finaux
  25.             $this->_stack->push(MON_PACKAGE_ERROR_DBERROR, 'error', 
  26.                array('dbmessage' => $err['message'], 
  27.                   'dbcode' => $err['code'], 
  28.                   'Nous avons des problème de connection,' . 
  29.                   'veuillez réessayer plus tard'), 
  30.                '', $err); // incluse l'erreur et rempaquète
  31.                      // Demande à la pile DB erreur interne d'ignorer
  32.                      // cette erreur
  33.                      // comme si elle n'etait jamais arrivée
  34.             return PEAR_ERRORSTACK_IGNORE; 
  35.             break; 
  36.       } // switch
  37.    }  
  38. }  
  39.  
  40. $myobj = &new myobj;  
  41. // Sépare les piles d'erreurs "my package" et DB interne
  42. $dbstack = &PEAR_ErrorStack::singleton('InternalDbPackage');  
  43. $mystack = &PEAR_ErrorStack::singleton('monpackage');  
  44. // Prépare un fichier log utilisant PEAR::Log
  45. $log = &Log::Factory('file', 'somefile.log', 'monpackage error log');  
  46. $mystack->setLogger($log);  
  47. // Prépare un log par defaut utilisé pour toutes les piles d'erreurs
  48. PEAR_ErrorStack::setDefaultLogger($log);  
  49.  
  50. // toutes les erreurs retournées par "my package" sont loggées
  51. $ret = SomePackage::doSomething();  
  52.  
  53. // Notez qu'il n'est pas nécessaire de contrôler les conditions de l'erreur
  54. // sur $ret - les erreurs sont complètement separées du code.
  55. if ($dbstack->hasErrors()) { 
  56.    var_dump($dbstack->getErrors();  
  57. }  
  58.  
  59. // Définission d'une fonction de callback pour toutes les erreurs
  60. PEAR_ErrorStack::setDefaultCallback(array(&$myobj, 'errorCallback'));  
  61.  
  62. // toutes les erreurs DB sont rempaquetées en erreurs "my package"
  63. // de façon transparente
  64. $ret = SomePackage::doSomething();  
  65.  
  66.  
  67. ?> 

Pourquoi écrire une nouvelle routine de gestion d'erreurs alors que PEAR_Error existe déjà? Il y a un nombre important de problèmes avec PEAR_Error. Bien qu'un message d'erreur soit présent dans une classe d'erreur, ce message d'erreur ne peut pas facilement être traité une fois qu'il a été placé dans PEAR_Error. Il n'existe pas non plus de service standard pour stocker des données associées aux erreurs dans la classe. En plus de ces problèmes de messages d'erreurs, il n'est pas possible de déterminer automatiquement de quel paquet provient l'objet PEAR_Error ou même la sévérité d'une erreur. En effet, les erreurs fatales ressemblent exactement aux erreurs non fatales.

Le plus gros problème de l'objet PEAR_Error est sa conception orientée « mono erreur ». Chaque objet PEAR_Error est juste un objet PEAR_Error. Il n'y a pas de différenciation entre les sévérités des erreurs ou leurs origines. Le seul moyen de déterminer sa sévérité est d'utiliser PEAR_ERROR_TRIGGER et les constantes E_USER_NOTICE/E_USER_WARNING/E_USER_ERROR de la fonction trigger_error de PHP. Mais utiliser ces fonctionnalités ne justifie pas les 900 lignes de code simplement parce que trigger_error() fait partie de PHP lui-même.

Maintenant pour commencer à utiliser vos objets d'erreur nouvellement créés, changez tous vos appels PEAR::raiseError() ou PEAR::throwError() de cette forme ...

  1. <?php
  2. require_once 'PEAR.php';  
  3. // ancienne technique :
  4. $error_specific_info = 'bad';  
  5. $e = PEAR::raiseError("error message - very " . $error_specific_info . 
  6.    " way to do things", MON_PACKAGE_ERROR_FOO);  
  7. // une autre ancienne technique :
  8. $e = PEAR::throwError("error message - very " . $error_specific_info . 
  9.    " way to do things", MON_PACKAGE_ERROR_FOO);  
  10.  
  11. ?> 

... à quelquechose comme ceci :

  1. <?php
  2. require_once 'PEAR/ErrorStack.php';  
  3. // nouvelle méthode
  4. // version 1: accès a l'instance de pile
  5. $stack = &PEAR_ErrorStack::singleton('monpackage');  
  6. $stack->push(MON_PACKAGE_ERROR_DBERROR, 'error', 
  7.    array('query' => $query, 'dsn' => $dsn), 
  8.    'Critical Database Error: Contact Administrator immediately');  
  9. // version 2: accès statique singleton : considérablement plus lent
  10. PEAR_ErrorStack::staticPush('monpackage', MON_PACKAGE_ERROR_DBERROR, 'error', 
  11.    array('query' => $query, 'dsn' => $dsn), 
  12.    'Critical Database Error: Contact Administrator immediately');  
  13.  
  14. ?> 

Dans le cadre d'une utilisation basique, voilà tout ce qui est nécessaire de faire pour utiliser le paquet PEAR_ErrorStack à la place de PEAR_Error.

Fonctionnalités avancées

Affichage du contexte d'une erreur

Dans certains cas, vous pouvez souhaiter personnaliser la génération des erreurs. Par exemple, pour beaucoup d'exceptions, il est utile d'inclure comme informations contextuelles : le nom de fichier, le numéro de ligne et le nom de la classe/fonction afin de tracer l'erreur. Une option par défaut est disponible et sera suffisante dans la majorité des cas, c'est : PEAR_ErrorStack::getFileLine().

Toutes les erreurs de paquet n'apparaissent pas dans le fichier source PHP lui même. Par exemple, les erreurs de compilation d'un moteur de templates apparaissent dans le fichier source du modèle, les erreurs de base de données peuvent se produire dans le texte d'une requête, ou d'une manière interne au serveur de base de données, les erreurs propres aux paquets Internet peuvent apparaître sur un autre serveur. Toutes ces informations peuvent être incluses dans un message d'erreur en utilisant une fonction de rappel capturant le contexte.

  1. <?php
  2. require_once 'PEAR/ErrorStack.php';  
  3. class DatabaseClass  
  4. { 
  5.    var $_dbError; 
  6.    var $_dbErrorMsg; 
  7.    var $_dbQuery; 
  8.    var $_dbPos; 
  9.    /**
  10.     * Context grabber for the Database package
  11.     * @param integer Error Code
  12.     * @param array Error parameters passed into {@link PEAR_ErrorStack::push()}
  13.     * @param array Output of debug_backtrace() (not used in this callback)
  14.     */ 
  15.    function getErrorContext($code, $params, $backtrace) 
  16.    { 
  17.       $context = array( 
  18.          'errorcode' => $this->_dbError, 
  19.          'errormsg' => $this->_dbErrorMsg, 
  20.          'query' => $this->_dbQuery, 
  21.          'pos' => $this->_dbPos, 
  22.       ); 
  23.       return $context; 
  24.    }  
  25. }  
  26. $db = new DatabaseClass;  
  27. PEAR_ErrorStack::staticSetContextCallback('Database', array(&$db, 'getErrorContext'));  
  28.  
  29. ?> 

L'information contextuelle est formatée pour être facilement traitée par une application externe. Si vous souhaitez que les informations contextuelles soient incluses dans le message d'erreur, la fonction de rappel de message d'erreur devrait être utilisée pour ajouter cette information d'une manière humainement lisible au message d'erreur, tel que décrit dans la section suivante.

Génération de messages d'erreur personnalisés

Il y a trois méthodes de PEAR_ErrorStack prévues pour générer des messages d'erreur de manière efficace. Pour les utiliser, vous devez faire une des deux choses suivantes :

  • Appeler PEAR_ErrorStack::setErrorMessageTemplate(), et configurer un tableau en mettant en correspondance les codes d'erreurs et les modèles correspondants, comme ceci :

    1. <?php
    2. define('ERREUR_UN', 1);  
    3. define('ERREUR_DEUX', 2);  
    4. define('ERREUR_TROIS', 3);  
    5. define('ERREUR_QUATRE', 4);  
    6. require_once 'PEAR/ErrorStack.php';  
    7. $stack = &PEAR_ErrorStack::singleton('monpackage');  
    8. $messages = array( 
    9.    ERREUR_UN => 'The gronk number %num% dropped a %thing%', 
    10.    ERREUR_DEUX => 'The %list% items were missing', 
    11.    ERREUR_TROIS => 'I like chocolate, how about %you%?', 
    12.    ERREUR_QUATRE => 'and a %partridge% in a pear %tree%',  
    13. );  
    14. $stack->setErrorMessageTemplate($messages);  
    15.  
    16. ?> 

    La substitution est faite en utilisant la fonction PHP str_replace, et est très simple. Basiquement, si un nom de variable est entouré de signes pourcentage (%), il sera remplacé par la valeur passée dans le tableau associatif. Si un tableau est passé en paramètre à la méthode,

    1. <?php
    2. array('nomvar' => 'valeur'  
    3. ?> 
    , toutes les occurrences de %nomvar% seront remplacées par leur valeur.

    De plus, si les valeurs passées sont des objets, les méthodes de substitution chercheront dans l'objet une méthode nommée "__toString()()" et si elle est trouvée, elle sera utilisée pour convertir l'objet en chaîne. Si un tableau de chaînes est passé, elles seront jointes par des virgules.

    1. <?php
    2. array('nomvar' => array('premier', 'second', 'tiers'));  
    3. // Deviendra : 'premier, second, tiers'
    4.  
    5. ?> 
  • Appel de PEAR_ErrorStack::setMessageCallback(), et paramétrer une fonction ou méthode générant un message d'erreur personnalisé. Ceci est probablement la meilleur option dans la majorité des situations complexes, dans la mesure où cela autorise les utilisateurs à remplacer ou même étendre la fonction de callback existante en utilisant PEAR_ErrorStack::getMessageCallback(). Par exemple:

    1. <?php
    2. require_once 'PEAR/ErrorStack.php';  
    3. class foo  
    4. { 
    5.    var $_oldcallback; 
    6.    function callback(&$stack, $err) 
    7.    { 
    8.       $message = call_user_func_array($this->_oldcallback, array(&$stack, $err)); 
    9.       $message .= "File " . $err['context']['file']; 
    10.       return $message; 
    11.    }  
    12. }  
    13. $a = new foo;  
    14. $stack = &PEAR_ErrorStack::singleton('otherpackage');  
    15. $a->_oldcallback = $stack->getMessageCallback('otherpackage');  
    16. $stack->setMessageCallback(array(&$a, 'callback'));  
    17.  
    18. ?> 
  • étendre PEAR_ErrorStack avec votre propre classe et remplacer PEAR_ErrorStack::getErrorMessageTemplate() ou PEAR_ErrorStack::getErrorMessage(). Afin de garantir que cette classe sera utilisée par d'autres pacquets / applications, utilisez ce code juste après la déclaration de la classe :

    1. <?php
    2. PEAR_ErrorStack::singleton('monpackage', false, null, 'MyPEAR_ErrorStack');  
    3.  
    4. ?> 

Contrôler la génération des erreurs

Il existe plusieurs scénarios dans lesquels le contrôle d'une fine granularité sur la levée d'une erreur est absolument nécessaire. Une fonction générique de callback de gestion des erreurs implique que chaque erreur levée sera gérée dans la même fonction de rappel. Même si PEAR_ErrorStack est conçu pour opérer avec des fonctions de rappel indépendantes pour chaque paquet, une gestion générique est possible à travers la méthode PEAR_ErrorStack::staticPushCallback(). Ce n'est pas différent du mode PEAR_ERROR_CALLBACK de gestion d'erreurs PEAR_Error.

La puissance réelle de PEAR_ErrorStack vient du mécanisme de callback lui-même. Le mécanisme de callback PEAR_Error n'a pas vraiment d'effet sur le message d'erreur - tous les traitements d'erreurs doivent être effectuées dans la méthode ou la fonction de rappel elle-même. Le mécanisme de rappel PEAR_ErrorStack peut influencer l'erreur à travers l'utilisation de trois constantes :

PEAR_ERRORSTACK_IGNORE informe la pile qu'elle doit ignorer l'erreur, comme si elle n'était jamais arrivée. L'erreur ne sera ni loguée, ni mise sur la pile. Elle pourra cependant être retournée par PEAR_ErrorStack::push().

PEAR_ERRORSTACK_PUSH informe la pile qu'elle doit empiler l'erreur sur la pile des erreurs, mais ne pas la loguer.

PEAR_ERRORSTACK_LOG informe la pile qu'elle ne doit pas empiler l'erreur mais seulement la loguer.

  1. <?php
  2. define('ERROR_CODE_ONE',1);  
  3. define('ERROR_CODE_TWO',2);  
  4. define('ERROR_CODE_THREE',3);  
  5. require_once 'PEAR/ErrorStack.php';  
  6. require_once 'Log.php';  
  7. function somecallback($err)  
  8. { 
  9.    switch($err['code']){ 
  10.       case ERROR_CODE_ONE: 
  11.             return PEAR_ERRORSTACK_IGNORE; 
  12.             break; 
  13.       case ERROR_CODE_TWO: 
  14.             return PEAR_ERRORSTACK_PUSH; 
  15.             break; 
  16.       case ERROR_CODE_THREE: 
  17.             return PEAR_ERRORSTACK_LOG; 
  18.             break; 
  19.    } // switch
  20. }  
  21. $log = &Log::factory('display');  
  22. $stack = &PEAR_ErrorStack::singleton('monpackage');  
  23. $stack->setLogger($log);  
  24. $stack->pushCallback('somecallback');  
  25. $stack->push(ERROR_CODE_ONE);  
  26. $stack->push(ERROR_CODE_TWO);  
  27. $stack->push(ERROR_CODE_THREE);  
  28. var_dump(PEAR_ErrorStack::staticGetErrors());  
  29.  
  30. // simule PEAR_ERROR_CALLBACK avec des callbacks spécifiques pour
  31. // monpackage.
  32. // Chaque autre paquet loguera simplement l'erreur,
  33. // seul les erreurs mypackage sont poussées sur la pile de manière
  34. // conditionnelle
  35. class myclass { 
  36.    function acallback($err) 
  37.    { 
  38.       return PEAR_ERRORSTACK_LOG; 
  39.    }  
  40. }  
  41. $stack2 = PEAR_ErrorStack::singleton('anotherpackage');  
  42. $stack3 = &PEAR_ErrorStack::singleton('thirdpackage');  
  43. PEAR_ErrorStack::setDefaultCallback(array('myclass', 'acallback'));  
  44.  
  45. ?> 

Reconditionner les erreurs d'un package à un autre

L'usage le plus évident d'une fonction de callback d'erreurs implique un scénario commun à beaucoup d'applications niveau utilisateur qui emploient des paquets systèmes. Si vous écrivez un Système de gestion de contenu (CMS) en utilisant le paquet PEAR DB, c'est généralement une mauvaise idée d'afficher des erreurs du niveau base de données quand un utilisateur clique sur un lien pour ajouter un message à un forum. PEAR_ErrorStack peut être utilisé pour reconditionner cette erreur comme une erreur propre à votre paquet.

  1. <?php
  2. define('MON_PACKAGE_ERROR_DBDOWN',1);  
  3. require_once 'PEAR/ErrorStack.php';  
  4. function repackage($err)  
  5. { 
  6.    if ($err['package'] == 'DB') { 
  7.       $mystack = &PEAR_ErrorStack::singleton('monpackage'); 
  8.       $mystack->push(MON_PACKAGE_ERROR_DBDOWN, 'error', array('olderror' => $err)); 
  9.       // ignore l'erreur DB ,
  10.       // mais la sauvegarde dans l'erreur monpackage afin d'être loguée
  11.       return PEAR_ERRORSTACK_IGNORE; 
  12.    }  
  13. }  
  14.  
  15. ?> 

émulation de l'opérateur @

Une des forces de PEAR_Error difficile à utiliser est la méthode PEAR::expectError(). Il est possible de rendre silencieuses les erreurs régulières de PHP en utilisant l'opérateur @ de la façon suivante :

  1. <?php
  2. @file_get_contents();  
  3.  
  4. ?> 

émuler ce comportement à l'aide de PEAR_ErrorStack est simple.

  1. <?php
  2. define('ERROR_CODE_SOMETHING', 1);  
  3. require_once 'PEAR/ErrorStack.php';  
  4. function silence($err)  
  5. { 
  6.    // ignore toutes les erreurs
  7.    return PEAR_ERRORSTACK_IGNORE;  
  8. }  
  9. $stack = &PEAR_ErrorStack::singleton('monpackage');  
  10. $stack->pushCallback('silence');  
  11. $stack->push(ERROR_CODE_SOMETHING);  
  12.  
  13. ?> 

PEAR_ErrorStack peut passer un cran au dessus et seulement loguer les erreurs ou seulement les placer sur la pile d'erreurs en utilisant les deux autres constantes. Finalement, des erreurs particulières peuvent être levées et toutes les autres ignorées.

  1. <?php
  2. define('SOMEPACKAGE_ERROR_THING', 1);  
  3. require_once 'PEAR/ErrorStack.php';  
  4. function silenceSome($err)  
  5. { 
  6.    if ($err['package'] != 'somepackage') { 
  7.       // ignore toutes les erreurs d'autres packages
  8.       return PEAR_ERROR_IGNORE; 
  9.    } 
  10.    if ($err['code'] != SOMEPACKAGE_ERROR_THING) { 
  11.       // ignore tous les autres codes d'erreurs
  12.       return PEAR_ERRORSTACK_IGNORE; 
  13.    }  
  14. }  
  15. $stack = &PEAR_ErrorStack::singleton('monpackage');  
  16. $stack->pushCallback('silenceSome');  
  17. $stack->push(ERROR_CODE_SOMETHING);  
  18.  
  19. ?> 

Compatibilité descendante avec PEAR_Error, et ascendante avec les exceptions PHP5

PEAR_ErrorStack peut être aussi programmée pour générer automatiquement un objet PEAR_Error en utilisant PEAR::raiseError(), en passant simplement le paramètre de compatibilité Pear_Error à TRUE de la manière suivante :

  1. <?php
  2. require_once 'PEAR/ErrorStack.php';  
  3. $stack = &PEAR_ErrorStack::singleton('monpackage', false, null, 'PEAR_ErrorStack', true);  
  4.  
  5. ?> 

PEAR_ErrorStack peut se coordonner avec la nouvelle classe PEAR_Exception pour la convertion en exception avec ce code : Vous pouvez définir le nom de l'exception qui sera retournée en utilisant le code suivant :

  1. <?php
  2. require_once 'PEAR/ErrorStack.php';  
  3. require_once 'PEAR/Exception.php';  
  4. $stack = &PEAR_ErrorStack::singleton('mypackage');  
  5. $stack->push(1, 'erreur de test');  
  6. throw new PEAR_Exception('ne fonctionne pas', $stack->pop());  
  7.  
  8. ?> 

Remonter Remonter
L'éditeur javascript - CSS - Gentoo - Tutoriaux PHP - Tutoriels PHP - Bretagne - php - Moto - Kit graphique