Les Exceptions

Résumé Support Quiz

Dans ce chapitre, nous allons voir le principe des exceptions en PHP. Une exception permet de signaler qu'une situation inattendue s'est produite, puis de décider plus haut dans le code comment gérer ce problème.

C'est particulièrement utile lorsque l'on écrit du code qui dépend d'éléments extérieurs : une API peut ne pas répondre, une requête peut retourner une erreur, une donnée peut être absente. Plutôt que de retourner null partout et de provoquer des erreurs difficiles à comprendre plus tard, on peut lancer une exception explicite.

Le problème des erreurs silencieuses

Quand une fonction rencontre un problème, on peut être tenté de retourner null ou false. Cela fonctionne, mais il faut ensuite penser à vérifier cette valeur à chaque étape. Si on oublie une vérification, l'erreur réelle peut être masquée par une autre erreur plus loin dans le code.

$data = $this->callAPI('weather?q=Montpellier,fr'); foreach ($data as $day) { // Si $data vaut null, l'erreur apparaîtra ici. }

Dans ce cas, le message d'erreur ne dit pas forcément que le problème vient de l'appel à l'API. Il indique seulement que le code essaie de parcourir une valeur invalide. Les exceptions permettent de signaler le vrai problème au bon moment.

Lancer une exception

Pour lancer une exception, on utilise le mot-clé throw, suivi d'un objet qui représente l'erreur. PHP fournit une classe Exception que l'on peut utiliser directement.

throw new Exception('Impossible de récupérer les données');

Dans un appel cURL, on peut lancer une exception quand l'exécution échoue ou quand le code HTTP n'est pas celui attendu.

$data = curl_exec($curl); if ($data === false) { throw new Exception(curl_error($curl)); } $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($code !== 200) { throw new Exception($data, $code); }

Une exception non capturée provoque une erreur fatale. PHP arrête l'exécution du script et affiche le message de l'exception. Ce comportement peut sembler brutal, mais il a un avantage : on évite de continuer avec un état invalide.

Capturer une exception avec try / catch

Pour gérer une exception, on entoure le code qui peut échouer avec un bloc try, puis on ajoute un bloc catch pour récupérer l'exception.

try { $data = $this->callAPI('weather?q=Montpellier,fr'); } catch (Exception $e) { echo $e->getMessage(); }

Le bloc try contient le code que l'on veut essayer d'exécuter. Si une exception est lancée, PHP quitte immédiatement le bloc try et entre dans le bloc catch correspondant.

Dans le catch, on reçoit l'objet exception. On peut ensuite utiliser ses méthodes, par exemple getMessage(), pour récupérer le message d'erreur.

catch (Exception $e) { $message = $e->getMessage(); }

On voit souvent la variable $e, simplement parce que c'est l'abréviation d'exception.

Choisir où capturer l'erreur

Une exception remonte dans la pile d'appels tant qu'elle n'est pas capturée. Cela permet de lancer l'exception dans une méthode interne, puis de la gérer plus haut, au moment où l'on sait quoi faire pour l'utilisateur.

try { $today = $weather->getToday('Montpellier,fr'); $forecast = $weather->getForecast('Montpellier,fr'); } catch (Exception $e) { $error = $e->getMessage(); }

Cette approche est souvent plus propre que de gérer chaque erreur immédiatement dans les méthodes internes. La classe peut signaler qu'un problème est arrivé, et le code qui l'utilise décide ensuite s'il faut afficher une alerte, arrêter le script, journaliser l'erreur ou afficher une valeur de remplacement.

<?php if ($error): ?> <div class="alert alert-danger"> <?= $error ?> </div> <?php else: ?> <!-- Affichage normal --> <?php endif ?>

L'avantage est que le comportement en cas d'erreur reste centralisé et plus lisible.

Exécuter du code dans finally

PHP propose aussi un bloc finally. Le code placé dans finally est exécuté quoi qu'il arrive : que le try réussisse ou qu'une exception soit lancée.

try { $data = curl_exec($curl); if ($data === false) { throw new Exception(curl_error($curl)); } } catch (Exception $e) { echo $e->getMessage(); } finally { curl_close($curl); }

Ce bloc est pratique pour libérer une ressource ou fermer quelque chose qui doit toujours être fermé. Dans le cas de cURL, il faut penser à appeler curl_close() même lorsqu'une erreur se produit.

Créer ses propres exceptions

On peut utiliser directement la classe Exception, mais on peut aussi créer nos propres classes d'exception. Pour cela, on crée une classe qui hérite de Exception.

<?php class APIException extends Exception { }

Cette classe peut rester vide. Son intérêt est surtout de représenter un type d'erreur plus précis. On peut ensuite la lancer comme une exception classique.

throw new APIException('Erreur lors de l'appel à l'API');

Puis la capturer spécifiquement :

try { $data = $this->callAPI('weather?q=Montpellier,fr'); } catch (APIException $e) { echo 'Erreur API : ' . $e->getMessage(); } catch (Exception $e) { echo 'Erreur classique : ' . $e->getMessage(); }

L'ordre des catch est important. Si on capture Exception en premier, ce bloc attrapera aussi les exceptions personnalisées qui héritent de Exception. Il faut donc placer les exceptions les plus spécifiques avant les exceptions plus générales.

Structurer plusieurs types d'erreurs

Créer plusieurs classes d'exception permet de distinguer les problèmes. On peut par exemple représenter une erreur cURL, une erreur HTTP, puis une erreur HTTP plus précise.

<?php class CurlException extends Exception { public function __construct($curl) { parent::__construct(curl_error($curl)); curl_close($curl); } } <?php class HTTPException extends Exception { } <?php class UnauthorizedHTTPException extends HTTPException { }

On peut ensuite lancer l'exception adaptée selon la situation.

$data = curl_exec($curl); if ($data === false) { throw new CurlException($curl); } $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($code === 401) { $response = json_decode($data, true); throw new UnauthorizedHTTPException($response['message'], 401); } if ($code !== 200) { throw new HTTPException($data, $code); }

Grâce à l'héritage, on peut ensuite capturer un type précis ou une famille d'erreurs.

try { $today = $weather->getToday('Montpellier,fr'); $forecast = $weather->getForecast('Montpellier,fr'); } catch (CurlException $e) { die($e->getMessage()); } catch (HTTPException $e) { $error = $e->getMessage(); }

Ici, UnauthorizedHTTPException sera capturée par le bloc catch (HTTPException $e), car elle hérite de HTTPException.

Capturer les erreurs PHP

Les exceptions ne sont pas les seules erreurs que l'on peut capturer. PHP possède aussi une classe Error, qui représente certaines erreurs internes du langage.

try { $data = explode(','); } catch (Error $e) { echo $e->getMessage(); }

La classe Error fonctionne de manière proche de Exception, avec un message, un code et des sous-classes plus précises. Certaines erreurs PHP peuvent ainsi être capturées avec catch (Error $e).

Dans la vidéo, declare(strict_types=1); est aussi utilisé pour rendre PHP plus strict dans certains cas.

<?php declare(strict_types=1);

Avec un mode plus strict, certaines erreurs sont remontées plus clairement et peuvent être capturées comme des erreurs.

Capturer plusieurs types en même temps

Si plusieurs types d'erreurs doivent être traités de la même manière, on peut les regrouper dans un même catch avec |.

try { // Code qui peut lancer une exception ou une erreur. } catch (Exception | Error $e) { $error = $e->getMessage(); }

Cela évite de dupliquer le même traitement dans plusieurs blocs catch.

À retenir

Les exceptions servent à signaler des situations anormales de manière explicite. Plutôt que de retourner null sans contexte, on lance une exception avec un message clair, puis on la capture à l'endroit où l'on sait comment réagir.

On peut utiliser Exception directement ou créer ses propres classes pour représenter des erreurs plus précises. Grâce à l'héritage, il devient possible de capturer un type d'erreur spécifique ou une famille complète d'erreurs.

Ressources

Pour aller plus loin, vous pouvez consulter la documentation officielle PHP sur les exceptions, la classe Exception et la classe Error.
#