III. Exceptions ou erreurs ?▲
Dans ce paragraphe, nous allons comparer les 2 systèmes (exceptions/erreurs). En fait, nous allons nous concentrer sur les exceptions qui est le sujet principal, car on remarquera vite que les inconvénients de l'un sont les avantages de l'autre et réciproquement.
III-A. Avantages des exceptions▲
III-A-1. Lisibilité▲
Comme je l'ai déjà écrit plusieurs fois, les exceptions ne sont pas identiques aux erreurs dans le sens ou elles représentent un fonctionnement normal, mais exceptionnel. Nous allons maintenant voir un exemple dans lequel les exceptions nous aident grandement et montrent clairement que le mécanisme de gestion d'erreurs n'a rien à voir là-dedans. Considérons une page de traitement de formulaire d'inscription à un site. Nous avons le pseudo, le mot de passe et la confirmation ainsi que l'adresse mail qui arrivent du formulaire. Il faudra vérifier que le pseudo n'existe pas, que le mot de passe et la confirmation sont identiques et que le mail est correct.
On pourrait y arriver avec une avalanche de if, mais ça deviendrait vite suffisamment compliqué pour qu'une erreur s'y glisse. Par exemple :
$result
=
mysql_query("
SELECT
1
FROM
membres
WHERE
login
=
'
"
.
mysql_real_escape_string($_POST
[
'
login
'
]
).
"
'
"
);
if (mysql_num_rows($result
) =
=
0)
{
//
le
pseudo
n'est
pas
pris,
on
continue
if ($_POST
[
'
pass
'
]
=
=
$_POST
[
'
confirmation
'
]
)
{
//
Le
mot
de
passe
et
la
confirmation
coïncident
if (isValide($_POST
[
'
mail
'
]
))
{
//
Le
mail
est
valide,
on
insère
le
mebre
insere_membre($_POST
[
'
login
'
]
,
$_POST
[
'
pass
'
]
,
$_POST
[
'
mail
'
]
);
}
else
{
affiche_formulaire('
mail_invalide
'
);
}
}
//
Le
mot
de
passe
et
la
confirmation
ne
coïncident
pas
else
{
affiche_formulaire('
error_mdp
'
);
}
}
//
Le
pseudo
est
pris,
on
affiche
le
formulaire
else
{
affiche_formulaire('
error_login
'
);
}
Imaginez si on doit effectuer une vérification sur 200 champs de fomulaire. C'est sincèrement ingérable à cause de la profondeur des if.
Grâce aux exceptions, on ne s'enfonce plus dans une telle profondeur de if, et on n'appelle plus qu'une seule fois la fonction affiche_formulaire. Ainsi, le code précédent devient :
try
{
$result
=
mysql_query("
SELECT
1
FROM
membres
WHERE
login
=
'
"
.
mysql_real_escape_string($_POST
[
'
login
'
]
).
"
'
"
);
if (mysql_num_rows($result
) >
0)
{
throw new Exception('
error_login
'
);
}
if ($_POST
[
'
pass
'
]
!
=
$_POST
[
'
confirmation
'
]
)
{
throw new Exception('
error_mdp
'
);
}
if ( !
isValide($_POST
[
'
mail
'
]
))
{
throw new Exception('
mail_invalide
'
);
}
}
catch(Exception $myException
)
{
affiche_formulaire($myException
-
>
getMessage());
}
III-A-2. Fiabilité▲
Les exceptions permettent de ne pas stopper net un système ayant généré une erreur. Ceci permet d'effectuer un traitement adapté au problème rencontré, voire parfois de le réparer.
Ainsi, certains langages comme l'ADA ont intégré entièrement les exceptions dans leur langage pour gérer les erreurs liées au système. C'est pourquoi l'ADA est très utilisé en milieu militaire et aérospatial. En effet, même dans ces systèmes coutânt des milliards, il y a des bugs, des erreurs...
Il vaut donc mieux être sûr que même en cas d'erreur, le système ne va pas simplement s'arréter.
Java, bien que possédant une gestion des erreurs, est très orienté exception, ce qui a contribué (entre autre) à son succès dans le monde industriel. Une classe Java annonce quelles exceptions elle lance. Ainsi, un code source Java ne traitant pas ces exceptions n'est simplement pas compilé.
Tous les autres langages utilisent soit les erreurs uniquement, soit un répartition équilibrée entre exception et erreur.
Evidemment, pour augmenter réellement la fiabilité des systèmes, il est important de bien réfléchir à la cohérence des classes d'exception et de bien les capturer. Sinon, le système ne correspondra qu'à une gestion des erreurs traditionnelles.
III-B. Inconvénients des Exceptions▲
III-B-1. Implémentation "partielle"▲
Dans PHP5, le mécanisme d'exception est totalement opérationnel, mais ne constitue qu'un apport pour le développeur et non pour le langage. C'est à dire que PHP5 n'utilise pas les exceptions. L'ensemble des fonctions de PHP génèrent toujours des erreurs PHP lorsqu'elles rencontrent un problème, seules quelques extensions ont adopté le modèle des exceptions. Cette faible présence des exceptions dans PHP5 s'explique par :
- La programmation objet en PHP n'a de réel sens que depuis PHP5 or, pour utiliser au mieux les exceptions, il est nécessaire pour un langage de possèder un bon modèle objet.
- Lorsqu'une exception est soulevée, le contexte local contenu dans un bloc try est sauvegardé, ceci peut être amplifié par l'imbrication de bloc try. L'abus de l'utilisation des exceptions peut donc diminuer considérablement les performances.
- Par choix. La plupart des langages choisissent de ne pas supprimer la gestion des erreurs au profit des exceptions pour des raisons de performances et de style de programmation (les exceptions sont équivalentes pour certains programmateurs à l'affreux goto, de plus les exceptions augmentent considérablement le nombre de lignes de code). D'autres langages, comme l'ADA, n'utilisent que les exceptions.
III-B-2. Boucle infinie▲
Les exceptions permettent un traitement personnalisé d'une erreur. Imaginons un système d'exception unique pour tout un script, que le développeur utilise pour fiabiliser l'accès à un fichier, la connexion à une BDD... Dans cette classe d'exception, il décide de logguer toutes les exceptions soulevées afin de garder une trace. Pour cela, il utilise une BDD. Si la première connexion à la base de donnée ne se fait pas, on soulève une exception, qui tentera de se connecter à la base de donnée, ce qui soulèvera une exception... On obtient des comportements plutôt imprévus, pouvant aller jusqu'à la boucle infinie si le script implémente la fonction set_exception_handler. Il est donc très important d'éviter ce genre de scénario. La meilleure méthode pour éviter ceci est de bien distinguer les différentes classes d'exception dont le script a besoin. En créant ici une classe d'Exception spécifique à la base de données, on aurait forcement compris que l'on ne peut pas utiliser la base de données, vu que l'appel même de l'exception signifie que la connexion à la base de données a échoué. On utilisera alors un fichier. De même, on logguera les exceptions soulevées lors d'un accès fichier dans la BDD et non dans un fichier.
En réalité, PHP empêche ceci. Si il détecte une exception lancée à l'intérieur du traitement d'une autre exception (que ce soit avec catch ou set_exception_handler), il stoppe l'éxecution et affichera un message d'erreur. Ainsi, le code suivant :
<?php
class
MyException extends
Exception
{
public
function
__construct
($msg
=
null
,
$code
=
0
)
{
parent
:
:
__construct
($msg
,
$code
);
}
//
3
)
La
fonction
addLog
va
essayer
de
se
connecter
à
MySQL,
or
cette
fonction
est
//
justement
appelée
car
une
précédente
tentative
de
connexion
a
échouée.
public
function
addLog()
{
//
La
tentative
échoue
encore
if
( !
@
mysql_connect($server
,
$user
,
$pass
) )
{
//
4)
On
lance
une
exception
non
capturée
(on
pourait
la
capturer,
//
l'erreur
serait
identique)
throw
new
MyException('
Impossible
de
se
connecter
à
MySQL
dans
la
classe
MyException
'
);
}
}
}
//
5
)
On
attrape
l'exception
lancée
dans
la
méthode
addLog
//
et
on
ressaie
d'insérer
un
log...
//
On
voit
ici
la
boucle
infinie
qui
se
crée
entre
l'appel
de
//
cette
fonction
exception_handler
et
la
méthode
addLog.
function
exception_handler($myException
)
{
$myException
-
>
addLog();
}
set_exception_handler('
exception_handler
'
);
//
1)
Le
commencement,
on
ouvre
une
connexion
à
MySQL,
qui bien sûr ne va pas fonctionner
try
{
if
( !
@
mysql_connect($server
,
$user
,
$pass
) )
{
//
On
lance
donc
l'exception
throw
new
MyException('
Impossible
de
se
connecter
à
Mysql
dans
le
script
'
);
}
}
//
2)
On
capture
l'exception
et
on
applique
le
traitement,
//
c'est
à
dire
insérer
un
log
catch
(MyException $e
)
{
$e
-
>
addLog();
}
?>
Affichera l'erreur suivante.
Fatal error: Exception thrown without a stack frame in Unknown on line 0
Pour résumer : il faut bien être conscient de la cause d'une exception, de façon à ne pas appeler de nouveau dans son traitement la portion de code responsable de sa levée. Le mieux est d'établir une hiérarchie des classes d'exception. Par exemple, si on lance une exception dans le script, le traitement pourra tenter d'écrire dans la base de données, s'il n'y parvient pas, il lance une exception qui tentera d'écrire dans un fichier. Si ce traitement est aussi un échec, une nouvelle exception tentera simplement d'afficher à l'ecran, si rien ne fonctionne, on lance une erreur simple, ou on ne fait rien du tout. Le meilleur moyen d'éviter cette erreur consiste à limiter les opérations à risque dans le code de traitement des exceptions.