Comment utiliser les exceptions de la SPL pour une meilleure gestion des erreurs ?

Jani Hartikainen

Traducteur

itanea

Cet article est une traduction de « How to use built-in SPL exception classes for better error handling », par Jani Hartikainen.

Sachant que la documentation sur les exceptions de la SPL est « assez » (euphémisme) pauvre, cet article vous propose de creuser un peu plus la question et de trouver des exceptions adaptées à différentes situations. L'objectif final n'étant pas d'étaler son savoir sur les exceptions SPL en soirée mais bien de faciliter le débogage de ses applications.

Commentez

- Titre : Comment utiliser les exceptions de la SPL pour une meilleure gestion des erreurs ?

- Auteur : Jani Hartikainen

- Traducteur : itanea

- Parution : 16 novembre 2011

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2011 itanea. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Comment utiliser les exceptions de la SPL pour une meilleure gestion des erreurs ?

- Que sont les exceptions SPL ?

- BadFunctionCallException

- BadMethodCallException

- DomainException

- InvalidArgumentException

- LengthException

- LogicException

- OutOfBoundsException

- OutOfRangeException

- OverflowException

- RangeException

- RuntimeException

- UnderflowException

- UnexpectedValueException

- Autres astuces

- Conclusion

- Pour aller plus loin

- Notes et remerciements

Comment utiliser les exceptions de la SPL pour une meilleure gestion des erreurs ?

Depuis PHP5, il y a un bundle intégré d'exceptions  les « SPL Exceptions »  au cœur de PHP. Cependant la documentation pour ces classes fait légèrement défaut, et il peut être difficile de bien saisir à quel moment les utiliser.

Une réponse rapide serait toujours. Vous trouverez une réponse plus complète en poursuivant votre lecture.

Que sont les exceptions SPL ?

L'exception de base en PHP est la classe Exception.

Les Exceptions de la « SPL Exceptions » sont assez similaires à cette classe, mais sont plus spécialisées  en tant que telles, elles doivent être utilisées pour rapporter des conditions d'erreurs spécifiques. Elles sont utilisées exactement de la même manière que les exceptions de base  en utilisant throw.

Vous devriez utiliser ces classes plus spécifiques la plupart du temps, et à partir du moment où elles s'adaptent aux conditions plus spécifiques que vous avez généralement.

Vous pouvez trouver la liste des Exceptions SPL dans le manuel PHP. Nous allons à présent voir chacune d'entre elles et un exemple ou deux de la façon de les utiliser.

BadFunctionCallException

Si on suit l'ordre du manuel, la première exception est BadFunctionCallException.

Cette exception n'est pas vraiment utile selon moi. Habituellement cela est déclenché par PHP si vous appelez du code de manière incorrecte, mais depuis que l'usage principal de cela se fait si une fonction (pas une méthode de classe) est appelée sans tous les paramètres nécessaires ou si la fonction n'existe pas, le code personnalisé tombe rarement dans ce cas-là.

BadMethodCallException

Celle-là est un peu plus intéressante. Similaire à l'exceptionBadFunctionCallException, elle devrait être déclenchée lorsqu'une méthode de classe soit n'existe pas, soit ne dispose pas de tous les paramètres requis.

L'utilisation principale de cette exception est lorsque vous implémentez la méthode magique __call :

public function __call($name, $arg) {
// Imaginons une condition déterminant l'action à effectuer
if(...) {
// La condition est remplie, retournons une valeur ou autre chose
}

// Quelque chose s'est mal passé - La méthode n'existe pas !
throw new BadMethodCallException("La méthode '$name' n'existe pas");
}

C'est en fait une bonne pratique : toujours lever une BadMethodCallException lorsque vous utilisez une méthode __call ! Sinon votre code pourrait appeler des fonctions qui n'existent vraiment pas, et vous ne seriez jamais averti de cela (par exemple si vous avez mal saisi le nom).

DomainException

L'exceptionDomainException est un peu plus compliquée à expliquer.

Simplement, c'est ce que vous devriez lever si votre code est approximatif et par exemple lorsqu'un contrôle de validité trouve une valeur « en dehors du domaine de validité ».

Par exemple, si vous avez une méthode qui effectue des calculs sur les jours de la semaine et si pour une quelconque raison un résultat de calcul est en dehors de la plage de 1 à 7 (pour les jours d'une semaine), vous pouvez lever une exception DomainException. C'est parce que la valeur se trouve à l'extérieur du « domaine » pour les numéros de jour dans une semaine.

InvalidArgumentException

Celle-ci est assez explicite : lancez une exceptionInvalidArgumentException lorsque vos fonctions ou méthodes reçoivent des arguments invalides.

Par exemple, si votre fonction attend un nombre mais qu'à la place elle reçoit une chaîne, lancez une exception InvalidArgumentException indiquant que la fonction attend un nombre :

public function numberRobot($number) {
if(!is_numeric($number)) {
throw new InvalidArgumentException('Le robot nombre demande un sacrifice numérique !');
}
}

De plus, vous pouvez utiliser cela lorsque l'application reçoit des arguments invalides POST ou GET. Par exemple, selon la façon dont vous voulez gérer vos erreurs, vous pouvez lever une exception InvalidArgumntException dans un contrôleur qui attend des arguments GET/POST spécifiques mais reçoit des mauvais types de ces derniers.

LengthException

L'exceptionLengthException peut être utilisée lorsque la longueur d'un élément est trop courte ou trop longue. Par exemple, la longueur d'un nom de fichier peut être trop longue.

Elle peut également être utilisée si la taille d'un tableau est incorrecte.

LogicException

L'exceptionLogicException est de nouveau un peu plus compliquée car elle n'a pas d'utilisations évidentes, la plupart des cas possibles sont déjà couverts par les autres exceptions.

L'utilisation principale de l'exceptionLogicException est quasiment similaire à l'exception DomainException  elle peut être utilisée si votre code (par exemple un calcul) renvoie un résultat qu'il ne devrait pas produire.

Les erreurs qui peuvent lever une exception LogicException sont généralement provoquées par un bogue dans votre code.

OutOfBoundsException

Une exceptionOutOfBoundsException devrait être levée si le code tente d'accéder à une clef invalide.

Typiquement cela peut être utilisé dans un code essayant d'accéder à un tableau associatif, mais effectue un contrôle sur la clé.

Enfin, un autre usage de cette exception est lorsque vous implémentez ArrayAccess dans votre classe.

public function offsetGet($offset) {
if(!isset($this->objects[$offset])) {
throw new OutOfBoundsException("Il n'y a pas de clé '$offset'");
}

return $this->objects[$offset];
}

Ceci doit être utilisé pour les clés, pas les index (comme des chaînes, pas des nombres). Vous pouvez vérifier lors de la mise en œuvre si oui ou non l'offset pouvant être lu est un nombre ou non et lever à la place une exception OutOfRangeException.

OutOfRangeException

C'est la même chose que pour une exception OutOfBoundsException, mais elle doit être utilisée pour les tableaux normaux indexés par des nombres et non des clés.

OverflowException

Si votre classe se comporte comme un conteneur, vous pouvez utiliser l'exception OverflowException lorsque l'objet est rempli et que quelqu'un essaye de lui rajouter quelques items.

RangeException

C'est une exception qui peut être levée lorsqu'une valeur est en dehors des limites définies. Elle est similaire à l'exception DomainException dans ses objectifs, mais elle devrait être utilisée lorsque l'erreur est provoquée dans une séquence de runtime.

RuntimeException

L'objectif de l'exception RuntimeException est assez semblable à son homonyme en Java.

En programmation Java vous avez les erreurs de compilation et les erreurs d'exécution. Les erreurs de compilation doivent toujours être attrapées. Le compilateur Java ne compilera pas du code qui n'a pas de bloc try-catch pour du code qui peut provoquer des erreurs de compilation.

Les exceptions de type Runtime en Java ne requièrent pas de bloc catch dans le code source.

Comme PHP ne supporte pas les erreurs de compilation, la séparation entre les exceptions d'exécution et les autres est moins stricte. Cependant, l'objectif d'une exception RuntimeException reste identique : elle doit être attrapée dans le cas où le code n'est pas en mesure de le faire lui-même.

Cela s'applique également aux classes filles de l'exception RuntimeException : OutOfBoundsException, OverflowException, RangeException, UnderflowException et UnexpectedValueException.

UnderflowException

C'est l'opposé de l'exception OverflowException. Si votre classe est un conteneur et est vide, mais que quelque chose essaye de lui supprimer un élément, vous pouvez lever une exception UnderflowException.

UnexpectedValueException

Une exception UnexpectedValueException peut être levée si une valeur se trouve en dehors d'un jeu de valeurs défini.

Par exemple, si vous avez une liste de const et qu'une valeur doit être l'une d'entre elles :

const TYPE_FOO = 'foo';
const TYPE_BAR = 'bar';

public function doSomething($x) {
if($x != self::TYPE_FOO || $x != self::TYPE_BAR) {
throw new UnexpectedValueException('Le paramètre doit être l\'une des constantes TYPE_*');
}
}

Autres astuces

Parfois, plusieurs exceptions peuvent être attribuées pour une même erreur. Dans ce cas vous devez toujours essayer d'utiliser l'exception la plus à même d'indiquer ce qui est en erreur. Le but est de faciliter le débogage du code.

C'est aussi pourquoi vous devriez toujours permettre au message de l'exception de se révéler utile. Il n'est pas nécessaire qu'il soit compréhensible par quiconque utilisant votre application, mais il doit être compréhensible par celui qui la développe ou la maintient.

Si vous êtes amené à écrire une bibliothèque qui sera utilisée par d'autres développeurs, les exceptions doivent toujours essayer d'apporter le maximum d'aide à l'utilisateur de la bibliothèque. C'est extrêmement frustrant lorsque vous essayez de comprendre pourquoi du code ne fonctionne pas, d'obtenir des messages d'erreur portant à confusion.

Conclusion

Vous devez toujours utiliser l'exception qui décrit le mieux le scénario d'erreur correspondant à votre code. Cela ne permet pas uniquement de manipuler plus facilement les erreurs, cela permet également de trouver plus facilement et de comprendre la cause de l'erreur.

Si vous avez d'autres utilisations des exceptions de la SPL que celles que j'ai décrites ici, n'hésitez pas à laisser un commentaire. La documentation officielle pour ces dernières est pauvre et je suis persuadé qu'il y a d'autres façons des les utiliser.

Pour aller plus loin

- Should a failed function return a value or throw an exception? sur le blog de l'auteur.

- Exceptions and abstraction sur le blog de l'auteur.

- Brandon Savage a également écrit d'excellents articles sur les exceptions sur son blog.

Notes et remerciements

J'ai tenté de traduire l'article original de l'auteur de la façon la plus fidèle qui soit. Toutefois, si vous constatez des erreurs importantes, n'hésitez pas à m'en faire part dans les commentaires.

L'article original peut être consulté à cette adresse : How to use built-in SPL exception classes for better error handling | CodeUtopia - The blog of Jani Hartikainen.

La traduction originale peut être consultée sur le blog d'Itinea : Comment utiliser les exceptions de la SPLpour une meilleure gestion des erreurs

Nous tenons à remercier ClaudeLELOUP pour sa relecture attentive de cet article.