En PHP, on lève une exception avec throw new Exception('message');. L'exécution s'arrête à cette ligne et saute au bloc catch le plus proche qui accepte le type de l'exception, où $e->getMessage() renvoie le message. Utilisez une classe précise, comme InvalidArgumentException, pour que les appelants puissent distinguer les problèmes.
Une exception est un objet. throw la fait remonter la pile d'appels jusqu'au premier appelant qui a un catch correspondant, et c'est cet appelant qui décide quoi faire ; la fonction qui a trouvé le problème n'a pas à le savoir. Le fonctionnement de l'interception, de finally et de l'interception de plusieurs types est expliqué sur la page try catch.
Ce que contient un objet exception
Chaque exception porte un message, un code entier, le fichier et la ligne où elle a été créée, et une trace de la pile d'appels. Le constructeur prend ($message, $code, $previous), tous facultatifs :
getLine() est la ligne du throw new, pas celle du catch. La trace liste les appels qui y ont mené (#0 /home/index.php(8): findOrder()), et c'est en général la première chose à lire quand une erreur apparaît dans un journal. Convertir l'exception en chaîne (echo $e; ou (string) $e) affiche tout cela au format standard de PHP.
Les classes d'exception natives
PHP fournit une famille de classes d'exception dans sa Standard PHP Library (SPL). Lever celle qui nomme le problème rend votre code plus lisible et permet aux appelants d'intercepter de façon ciblée. Toutes celles-ci étendent Exception :
| Classe | À lever quand |
|---|---|
InvalidArgumentException | un argument a une mauvaise forme : un nom vide, une option inconnue |
DomainException | une valeur est hors de l'ensemble qui a du sens : un prix négatif |
OutOfRangeException | le code a demandé un index qui ne peut jamais exister (un bug de l'appelant) |
OutOfBoundsException | une clé ou un index manque dans des données connues seulement à l'exécution |
RangeException | un résultat calculé sort de la plage valide pendant l'exécution |
LengthException | quelque chose est trop long ou trop court |
RuntimeException | un problème détectable seulement à l'exécution : disque plein, délai dépassé |
UnexpectedValueException | une fonction a renvoyé ou reçu une valeur d'un type inattendu |
LogicException | le code lui-même est faux, un bug plutôt qu'une mauvaise saisie |
JsonException | levée par json_encode()/json_decode() avec JSON_THROW_ON_ERROR |
InvalidArgumentException, DomainException, LengthException et OutOfRangeException étendent LogicException ; OutOfBoundsException, RangeException et UnexpectedValueException étendent RuntimeException. Vous pouvez vérifier vous-même les parents de n'importe quelle classe :
TypeError a Error pour parent, pas Exception : elle appartient à la seconde famille, ci-dessous.
Les erreurs que PHP lève lui-même
Depuis PHP 7, et bien plus encore depuis PHP 8, les fonctions et opérateurs de PHP lèvent des sous-classes d'Error pour des problèmes qui étaient autrefois des warnings. Vous les interceptez de la même façon, mais catch (Exception $e) ne les attrapera pas :
DivisionByZeroError, TypeError, ValueError, ArgumentCountError, UnhandledMatchError et Error simple étendent toutes Error. Chacune signale plus souvent un bug dans le code appelant que des données invalides, c'est pourquoi elles vivent hors de l'arbre Exception. C'est declare(strict_types=1) qui fait de str_repeat(5, 2) une TypeError ; sans lui, PHP convertirait 5 en "5".
Écrire une classe d'exception personnalisée
Une sous-classe d'une ligne suffit souvent : le nom de la classe porte le sens, et les appelants peuvent intercepter exactement ce type. Quand celui qui intercepte a besoin de données (quelle commande, quel montant), ajoutez des propriétés et transmettez le message avec parent::__construct() :
Choisissez le parent avec soin : étendre RuntimeException (plutôt qu'Exception simple) signifie que le code qui intercepte RuntimeException traite aussi la vôtre. Une petite hiérarchie, une exception de base par bibliothèque ou par module avec des sous-classes précises en dessous, permet aux appelants de choisir entre « tout intercepter des paiements » et « intercepter seulement ce cas ».
Chaîner les exceptions avec previous
Quand vous interceptez une exception de bas niveau et en levez une de plus haut niveau, passez l'originale en troisième argument. Rien n'est perdu : getPrevious() remonte jusqu'à la cause première.
L'utilisateur voit « customers.csv could not be imported » ; le journal, en parcourant la chaîne, reçoit aussi « row 2 has 2 columns ». Si une exception chaînée n'est jamais interceptée, l'erreur fatale de PHP affiche toute la chaîne : la cause d'origine d'abord, puis chaque enveloppe sous une ligne Next.
throw comme expression
Depuis PHP 8.0, throw est une expression, il peut donc se placer partout où une valeur est attendue : après ??, dans un ternaire, dans une fonction fléchée. « Obtenir ceci ou échouer » tient alors en une ligne :
Quand ne pas lever d'exception
Les exceptions sont pour les situations que la fonction courante ne peut pas traiter et que le déroulement normal ne prévoit pas. Une recherche qui ne trouve rien, un champ facultatif vide ou un utilisateur non connecté sont des résultats ordinaires : renvoyez null, false ou un tableau vide et laissez l'appelant vérifier, et gardez les exceptions pour les cas où continuer serait une erreur.
Deux autres habitudes gardent le code des exceptions lisible : n'interceptez jamais une exception juste pour l'ignorer (un catch (Exception $e) {} vide cache le prochain vrai bug), et levez des classes précises plutôt qu'un simple Exception, pour qu'un appelant ne soit jamais obligé de tout intercepter pour traiter un seul cas.
Questions fréquentes
Comment lever une exception en PHP ?
Créez un objet exception et levez-le : throw new InvalidArgumentException('Quantity must be positive');. L'exécution s'arrête à cette ligne et saute au bloc catch correspondant le plus proche ; s'il n'y en a pas, le script se termine par une erreur fatale « Uncaught ».
Comment créer une exception personnalisée en PHP ?
Étendez Exception ou l'une de ses sous-classes : class PaymentFailedException extends RuntimeException {}. Cette seule ligne suffit pour la lever et l'intercepter par son propre type. Ajoutez des propriétés et un constructeur quand celui qui l'intercepte a besoin de données supplémentaires, et appelez parent::__construct($message, $code, $previous).
Quelle est la différence entre getMessage et getCode ?
getMessage() renvoie le texte passé en premier argument du constructeur, destiné aux humains et aux journaux. getCode() renvoie l'entier passé en second argument (0 par défaut), utile pour des vérifications automatiques comme associer des erreurs à des codes de statut HTTP.
Quelle exception lever en PHP ?
Utilisez une classe SPL native qui correspond au problème : InvalidArgumentException pour un mauvais argument, DomainException pour une valeur hors de ce qui est permis, RuntimeException pour des échecs visibles seulement à l'exécution (disque plein, délai dépassé), LogicException pour des erreurs de programmation. Pour les erreurs que vos appelants doivent distinguer, créez votre propre sous-classe.
Qu'est-ce que le chaînage d'exceptions en PHP ?
Passer l'exception d'origine en troisième argument du constructeur quand vous en levez une nouvelle : throw new ImportException('Import failed', 0, $e);. La nouvelle exception apporte le contexte, et $e->getPrevious() sur elle renvoie la cause d'origine, si bien qu'aucun détail n'est perdu.