PHP Exception
Apprenez les exceptions PHP : lancer et intercepter des erreurs avec try/catch/finally, les méthodes de la classe Exception et les classes personnalisées.
Qu'est-ce qu'une exception ?
Une exception est un objet qui représente une erreur ou une condition inattendue qui interrompt le déroulement normal d'un programme. Au lieu de laisser un problème faire planter le script silencieusement, vous lancez une exception à l'endroit où quelque chose tourne mal, puis vous la interceptez là où vous savez comment récupérer, journaliser ou signaler l'erreur.
Cette page explique comment lancer et intercepter des exceptions avec try/catch/finally, les méthodes que tout objet exception vous fournit, comment intercepter plusieurs types d'exceptions, et comment écrire vos propres classes d'exception.
Les exceptions sont utiles chaque fois qu'une fonction ne peut pas continuer de manière significative : une entrée utilisateur invalide, une connexion à la base de données échouée, un fichier manquant, ou une valeur hors de la plage autorisée. Elles séparent la logique de gestion des erreurs de la logique normale, de sorte que le « chemin heureux » reste lisible.
throw new Exception('Something went wrong');En PHP 7 et versions ultérieures, Exception et Error implémentent toutes deux l'interface Throwable, donc tout ce qui peut être lancé est un Throwable.
Lancer et intercepter avec try / catch / finally
Vous enveloppez le code risqué dans un bloc try. Si une instruction à l'intérieur lance une exception, PHP arrête d'exécuter le reste du bloc try et saute au premier catch correspondant. Le bloc finally optionnel s'exécute toujours ensuite — qu'une exception ait été lancée ou non — ce qui en fait le bon endroit pour libérer des ressources (fermer un fichier, un descripteur de base de données, un verrou).
<?php
function divide($a, $b) {
if ($b === 0) {
throw new InvalidArgumentException('Division by zero is not allowed.');
}
return $a / $b;
}
try {
echo divide(10, 2), "\n"; // 5
echo divide(10, 0), "\n"; // throws — the next line never runs
} catch (InvalidArgumentException $e) {
echo 'Caught: ' . $e->getMessage() . "\n";
} finally {
echo "Done.\n";
}Sortie :
5
Caught: Division by zero is not allowed.
Done.Notez que le second echo divide(...) ne s'affiche jamais, car le lancement d'exception interrompt immédiatement le bloc try. Le bloc finally s'exécute quand même.
Lire les informations d'une exception
Chaque objet exception contient des détails utiles. Les méthodes les plus courantes, toutes héritées de la classe de base Exception, sont :
| Méthode | Retourne |
|---|---|
getMessage() | Le message d'erreur lisible par l'homme |
getCode() | Le code d'erreur entier que vous avez passé au constructeur |
getFile() | Le fichier où l'exception a été créée |
getLine() | Le numéro de ligne où elle a été créée |
getTraceAsString() | La pile d'appels sous forme de chaîne, utile pour la journalisation |
getPrevious() | L'exception précédente, quand une en enveloppe une autre |
<?php
class InsufficientFundsException extends Exception {}
class Account {
private float $balance;
public function __construct(float $balance) { $this->balance = $balance; }
public function withdraw(float $amount): void {
if ($amount > $this->balance) {
throw new InsufficientFundsException(
"Cannot withdraw $amount; balance is {$this->balance}.",
100 // a custom error code
);
}
$this->balance -= $amount;
}
}
$account = new Account(50);
try {
$account->withdraw(80);
} catch (InsufficientFundsException $e) {
echo $e->getMessage() . "\n"; // Cannot withdraw 80; balance is 50.
echo 'Code: ' . $e->getCode() . "\n"; // Code: 100
}Sortie :
Cannot withdraw 80; balance is 50.
Code: 100Intercepter plusieurs types d'exceptions
Un seul try peut avoir plusieurs blocs catch, vérifiés de haut en bas — le premier dont le type correspond à l'exception lancée l'emporte. Si deux types d'exceptions non liés doivent être traités de la même façon, combinez-les dans un seul bloc avec l'opérateur | (pipe) plutôt que de dupliquer le code.
<?php
function parseAge(string $input): int {
if (!is_numeric($input)) {
throw new TypeError("'$input' is not a number.");
}
$age = (int) $input;
if ($age < 0) {
throw new RangeException("Age cannot be negative.");
}
return $age;
}
foreach (['42', 'abc', '-5'] as $value) {
try {
echo parseAge($value) . "\n";
} catch (TypeError | RangeException $e) {
echo get_class($e) . ': ' . $e->getMessage() . "\n";
}
}Sortie :
42
TypeError: 'abc' is not a number.
RangeException: Age cannot be negative.L'ordre est important : comme les exceptions PHP forment une hiérarchie, placez les types plus spécifiques en premier et les types plus larges (comme Exception ou Throwable) en dernier, sinon le bloc large interceptera tout avant que le bloc spécifique ait une chance.
Créer des exceptions personnalisées
Au-delà des classes intégrées, vous pouvez définir vos propres types d'exceptions en étendant Exception (ou une classe intégrée plus spécifique comme RuntimeException). Une classe dédiée rend vos blocs catch expressifs — vous pouvez réagir spécifiquement à votre erreur — et vous permet d'attacher des données supplémentaires.
Dans l'exemple Account ci-dessus, InsufficientFundsException est une exception personnalisée. Souvent une sous-classe vide suffit ; n'ajoutez des méthodes que lorsque vous avez besoin d'un comportement supplémentaire :
<?php
class ValidationException extends Exception {
private array $errors;
public function __construct(string $message, array $errors = []) {
parent::__construct($message);
$this->errors = $errors;
}
public function getErrors(): array {
return $this->errors;
}
}Si vous redéfinissez le constructeur, appelez toujours parent::__construct() afin que le message, le code et l'exception précédente soient correctement configurés.
Intercepter
Throwable. Pour gérer à la fois les exceptions ordinaires et les erreurs au niveau du moteur (comme uneTypeErrordue à un type d'argument incorrect), interceptezThrowable. C'est le « catch-all » le plus sûr, mais utilisez-le en dernier recours pour ne pas masquer accidentellement des bugs à corriger :try { // risky code } catch (Throwable $e) { error_log($e->getMessage()); }
Bonnes pratiques
- N'interceptez que ce que vous pouvez gérer. Laissez les exceptions dont vous ne pouvez pas vous remettre se propager jusqu'à un gestionnaire central.
- Lancez tôt, interceptez tard. Lancez exactement à l'endroit où une valeur devient invalide ; interceptez là où vous pouvez réellement répondre.
- Utilisez des types spécifiques. Les sous-classes personnalisées ou intégrées sont préférables à une
Exceptionbrute pour le flux de contrôle. - Écrivez des messages informatifs, et utilisez
getCode()pour une catégorisation lisible par machine. - Journalisez, ne silenciez pas. Enregistrez les exceptions avec
error_log()plutôt que de les avaler. Pour les exceptions non interceptées, enregistrez un gestionnaire global avecset_exception_handler(). - N'utilisez pas les exceptions pour le flux normal. Réservez-les aux conditions véritablement exceptionnelles.
Comment le flux de contrôle fonctionne
Le diagramme ci-dessous montre le chemin d'exécution à travers une structure try/catch/finally :
graph TD
Try[Try Block] -->|No Exception| Finally[Finally Block]
Try -->|Exception Thrown| Catch[Catch Block]
Catch --> Finally