Vous ne devriez pas intercepter l'exception à moins que vous n'ayez l'intention de faire quelque chose de significatif .
"Quelque chose de significatif" peut être l'un de ces éléments:
Gérer l'exception
L'action significative la plus évidente est de gérer l'exception, par exemple en affichant un message d'erreur et en abandonnant l'opération:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}
Journalisation ou nettoyage partiel
Parfois, vous ne savez pas comment gérer correctement une exception dans un contexte spécifique; peut-être manquez-vous d'informations sur la «vue d'ensemble», mais vous voulez enregistrer l'échec le plus près possible du point où il s'est produit. Dans ce cas, vous souhaiterez peut-être attraper, enregistrer et relancer:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}
Un scénario connexe est celui où vous êtes au bon endroit pour effectuer un nettoyage de l'opération qui a échoué, mais pas pour décider comment l'échec doit être géré au niveau supérieur. Dans les versions antérieures de PHP, cela serait implémenté comme
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
catch (Exception $e) {
$connect->disconnect(); // we don't want to keep the connection open anymore
throw $e; // but we also don't know how to respond to the failure
}
PHP 5.5 a introduit le finally
mot - clé, donc pour les scénarios de nettoyage, il existe maintenant une autre façon d'aborder cela. Si le code de nettoyage doit s'exécuter quoi qu'il arrive (c'est-à-dire à la fois en cas d'erreur et de succès), il est maintenant possible de le faire tout en permettant de manière transparente à toutes les exceptions levées de se propager:
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}
Abstraction d'erreur (avec chaînage d'exceptions)
Un troisième cas est celui où vous souhaitez regrouper logiquement de nombreuses pannes possibles sous un plus grand parapluie. Un exemple de regroupement logique:
class ComponentInitException extends Exception {
// public constructors etc as in Exception
}
class Component {
public function __construct() {
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
throw new ComponentInitException($e->getMessage(), $e->getCode(), $e);
}
}
}
Dans ce cas, vous ne voulez pas que les utilisateurs de Component
sachent qu'il est implémenté à l'aide d'une connexion à une base de données (peut-être souhaitez-vous garder vos options ouvertes et utiliser le stockage basé sur des fichiers à l'avenir). Donc, votre spécification pour Component
dirait que "dans le cas d'un échec d'initialisation, ComponentInitException
sera jeté". Cela permet aux consommateurs d' Component
intercepter les exceptions du type attendu tout en permettant au code de débogage d'accéder à tous les détails (dépendants de l'implémentation) .
Fournir un contexte plus riche (avec chaînage d'exceptions)
Enfin, dans certains cas, vous souhaiterez peut-être fournir plus de contexte pour l'exception. Dans ce cas, il est judicieux d'encapsuler l'exception dans une autre qui contient plus d'informations sur ce que vous tentiez de faire lorsque l'erreur s'est produite. Par exemple:
class FileOperation {
public static function copyFiles() {
try {
$copier = new FileCopier(); // the constructor may throw
// this may throw if the files do no not exist
$copier->ensureSourceFilesExist();
// this may throw if the directory cannot be created
$copier->createTargetDirectory();
// this may throw if copying a file fails
$copier->performCopy();
}
catch (Exception $e) {
throw new Exception("Could not perform copy operation.", 0, $e);
}
}
}
Ce cas est similaire à celui ci-dessus (et l'exemple n'est probablement pas le meilleur que l'on puisse trouver), mais il illustre l'intérêt de fournir plus de contexte: si une exception est levée, cela nous indique que la copie du fichier a échoué. Mais pourquoi a- t-il échoué? Ces informations sont fournies dans les exceptions encapsulées (dont il pourrait y avoir plus d'un niveau si l'exemple était beaucoup plus compliqué).
La valeur de cette opération est illustrée si vous pensez à un scénario où, par exemple, la création d'un UserProfile
objet entraîne la copie de fichiers car le profil utilisateur est stocké dans des fichiers et prend en charge la sémantique des transactions: vous pouvez «annuler» les modifications car elles ne sont effectuées que sur un copie du profil jusqu'à ce que vous vous engagiez.
Dans ce cas, si vous l'avez fait
try {
$profile = UserProfile::getInstance();
}
et en conséquence a attrapé une erreur d'exception «Le répertoire cible n'a pas pu être créé», vous auriez le droit d'être confus. Emballer cette exception "principale" dans des couches d'autres exceptions qui fournissent du contexte rendra l'erreur beaucoup plus facile à traiter ("La création de la copie du profil a échoué" -> "L'opération de copie du fichier a échoué" -> "Le répertoire cible n'a pas pu être créé").
finally
construction (pas encore du moins) ... Donc c'estfinally
de PHP.error_code
propriété qui peut être vérifiée pour obtenir l'erreur sous-jacente code. Si vous ne pouvez gérer de manière significative que certaines de ces erreurs, vous voudrez probablement attraper, inspecter et, si vous ne pouvez pas gérer l'erreur, renvoyer.Eh bien, il s'agit de maintenir l'abstraction. Je suggère donc d'utiliser le chaînage d'exceptions pour lancer directement. Quant à savoir pourquoi, laissez-moi vous expliquer le concept d' abstractions qui fuient
Disons que vous construisez un modèle. Le modèle est censé éliminer toutes les données de persistance et de validation du reste de l'application. Alors maintenant, que se passe-t-il lorsque vous obtenez une erreur de base de données? Si vous relancez le
DatabaseQueryException
, vous perdez l'abstraction. Pour comprendre pourquoi, pensez à l'abstraction pendant une seconde. Vous ne vous souciez pas de la façon dont le modèle stocke les données, mais simplement de ce qu'il fait. De même, vous ne vous souciez pas exactement de ce qui n'a pas fonctionné dans les systèmes sous-jacents du modèle, mais simplement du fait que vous savez que quelque chose s'est mal passé, et à peu près ce qui n'a pas fonctionné.Ainsi, en renvoyant l'exception DatabaseQueryException, vous divulguez l'abstraction et exigez que le code appelant comprenne la sémantique de ce qui se passe sous le modèle. Au lieu de cela, créez un générique
ModelStorageException
et enveloppez le capturé à l'DatabaseQueryException
intérieur de celui-ci. De cette façon, votre code d'appel peut toujours essayer de traiter l'erreur sémantiquement, mais la technologie sous-jacente du modèle n'a pas d'importance, car vous n'exposez que les erreurs de cette couche d'abstraction. Mieux encore, puisque vous avez encapsulé l'exception, si elle bouillonne complètement et doit être journalisée, vous pouvez tracer jusqu'à l'exception racine lancée (parcourir la chaîne) afin que vous ayez toujours toutes les informations de débogage dont vous avez besoin!Ne vous contentez pas d'attraper et de renvoyer la même exception à moins que vous n'ayez besoin de faire un post-traitement. Mais un bloc comme ça ne
} catch (Exception $e) { throw $e; }
sert à rien. Mais vous pouvez reconditionner les exceptions pour un gain d'abstraction significatif.la source
IMHO, attraper une exception pour simplement la relancer est inutile . Dans ce cas, ne l'attrapez pas et laissez les méthodes appelées plus tôt le gérer ( c'est -à-dire les méthodes «supérieures» dans la pile d'appels) .
Si vous la relancez, enchaîner l'exception capturée dans la nouvelle que vous lancerez est certainement une bonne pratique, car elle conservera les informations contenues dans l'exception capturée. Cependant, le renvoyer n'est utile que si vous ajoutez des informations ou gérez quelque chose à l'exception interceptée, que ce soit un contexte, des valeurs, une journalisation, la libération de ressources, peu importe.
Une façon d'ajouter des informations est d'étendre la
Exception
classe, d'avoir des exceptions commeNullParameterException
,DatabaseException
, etc. Plus sur, cela permet au developpeur attraper seulement quelques exceptions près qu'il peut gérer. Par exemple, on peut attraper seulementDatabaseException
et essayer de résoudre ce qui a causé leException
, comme la reconnexion à la base de données.la source
Vous devez jeter un œil aux meilleures pratiques d'exception en PHP 5.3
La gestion des exceptions en PHP n'est en aucun cas une nouvelle fonctionnalité. Dans le lien suivant, vous verrez deux nouvelles fonctionnalités de PHP 5.3 basées sur les exceptions. Le premier est les exceptions imbriquées et le second est un nouvel ensemble de types d'exceptions offerts par l'extension SPL (qui est maintenant une extension principale du runtime PHP). Ces deux nouvelles fonctionnalités ont trouvé leur place dans le livre des meilleures pratiques et méritent d'être examinées en détail.
http://ralphschindler.com/2010/09/15/exception-best-practices-in-php-5-3
la source
Vous y pensez généralement de cette façon.
Une classe peut lever de nombreux types d'exceptions qui ne correspondent pas. Vous créez donc une classe d'exception pour cette classe ou ce type de classe et la lancez.
Ainsi, le code qui utilise la classe n'a qu'à attraper un type d'exception.
la source