Bonnes pratiques de gestion des erreurs PHP pour un code maintenable
Vous venez de déployer une nouvelle fonctionnalité, et soudain votre application PHP lance une erreur fatale. Les utilisateurs voient une page blanche, et vos logs sont vides. Ça vous dit quelque chose ? Une mauvaise gestion des erreurs est l'une des raisons les plus courantes pour lesquelles le code PHP devient difficile à maintenir. Dans cet article, nous allons passer en revue les bonnes pratiques de gestion des erreurs PHP qui rendront votre code plus robuste, plus facile à déboguer et plus simple à maintenir.
Pourquoi la gestion des erreurs PHP est importante
PHP est indulgent par défaut : il continue souvent l'exécution après un avertissement, et les erreurs peuvent être silencieusement ignorées. Cette flexibilité est une arme à double tranchant. Sans une gestion appropriée, les erreurs peuvent :
- Exposer des informations sensibles aux utilisateurs (par exemple, les identifiants de base de données dans les traces de pile).
- Corrompre les données ou laisser l'application dans un état incohérent.
- Rendre le débogage cauchemardesque car les erreurs sont dispersées ou supprimées.
Une bonne gestion des erreurs garantit que lorsque quelque chose ne va pas, vous le savez, vous pouvez le corriger rapidement, et vos utilisateurs ont une expérience agréable.
1. Définir des niveaux de rapport d'erreurs appropriés
La première étape consiste à configurer correctement le rapport d'erreurs de PHP pour votre environnement. En développement, vous voulez voir toutes les erreurs ; en production, vous voulez les journaliser mais ne pas les afficher.
// Développement
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Production
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/path/to/php-error.log');
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
Utilisez des variables d'environnement ou des fichiers de configuration pour basculer automatiquement entre ces paramètres. Ne comptez jamais sur l'édition manuelle de php.ini sur les serveurs de production.
2. Utiliser des exceptions au lieu de codes d'erreur
Retourner des codes d'erreur (comme false ou -1) est un modèle hérité qui encombre votre code et facilite l'ignorance des échecs. Les exceptions vous obligent à gérer les erreurs explicitement et gardent votre chemin heureux propre.
// Mauvais : code d'erreur
function getUser($id) {
$user = db_find($id);
if (!$user) {
return false; // l'appelant doit vérifier
}
return $user;
}
// Bon : exception
function getUser($id) {
$user = db_find($id);
if (!$user) {
throw new UserNotFoundException("Utilisateur $id introuvable");
}
return $user;
}
Créez des classes d'exception personnalisées pour différents types d'erreurs. Cela facilite la capture d'erreurs spécifiques et leur gestion appropriée.
3. Capturer les exceptions au bon niveau
Une erreur courante est de capturer les exceptions trop tôt ou trop largement. Capturez les exceptions uniquement lorsque vous pouvez réellement faire quelque chose à leur sujet—les journaliser, réessayer, ou afficher un message convivial pour l'utilisateur.
try {
$user = getUser($id);
$order = createOrder($user, $items);
} catch (UserNotFoundException $e) {
// Gérer spécifiquement l'utilisateur manquant
return response('Utilisateur introuvable', 404);
} catch (PaymentFailedException $e) {
// Gérer l'échec de paiement
return response('Échec du paiement : ' . $e->getMessage(), 400);
} catch (Throwable $e) {
// Capture tout pour les erreurs inattendues
log_error($e);
return response('Quelque chose s\'est mal passé', 500);
}
Utilisez Throwable (PHP 7+) pour capturer à la fois les exceptions et les erreurs. Évitez les blocs catch vides—si vous capturez, faites quelque chose de significatif.
4. Journaliser les erreurs avec contexte
La journalisation est votre meilleur ami pour déboguer les problèmes de production. Mais un message de log comme « Une erreur s'est produite » est inutile. Incluez le contexte : ID utilisateur, paramètres de requête, trace de pile et horodatages.
try {
processPayment($order);
} catch (PaymentException $e) {
error_log(sprintf(
"Échec du paiement pour la commande %d : %s dans %s:%d\nTrace de pile : %s",
$order->id,
$e->getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString()
));
throw $e; // relancer après journalisation
}
Envisagez d'utiliser une bibliothèque de journalisation comme Monolog pour des logs structurés. Elle prend en charge différents gestionnaires (fichier, syslog, Slack) et niveaux de log (debug, info, warning, error).
5. Créer un gestionnaire d'erreurs personnalisé
Le gestionnaire d'erreurs par défaut de PHP affiche les erreurs à l'écran, ce qui ne convient pas à la production. Un gestionnaire d'erreurs personnalisé vous permet de convertir les erreurs en exceptions, de les journaliser ou d'afficher une page d'erreur conviviale.
set_error_handler(function ($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return false; // respecter les paramètres error_reporting
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
set_exception_handler(function ($e) {
log_error($e);
http_response_code(500);
include 'views/error.php';
});
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
log_error(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']));
}
});
Cette configuration garantit que toutes les erreurs—y compris les erreurs fatales—sont journalisées et gérées avec élégance.
6. Valider les entrées et échouer tôt
De nombreuses erreurs proviennent d'entrées invalides. Validez les données aux frontières de votre application (contrôleurs, points de terminaison API) et lancez des exceptions immédiatement si la validation échoue. Cela empêche les erreurs de se propager profondément dans votre code.
function createUser(array $data) {
if (empty($data['email'])) {
throw new InvalidArgumentException('L\'email est requis');
}
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Format d\'email invalide');
}
// ... continuer en toute confiance
}
Utilisez les fonctions de filtre de PHP ou une bibliothèque de validation (comme Respect\Validation) pour maintenir une validation cohérente.
7. Ne supprimez pas les erreurs avec @
L'opérateur @ fait taire les erreurs, rendant le débogage plus difficile. Il est tentant de l'utiliser lors de l'appel de fonctions qui pourraient émettre des avertissements (comme file_get_contents), mais il cache de vrais problèmes. À la place, vérifiez les préconditions ou utilisez try-catch avec des exceptions.
// Mauvais
$content = @file_get_contents($url);
// Bon
if (!is_readable($url)) {
throw new RuntimeException("Impossible de lire $url");
}
$content = file_get_contents($url);
Si vous devez supprimer, faites-le uniquement pour des cas bien compris et documentez pourquoi.
8. Utiliser un middleware de gestion centralisée des erreurs
Dans des frameworks comme Laravel ou Symfony, la gestion des erreurs est souvent centralisée dans un middleware ou un gestionnaire d'exceptions. Si vous construisez le vôtre, créez un point d'entrée unique qui capture toutes les exceptions et les convertit en réponses HTTP.
// Dans votre contrôleur frontal (index.php)
try {
$response = $router->dispatch($request);
} catch (HttpException $e) {
$response = new Response($e->getMessage(), $e->getStatusCode());
} catch (Throwable $e) {
log_error($e);
$response = new Response('Erreur interne du serveur', 500);
}
$response->send();
Cela garde la logique de gestion des erreurs en un seul endroit et assure la cohérence.
Comparaison : Approches de gestion des erreurs
| Approche | Avantages | Inconvénients |
|---|---|---|
| Codes d'erreur | Simple, pas d'exceptions | Facile à ignorer, encombre le code |
| Exceptions | Force la gestion, séparation propre | Peut être surutilisé, surcoût de performance |
| Gestionnaire d'erreurs personnalisé | Centralisé, capture toutes les erreurs | Nécessite une configuration, peut masquer les erreurs si mal configuré |
| Journalisation uniquement | Non intrusif, bon pour la surveillance | Ne gère pas les erreurs, ne fait que les enregistrer |
FAQ
Quelle est la différence entre les erreurs et les exceptions en PHP ?
Les erreurs sont des problèmes de bas niveau comme les erreurs de syntaxe ou de type, tandis que les exceptions sont des objets lancés qui représentent des conditions exceptionnelles. Dans PHP 7+, les deux implémentent l'interface Throwable, vous pouvez donc capturer les deux avec un seul bloc catch.
Dois-je utiliser try-catch pour chaque appel de fonction ?
Non, cela conduit à un code trop défensif. Capturez les exceptions uniquement lorsque vous pouvez les gérer de manière significative—journaliser, réessayer ou afficher un message convivial pour l'utilisateur. Laissez les exceptions remonter à un gestionnaire central pour les cas inattendus.
Comment journaliser les erreurs sans exposer de données sensibles ?
Assainissez les messages de log en supprimant les mots de passe, les jetons et les données personnelles. Utilisez une journalisation structurée avec des champs de contexte, et configurez votre bibliothèque de journalisation pour masquer les clés sensibles. Assurez-vous également que les fichiers de log sont stockés en toute sécurité et que l'accès est restreint.
Prêt à rationaliser votre gestion des erreurs PHP ? Commencez par auditer vos paramètres actuels de rapport d'erreurs et implémentez un gestionnaire d'erreurs personnalisé. Pour un débogage rapide des charges utiles JSON ou des logs, essayez notre JSON Formatter pour valider et embellir vos données.