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.
Remontez un cas d'erreur avec le mot clé return
Voyons ensemble comment PHP gère les erreurs à travers cette vidéo. :)
Remontez un cas d'erreur avec le mot clé throw
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.
Attrapez les exceptions pour mieux les traiter
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.
Les exceptions natives de PHP
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. 😎
Créez des exceptions personnalisées
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 !
Exercez-vous
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.
En résumé
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 bloctry...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.