Origine et objectif de l’article
Origine
Cet article est une traduction de « How to use built-in SPL exception classes for better error handling« , vous pourrez trouver l’article original de Jani Hartikainen à cette adresse : http://codeutopia.net/blog/2011/05/06/how-to-use-built-in-spl-exception-classes-for-better-error-handling/
Objectif
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 debugging de ses applications.
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 coeur 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é 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’exception BadFunctionCallException
, 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) { //Let's say here's a condition which determines what to do if(...) { //This is the succesful condition and perhaps we return a value or something } //Here things went wrong - The method doesn't exist! throw new BadMethodCallException("The method '$name' does not exist"); }
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’exception DomainException
est un peu plus compliquée à expliquer.
Simplement, c’est ce 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 calcul 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 exception InvalidArgumentException
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 un chaîne, lancez une exception InvalidArgumentException
indiquant que la fonction attend un nombre:
public function numberRobot($number) { if(!is_numeric($number)) { throw new InvalidArgumentException('The number robot demands a numeric sacrifice!'); } }
De plus, vous pouvez utiliser cela lorsque l’application reçois 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’exception LengthException
peut être utilisée lorsque la longueur d’un élément est trop court ou trop long – 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’exception LogicException
est de nouveau un peu plus compliquée car il n’a pas utilisations évidentes, car la plupart des cas possibles sont déjà couvert par les autres exceptions.
L’utilisation principale de l’exception LogicException
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 bug dans votre code.
OutOfBoundsException
Une exception OutOfBoundsException
devrait être levée si le code tentes 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("The offset '$offset' is out of bounds"); } return $this->objects[$offset]; }
Note: 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 oeuvre 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 remplit, 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 compilations 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 block 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ée 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 elle:
const TYPE_FOO = 'foo'; const TYPE_BAR = 'bar'; public function doSomething($x) { if($x != self::TYPE_FOO || $x != self::TYPE_BAR) { throw new UnexpectedValueException('Parameter must be one of the TYPE_* constants'); } }
Other tips
Parfois, plus d’une exception peut être attribué 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 debugging 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 librairie qui sera utilisée par d’autres développeurs, les exceptions doivent toujours essayer d’apporter le maximum d’aide à l’utilisateur de la librairie. C’est extrêmement frustrant lorsque vous essayez de comprendre pourquoi du code ne fonctionne pas, d’obtenir des messages d’erreurs 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 plus facilement de trouver 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écrite 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? dans le blog de l’auteur.
Exceptions and abstraction dans le blog de l’auteur.
Brandon Savage a également écrit d’excellents sur les exceptions dans son blog.
Note du traducteur :
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.