• 15 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 31/01/2019

Gérer les erreurs

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

La gestion des erreurs est un sujet important en programmation. Il y a souvent des erreurs et il faut savoir vivre avec. Mais comment faire ça bien ? 🤔

Si vous vous souvenez de notre routeur, il contient beaucoup de  if . On fait des tests et on affiche des erreurs à chaque fois s'il y a un problème :

<?php

if (test) {
    // C'est bon, on fait des choses
    // ...
    
    if (encoreUnTest) {
        // C'est bon, on continue
    }
    else {
        echo 'Erreur';
    }
}
else {
    echo 'Autre erreur';
}

Et alors, ça marche, non ?

Oui, mais comme toujours, ce n'est pas parce que ça marche que c'est pratique à la longue. Les développeurs ont en particulier du mal à gérer comme ça les erreurs qui ont lieu à l'intérieur des fonctions.

Que se passe-t-il s'il y a une erreur dans le contrôleur ou dans le modèle ? Va-t-on les laisser se charger d'afficher des erreurs ? Ca ne devrait normalement pas être à eux de le faire. Ils devraient remonter qu'il y a une erreur, et laisser une partie spécialisée du code traiter l'erreur.

Les exceptions à la rescousse

Les exceptions sont un moyen en programmation de gérer les erreurs. Vous en avez peut-être déjà vu dans du code PHP, ça ressemble à ça :

<?php
try {
    // Essayer de faire quelque chose
}
catch(Exception $e) {
    // Si une erreur se produit, on arrive ici
}

En premier lieu, l'ordinateur essaie d'exécuter les instructions qui se trouvent dans le bloc  try  ("essayer" en anglais). Deux possibilités :

  • Soit il ne se passe aucune erreur dans le bloc  try  : dans ce cas, on saute le bloc  catch  et on passe à la suite du code.

  • Soit une erreur se produit dans le bloc  try  : on arrête ce qu'on faisait et on va directement dans le  catch  (pour "attraper" l'erreur).

C'est par exemple ce qu'on fait ici pour se connecter à la base de données :

<?php

// Code avant

try {
    $db = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', 'root');
}
catch(Exception $e) {
    die('Erreur : '.$e->getMessage());
}

// Code après

On essaie de se connecter à la base de données dans le bloc  try . Si tout va bien, on continue (on va dans le "Code après"). Si en revanche il y a un souci lors de la connexion (à l'intérieur du  new PDO  ), alors on récupère l'erreur dans le bloc catch.

Pour générer une erreur, il faut "jeter une exception" (oui, on dit ça 😂 ). Dès qu'il y a une erreur quelque part dans votre code, dans une fonction par exemple, vous utiliserez cette ligne :

<?php
throw new Exception('Message d\'erreur à transmettre');

On va utiliser ce mécanisme dans notre code !

Ajout de la gestion d'exceptions dans le routeur

Je vous propose d'entourer tout notre routeur par un bloc try / catch comme ceci :

<?php
require('controller/frontend.php');

try { // On essaie de faire des choses
    if (isset($_GET['action'])) {
        if ($_GET['action'] == 'listPosts') {
            listPosts();
        }
        elseif ($_GET['action'] == 'post') {
            if (isset($_GET['id']) && $_GET['id'] > 0) {
                post();
            }
            else {
                // Erreur ! On arrête tout, on envoie une exception, donc au saute directement au catch
                throw new Exception('Aucun identifiant de billet envoyé');
            }
        }
        elseif ($_GET['action'] == 'addComment') {
            if (isset($_GET['id']) && $_GET['id'] > 0) {
                if (!empty($_POST['author']) && !empty($_POST['comment'])) {
                    addComment($_GET['id'], $_POST['author'], $_POST['comment']);
                }
                else {
                    // Autre exception
                    throw new Exception('Tous les champs ne sont pas remplis !');
                }
            }
            else {
                // Autre exception
                throw new Exception('Aucun identifiant de billet envoyé');
            }
        }
    }
    else {
        listPosts();
    }
}
catch(Exception $e) { // S'il y a eu une erreur, alors...
    echo 'Erreur : ' . $e->getMessage();
}

Comme vous pouvez le voir, à l'endroit où les erreurs se produisent j'ai mis des  throw new Exception  . Cela arrête le bloc  try  et amène directement l'ordinateur au bloc  catch  .

Ici, notre bloc  catch  se contente de récupérer le message d'erreur qu'on a transmis et de l'afficher.

Remontez les exceptions !

Pour l'instant, vous vous dites sûrement que ça n'est pas fou fou. Ok, les exceptions sont faites pour gérer les erreurs, mais on a surtout compliqué le code du routeur avec un nouveau bloc.

C'est parce que vous n'avez pas encore vu à quel point les exceptions peuvent être pratiques ! Quand il se passe une erreur à l'intérieur d'une fonction située dans le bloc  try , celle-ci est "remontée" jusqu'au bloc  catch .

Par exemple, notre routeur appelle la fonction le contrôleur   addComment , on est d'accord ? Et ce contrôleur appelle une autre fonction, le modèle. Que se passe-t-il quand il y a une erreur dans le modèle ? Pour l'instant, on fait ça :

<?php
// Fonction addComment du modèle
function addComment($postId, $author, $comment)
{
    $affectedLines = postComment($postId, $author, $comment);

    if ($affectedLines === false) {
        // Gestion de l'erreur à l'arrache
        die('Impossible d\'ajouter le commentaire !');
    }
    else {
        header('Location: index.php?action=post&id=' . $postId);
    }
}

Notre modèle arrête tout et affiche une erreur avec un  die . Il y a moyen de faire plus propre : jetons ici une exception, le code va s'arrêter là et l'erreur être remontée jusque dans le routeur qui contenait le bloc  try  !

Voilà comment on peux mieux gérer l'erreur, en ajoutant un  throw  :

<?php
function addComment($postId, $author, $comment)
{
    $affectedLines = postComment($postId, $author, $comment);

    if ($affectedLines === false) {
        // Erreur gérée. Elle sera remontée jusqu'au bloc try du routeur !
        throw new Exception('Impossible d\'ajouter le commentaire !');
    }
    else {
        header('Location: index.php?action=post&id=' . $postId);
    }
}

Pratique non ? Ce principe de "remontée" de l'erreur jusqu'à l'endroit du code qui contenait le bloc  try  est vraiment un gros avantage des exceptions.

Comme ce n'est pas forcément évident à "voir" comme ça, je vous ai résumé le concept dans un schéma (j'adore les schémas, je vous l'ai déjà dit ?) :

Lorsqu'une erreur survient dans une sous-fonction, elle est remontée jusqu'au bloc catch
Lorsqu'une erreur survient dans une sous-fonction, elle est remontée jusqu'au bloc catch

Améliorer la présentation de l'erreur

Pour l'instant, notre bloc  catch  affiche une erreur avec un simple  echo . Si nous voulons faire quelque chose de plus joli, nous pouvons appeler une vue  errorView.php  qui affiche joliment le message d'erreur.

Il faudrait faire quelque chose dans ce goût-là :

<?php
require('controller/frontend.php');

try {
    // ...
}
catch(Exception $e) {
    $errorMessage = $e->getMessage();
    require('view/errorView.php');
}

Bon là je vous laisse travailler la vue vous-mêmes, je pense que vous avez compris le concept !

Essayez !

https://www.codevolve.com/api/v1/publishable_key/2A9CAA3419124E3E8C3F5AFCE5306292?content_id=c2ca6ca8-1457-4fc2-be24-efddb20dcffd

Exemple de certificat de réussite
Exemple de certificat de réussite