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 :
Les développeurs ont en particulier du mal à gérer comme ça les erreurs qui ont lieu à l'intérieur des fonctions.
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 :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 :
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.
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());
}
// ...
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');
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);
}
}
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');
}