W3docs

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éthodeRetourne
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: 100

Intercepter 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 une TypeError due à un type d'argument incorrect), interceptez Throwable. 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 Exception brute 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 avec set_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

Exercice

Pratique
Qu'est-ce qui est vrai concernant la gestion des exceptions PHP ?
Qu'est-ce qui est vrai concernant la gestion des exceptions PHP ?
Was this page helpful?