Tout d'abord, je vous félicite d'avoir étudié les méthodes d'erreur standard de PHP. Malheureusement, error_log
a quelques limites comme vous l'avez découvert.
C'est une longue réponse, lisez la suite pour en savoir plus :
- Erreurs
- Enregistrer directement l'erreur par rapport à
trigger_error
y set_error_handler
- Quand les bonnes erreurs tournent mal - Erreurs fatales.
- Exceptions
- Code
- Configuration
- Utilisation
TL;DR Utilisez trigger_error
pour signaler les erreurs et set_error_handler
pour les enregistrer.
1. Erreurs
Lorsque les choses ne se passent pas comme prévu dans votre programme, vous voudrez souvent déclencher une erreur afin que quelqu'un ou quelque chose en soit informé. Une erreur correspond à une situation où le programme peut continuer, mais où quelque chose de notable, éventuellement nuisible ou erroné, s'est produit. À ce stade, de nombreuses personnes souhaitent enregistrer immédiatement l'erreur avec le logiciel d'enregistrement de leur choix. Je pense que c'est exactement la mauvaise chose à faire. Je recommande d'utiliser trigger_error
pour soulever l'erreur afin qu'elle puisse être gérée par un callback défini par set_error_handler
. Comparons ces options :
Enregistrement direct de l'erreur
Vous avez donc choisi votre forfait d'exploitation forestière. Vous êtes maintenant prêt à diffuser les appels à votre logger partout où une erreur se produit dans votre code. Examinons un appel unique que vous pourriez faire (j'utiliserai un logger similaire à celui de la réponse de Jack) :
Logger::getLogger('standard')->error('Ouch, this hurts');
Que faut-il mettre en place pour exécuter ce code ?
**Class**: Logger
**Method**: getLogger
**Return**: Object with method 'error'
Ce sont les dépendances nécessaires pour utiliser ce code. Tous ceux qui veulent réutiliser ce code devront fournir ces dépendances. Cela signifie qu'une configuration PHP standard ne sera plus suffisante pour réutiliser votre code. Dans le meilleur des cas, en utilisant l'injection de dépendances, vous avez toujours besoin d'un objet logger à passer dans tout votre code qui peut émettre une erreur.
De plus, en plus de ce dont le code est responsable, il est également responsable de l'enregistrement de l'erreur. Cela va à l'encontre de la Principe de responsabilité unique .
On peut voir que l'enregistrement direct de l'erreur est mauvais .
trigger_error à la rescousse
PHP possède une fonction appelée trigger_error
qui peut être utilisé pour lever une erreur comme le font les fonctions standard. Les niveaux d'erreur que vous utilisez avec cette fonction sont définis dans le champ constantes de niveau d'erreur . En tant qu'utilisateur, vous devez utiliser l'une des erreurs d'utilisateur : E_USER_ERROR
, E_USER_WARNING
ou la valeur par défaut E_USER_NOTICE
(les autres niveaux d'erreur sont réservés aux fonctions standard, etc.) L'utilisation d'une fonction PHP standard pour générer l'erreur permet de réutiliser le code avec n'importe quelle installation PHP standard ! Notre code n'est plus responsable de l'enregistrement de l'erreur (il s'assure seulement qu'elle est levée).
Utilisation de trigger_error
nous n'effectuons que la moitié du processus d'enregistrement des erreurs (soulever l'erreur) et nous réservons la responsabilité de la réponse à l'erreur au gestionnaire d'erreurs, qui sera abordé plus loin.
Gestionnaire d'erreurs
Nous avons défini un gestionnaire d'erreurs personnalisé avec l'option set_error_handler
(voir la configuration du code). Ce gestionnaire d'erreurs personnalisé remplace le gestionnaire d'erreurs standard de PHP qui enregistre normalement les messages dans le journal d'erreurs du serveur Web en fonction des paramètres de configuration de PHP. Nous pouvons toujours utiliser ce gestionnaire d'erreur standard en retournant la fonction false
dans notre gestionnaire d'erreurs personnalisé.
Le gestionnaire d'erreurs personnalisé n'a qu'une seule responsabilité : répondre à l'erreur (y compris la journalisation que vous souhaitez effectuer). Dans le gestionnaire d'erreur personnalisé, vous avez un accès complet au système et vous pouvez exécuter n'importe quel type de journalisation que vous voulez. Pratiquement n'importe quel logger qui utilise le modèle de conception Observer fera l'affaire (je ne vais pas m'étendre sur ce sujet car je pense qu'il est d'une importance secondaire). Cela devrait vous permettre d'accrocher de nouveaux observateurs de logs pour envoyer la sortie là où vous en avez besoin.
Vous avez le contrôle total pour faire ce que vous voulez avec les erreurs dans une seule partie maintenable de votre code. L'enregistrement des erreurs peut désormais être modifié rapidement et facilement d'un projet à l'autre ou d'une page à l'autre au sein d'un même projet. Il est intéressant de noter que même @
Les erreurs supprimées sont transmises au gestionnaire d'erreurs personnalisé avec un message errno
de 0 qui, si le error_reporting
masque est respecté ne doit pas être signalé.
Quand les bonnes erreurs tournent mal - les erreurs fatales
Il n'est pas possible de poursuivre à partir de certaines erreurs. Les niveaux d'erreur suivants ne peuvent pas être traités par un gestionnaire d'erreurs personnalisé : E_ERROR
, E_PARSE
, E_CORE_ERROR
, E_CORE_WARNING
, E_COMPILE_ERROR
, E_COMPILE_WARNING
. Lorsque ce type d'erreur est déclenché par un appel de fonction standard, le gestionnaire d'erreur personnalisé est ignoré et le système s'arrête. Ceci peut être généré par :
call_this_function_that_obviously_does_not_exist_or_was_misspelt();
Il s'agit d'un sérieux erreur ! Il est impossible de s'en remettre, et le système est sur le point de s'arrêter. Notre seul choix est d'avoir register_shutdown_function
de faire face au shutdown. Cependant, cette fonction est exécutée chaque fois qu'un script se termine (avec succès, ainsi qu'en cas d'échec). En utilisant cette fonction et error_get_last
quelques informations de base peuvent être enregistrées (le système est presque arrêté à ce stade) lorsque la dernière erreur était une erreur fatale. Il peut également être utile d'envoyer le code d'état correct et d'afficher une page de type Internal Server Error de votre choix.
2. Exceptions
Les exceptions peuvent être traitées de manière très similaire aux erreurs de base. Au lieu de trigger_error
une exception sera levée par votre code (manuellement avec throw new Exception
ou à partir d'un appel de fonction standard). Utilisez set_exception_handler
pour définir le callback que vous voulez utiliser pour gérer l'exception.
SPL
La bibliothèque standard de PHP (SPL) fournit exceptions . C'est ma façon préférée de soulever des exceptions parce que comme trigger_error
ils sont une partie standard de PHP qui n'introduit pas de dépendances supplémentaires dans votre code.
Que faire d'eux ?
Lorsqu'une exception est levée, trois choix s'offrent à vous :
- Attrapez-le et réparez-le (le code continue ensuite comme si de rien n'était).
- Attrapez-le, ajoutez-y des informations utiles et rejetez-le.
- Laissez-le s'élever à un niveau supérieur.
Ces choix sont faits à chaque niveau de la pile. Finalement, une fois que la pile atteint le niveau le plus élevé, le callback que vous avez défini avec la commande set_exception_handler
sera exécuté. C'est ici qu'il faut placer le code de journalisation (pour les mêmes raisons que la gestion des erreurs) plutôt que de l'éparpiller dans tout le système. catch
dans votre code.
3. Code
Configuration
Gestionnaire d'erreurs
function errorHandler($errno , $errstr, $errfile, $errline, $errcontext)
{
// Perform your error handling here, respecting error_reporting() and
// $errno. This is where you can log the errors. The choice of logger
// that you use is based on your preference. So long as it implements
// the observer pattern you will be able to easily add logging for any
// type of output you desire.
}
$previousErrorHandler = set_error_handler('errorHandler');
Gestionnaire d'exceptions
function exceptionHandler($e)
{
// Perform your exception handling here.
}
$previousExceptionHandler = set_exception_handler('exceptionHandler');
Fonction d'arrêt
function shutdownFunction()
{
$err = error_get_last();
if (!isset($err))
{
return;
}
$handledErrorTypes = array(
E_USER_ERROR => 'USER ERROR',
E_ERROR => 'ERROR',
E_PARSE => 'PARSE',
E_CORE_ERROR => 'CORE_ERROR',
E_CORE_WARNING => 'CORE_WARNING',
E_COMPILE_ERROR => 'COMPILE_ERROR',
E_COMPILE_WARNING => 'COMPILE_WARNING');
// If our last error wasn't fatal then this must be a normal shutdown.
if (!isset($handledErrorTypes[$err['type']]))
{
return;
}
if (!headers_sent())
{
header('HTTP/1.1 500 Internal Server Error');
}
// Perform simple logging here.
}
register_shutdown_function('shutdownFunction');
Utilisation
Erreurs
// Notices.
trigger_error('Disk space is below 20%.', E_USER_NOTICE);
trigger_error('Disk space is below 20%.'); // Defaults to E_USER_NOTICE
// Warnings.
fopen('BAD_ARGS'); // E_WARNING fopen() expects at least 2 parameters, 1 given
trigger_error('Warning, this mode could be dangerous', E_USER_WARNING);
// Fatal Errors.
// This function has not been defined and so a fatal error is generated that
// does not reach the custom error handler.
this_function_has_not_been_defined();
// Execution does not reach this point.
// The following will be received by the custom error handler but is fatal.
trigger_error('Error in the code, cannot continue.', E_USER_ERROR);
// Execution does not reach this point.
Exceptions
Chacun des trois choix précédents est énuméré ici de manière générique, le fixer, le compléter et le laisser bouillonner.
1 Fixable :
try
{
$value = code_that_can_generate_exception();
}
catch (Exception $e)
{
// We decide to emit a notice here (a warning could also be used).
trigger_error('We had to use the default value instead of ' .
'code_that_can_generate_exception\'s', E_USER_NOTICE);
// Fix the exception.
$value = DEFAULT_VALUE;
}
// Code continues executing happily here.
2 Appendice :
Observez ci-dessous comment le code_that_can_generate_exception()
n'est pas au courant $context
. Le bloc catch à ce niveau dispose de plus d'informations qu'il peut ajouter à l'exception si cela est utile en la rejetant à nouveau.
try
{
$context = 'foo';
$value = code_that_can_generate_exception();
}
catch (Exception $e)
{
// Raise another exception, with extra information and the existing
// exception set as the previous exception.
throw new Exception('Context: ' . $context, 0, $e);
}
3 Laissez-le bouillonner :
// Don't catch it.
0 votes
Avez-vous envisagé d'utiliser l'enregistrement d'erreurs intégré, en utilisant la commande
log_errors
et, facultativement, la directive de configurationtrigger_error
pour les envoyer dans les journaux ? Cela les envoie dans les journaux d'erreurs de votre serveur web. Et vous n'avez pas à le faire manuellement.2 votes
Eh bien, selon le cas, j'aimerais utiliser différentes méthodes de consignation, courriel, base de données, etc.