Easy-Micro

LANGAGE MVC PHP
Gérer les erreurs

Utilisez une architecture MVC en PHP : Gérer les erreurs

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 qu'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';
}

Les développeurs ont en particulier du mal à gérer comme ça les erreurs qui ont lieu à l'intérieur des fonctions.

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
}

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

// ...

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

// ...
On affiche l'erreur qui nous a été envoyée avec $e->getMessage()

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');

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

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.

Ajout de la gestion d'exceptions dans le routeur

Je vous propose d'entourer tout notre routeur par un bloc try / catch comme ceci :
index.php
<?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();
}

Remontez les exceptions !

Le contrôleur ci-dessous 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 :

controller/frontend.php
<?php
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  :

controller/frontend.php
<?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);
    }
}

Du coup, dans la fonction dbConnect() de notre modèle, il n'est plus forcément nécessaire de garder un bloc try/catch. L'erreur de connexion à la base, s'il y en a, sera remontée jusqu'au routeur :

model/frontend.php :
<?php 
function dbConnect() {
    $db = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', 'root');
    return $db;
}

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');
}

> Voir cet exemple Easy-Micro

« Prev - Architecture MVC en PHP - Next »


< Page précédente MVC PHP Page suivante >