Kodokon kodokon.com

Exceptions : hiérarchie, finally, gestionnaire global

Construisez une stratégie d'erreurs complète : exceptions métier chaînées, finally maîtrisé et gestionnaires globaux en dernier rempart.

9 min · 3 questions

Ouvrir cette leçon dans Kodokon

Depuis PHP 7, tout ce qui se lance implémente Throwable, avec deux branches : Error (défaillances du moteur et du typage, comme TypeError) et Exception (échecs applicatifs). Vous n'attrapez Error qu'aux frontières du programme. Côté applicatif, la SPL fournit une hiérarchie prête à l'emploi : LogicException signale un bug du développeur, à corriger ; RuntimeException un aléa d'exécution, à gérer. Le réflexe professionnel : dériver vos exceptions métier de ces bases, une par situation identifiable.

PHP
<?php

declare(strict_types=1);

abstract class AppException extends RuntimeException
{
}

final class UserNotFound extends AppException
{
    public static function withEmail(
        string $email
    ): self {
        return new self("No user for $email");
    }
}

try {
    throw UserNotFound::withEmail('lea@example.com');
} catch (UserNotFound $e) {
    echo $e->getMessage();
}
Hiérarchie métier : on attrape UserNotFound, AppException ou RuntimeException selon le niveau.

try/catch n'a de valeur que si vous savez quoi faire de l'erreur - sinon, laissez-la remonter. Trois outils affinent le contrôle : le multi-catch catch (JsonException | ValueError $e) regroupe les traitements identiques ; le paramètre previous chaîne la cause d'origine quand vous traduisez une exception technique en exception métier ; finally s'exécute dans tous les cas - succès, exception, même return anticipé - idéal pour libérer une ressource.

PHP
<?php

declare(strict_types=1);

final class PayloadInvalid extends RuntimeException
{
}

function decode(string $json): mixed
{
    try {
        return json_decode(
            $json,
            true,
            flags: JSON_THROW_ON_ERROR,
        );
    } catch (JsonException $e) {
        throw new PayloadInvalid(
            'Invalid JSON payload',
            previous: $e,
        );
    } finally {
        echo "decode() finished\n";
    }
}

try {
    decode('{broken');
} catch (PayloadInvalid $e) {
    echo $e->getPrevious()?->getMessage();
}
Traduction d'exception : la cause technique reste accessible via getPrevious().

En bout de chaîne, deux gestionnaires globaux ferment le dispositif. set_exception_handler attrape tout ce qui n'a pas été géré : journalisation structurée, réponse 500 générique - sans jamais divulguer message ni trace à l'utilisateur. set_error_handler convertit les erreurs héritées (warnings, notices) en ErrorException, unifiant les deux mondes : tout devient exception, tout suit le même circuit de traitement.

PHP
<?php

declare(strict_types=1);

set_error_handler(function (
    int $severity,
    string $message,
    string $file,
    int $line,
): bool {
    throw new ErrorException(
        $message,
        0,
        $severity,
        $file,
        $line,
    );
});

set_exception_handler(function (Throwable $e): void {
    error_log($e->getMessage());
    http_response_code(500);
    echo 'A technical error occurred.';
});

trigger_error('Legacy warning', E_USER_WARNING);
echo 'Never reached';
Le warning devient une exception, non attrapée : le gestionnaire global répond, puis le script s'arrête.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Quel est le point commun entre Error et Exception en PHP moderne ?
    • Error hérite d'Exception
    • Les deux implémentent l'interface Throwable
    • Les deux sont réservées au moteur PHP
  2. Quand le bloc finally s'exécute-t-il ?
    • Uniquement si aucune exception n'a été levée
    • Uniquement après un catch
    • Dans tous les cas : succès, exception attrapée ou non, return anticipé
  3. À quoi sert le paramètre previous d'une exception ?
    • À conserver l'exception d'origine quand on la traduit en exception de plus haut niveau
    • À empêcher l'exception de remonter la pile d'appels
    • À définir l'exception à lever au prochain échec