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éé").