
Un bon programme ne plante jamais. Ça ne veut pas dire qu'il ne peut pas y avoir d'erreur, au contraire, ça arrive tout le temps ! Que ce soit un problème de saisie par un utilisateur ou un mauvais usage par un développeur.
L'essentiel est que le programme soit conçu pour réagir correctement en cas de mauvais usage ou de comportement inattendu. Avec un langage compilé, c'est assez facile à repérer, mais avec un langage interprété comme PHP, et dynamique de surcroît, alors c'est là qu'il faut vraiment se méfier.
Voyons ensemble comment PHP gère les erreurs à travers cette vidéo. :)
Au lieu de gérer les cas d'erreurs avec des booléens, on va jeter une exception qui s'accompagne d'un message et qui peut s'accompagner d'un code d'erreur. Pour cela, il faut utiliser le mot clé throw.
<?php
/**
* @var string $text le contenu du message
* @return bool true en cas de succès
* @throw Exception on error
*/
function sendEmail(string $text): bool
{
if (/*on simule que l’envoie du message réussie*/ false)
{
// l’exception jetée avec son message et son code d’erreur
throw new Exception('L\'envoi du mail a échoué', '22eb3737-3f43-497e-9912-a737975072ea');
}
return true;
}
/**
* @var string $text le contenu du message
* @return bool true en cas de succès
* @throw Exception on error
*/
function sendNotification(string $text): bool
{
if (/*on simule que l’envoie de notification échoue*/ true)
{
throw new Exception('L\'envoi de la notification a échoué', 'f3259929-61b2-44d0-a3d6-b855890c0726');
}
return true;
}
/**
* @var string $text le contenu du message
* @return bool true en cas de succès
* @throw Exception on error
*/
function sendMessage(string $text): bool
{
if (10 > strlen($text)) {
throw new Exception('Le texte est trop court', '02bc3998-b9ff-431e-9058-8ab333ff7742');
}
sendEmail($text);
sendNotification($text);
return true;
}
if (!sendMessage('Hello, ici Greg "pappy" Boyington')) {
// Avec les Exceptions, en cas d’erreur, ce code n’est plus jamais appelé contrairement à avant, avec l’envoie de booléen.
}Que remarquez-vous ? Nous instancions des objets Exception avec un message et un code d'identification unique, pour qu'un programme puisse les distinguer.
Encore plus intéressant, regardez la fonction sendMessage ! J'ai déplacé l'appel des méthodes sendEmail et sendNotification indépendamment de la vérification de longueur.
Pour attraper une exception, il faut utiliser une nouvelle structure : try { ... } catch (Exception $e) { ... }.
Entre les accolades du try, on va mettre le code qui potentiellement peut jeter une exception.
catch se comporte comme une fonction, car entre parenthèses on mettra un argument typé Exception, qui sera appelé en cas d’exception lancée dans le try.
Dans les accolades du catch, le code à exécuter en cas d'interception d'une exception, généralement un moyen de traiter l'erreur reçue.
Voyons ensemble comment intercepter les exceptions à travers cette vidéo.
On commence à avoir une belle gestion des erreurs, mais on peut faire encore mieux ! Exception est une classe, donc... on peut l'étendre.
Dans PHP, par défaut il n'existe que la classe Exception . MAIS la librairie SPL (la même que pour le chargement automatique), qui est embarquée avec PHP, en propose d'autres. :)
Elle propose 2 classes qui étendent Exception : LogicException et RuntimeException. Ainsi qu'une dizaine d'autres qui découlent de ces deux-ci, que vous pouvez voir dans la documentation PHP.
Alors pourquoi faire, déjà ?
Pour spécialiser votre gestion d'erreur à la manière de la programmation orientée objet ! Au lieu d'avoir un affreux if...else dans notre code, try...catch propose une structure permettant d'identifier la classe d'exception utilisée.
Lorsque vous voulez expliquer à un développeur qu'il n'a pas correctement utilisé le code, et qu'il doit effectuer un changement dans son code, alors vous devez utiliser une LogicException. Si vous voulez exprimer une erreur suite à une saisie utilisateur, qui ne pourrait pas être détectée autrement que durant l’exécution, et donc qu'un simple message d'explication suffit, alors vous devrez utiliser une RuntimeException.
Vous savez ce qui serait encore mieux que d'utiliser ces deux exceptions ? Utiliser nos propres exceptions. 😎
En reprenant nos précédents exemples, nous pourrions remplacer les exceptions par des classes dédiées. Lorsque l'on va attraper les exceptions, plutôt que de regarder le code associé, on va demander à catch de regarder la classe de l'exception obtenue.
Nous avons 3 exceptions à créer :
une en cas d'erreur d'envoi de mail ;
une en cas d'erreur d'envoi de notification ;
et une en cas de texte trop court.
Dans les 2 premiers cas, l'utilisateur ne peut rien y faire. Est-ce qu'il s'agit donc d'une LogicException ? Eh non, c'est un piège. Parce qu'ici le développeur ne peut rien y faire non plus. Nous sommes donc pour chacune dans le domaine des RuntimeException.
<?php
class EmailSendingErrorException extends RuntimeException
{
public $message = 'Impossible d\'envoyer l\'email.';
}
class NotificationSendingErrorException extends RuntimeException
{
public $message = 'Impossible d\'envoyer la notification.';
}
class ShortText extends RuntimeException
{
public $message = 'Le texte fourni est trop court.';
}C'est d'ailleurs très pratique de pouvoir y insérer un message par défaut. Il est temps de remplacer le code avec nos exceptions !
<?php
class EmailSendingErrorException extends RuntimeException
{
public $message = 'Impossible d\'envoyer l\'email.';
}
class NotificationSendingErrorException extends RuntimeException
{
public $message = 'Impossible d\'envoyer la notification.';
}
class ShortText extends RuntimeException
{
public $message = 'Le texte fourni est trop court.';
}
/**
* @var string $text le contenu du message
* @return bool true en cas de succès
* @throw Exception on error
*/
function sendEmail(string $text): bool
{
if (/*envoie du message échoue*/ true)
{
throw new EmailSendingErrorException();
}
return true;
}
/**
* @var string $text le contenu du message
* @return bool true en cas de succès
* @throw Exception on error
*/
function sendNotification(string $text): bool
{
if (/*envoie de notification échoue*/ true)
{
throw new NotificationSendingErrorException();
}
return true;
}
/**
* @var string $text le contenu du message
* @return bool true en cas de succès
* @throw Exception on error
*/
function sendMessage(string $text): bool
{
if (10 > strlen($text)) {
throw new ShortTextException();
}
try {
sendNotification($text);
} catch (NotificationSendingErrorException $e) {
// Envoyez vous une alerte
// pour vous prévenir que les notifications ne marche pas ;)
} finally {
// finally permet d'exécuter du code quoi qu'il arrive :)
sendEmail($text);
// si une exception est jetée par sendEmail,
// Le return n'est jamais exécuté
return true;
}
}
try {
sendMessage('Hello, ici Greg "pappy" Boyington');
} catch (ShortTextException $e) {
echo $e->message;
} catch (EmailSendingErrorException $e) {
echo 'Une erreur est survenue lors de l\'envoi du message, nos équipes ont été prévenues, veuillez réessayer plus tard';
} catch (Exception $e) {
echo 'Une erreur inattendue est survenue, nos équipes ont été prévenues, veuillez réessayer plus tard';
}C'est pas top, ça ? On peut enchaîner les catch pour gérer les différents comportements maîtrisés et gérer les cas généraux. C'est à vous maintenant – utilisez des Exceptions dans le code fourni ci-dessous !
Dans cet exercice, je vous ai créé une codebase rassemblant tous les concepts que nous avons vus ensemble durant le cours POO. Je vous encourage d’ailleurs à l’étudier.
Cependant, elle n’utilise pas encore les Exceptions 😱 !
Pour faciliter la gestion d'erreur, remplacez les usages de la fonction trigger_error déjà présente dans le code, par l'usage des exceptions. Vous créerez un répertoire Exceptions dans le répertoire src. Et à l'intérieur, vous créerez vos exceptions personnalisées.
Examinez le code et placez des try...catch aux endroits qui vous semblent importants.
Le code se trouve sur la branche P3C6, et la correction sur la branche P3C6-correction.
PHP permet de gérer les erreurs avec les classes Exception en faisant remonter l'erreur à travers toute la pile d'exécution, grâce au mot clé throw.
Les Exceptions offrent plus de finesse pour la gestion des erreurs avec le bloc try...catch.
Créer vos exceptions vous donne plus de clarté dans votre code et dans le traitement des erreurs.
Il est possible d’enchaîner les catch pour gérer les différents types d’erreurs.
Le bloc finally placé après le(s) bloc(s) catch permet d’exécuter du code, qu’il y ait eu des erreurs, ou non.
Vous voilà maintenant avec toutes les clés en main pour faire de la programmation orientée objet ! Avant de valider tout ce que vous avez appris dans cette partie dans le dernier quiz, repassons sur ce que nous avons vu ensemble dans le chapitre suivant.