Nie powinieneś łapać wyjątku, chyba że masz zamiar zrobić coś znaczącego .
„Coś znaczącego” może być jednym z tych:
Obsługa wyjątku
Najbardziej oczywistą sensowną akcją jest obsłużenie wyjątku, np. Wyświetlenie komunikatu o błędzie i przerwanie operacji:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}
Rejestrowanie lub częściowe czyszczenie
Czasami nie wiesz, jak prawidłowo obsłużyć wyjątek w określonym kontekście; być może brakuje Ci informacji o „ogólnym obrazie”, ale chcesz zarejestrować awarię jak najbliżej miejsca, w którym się wydarzyła. W takim przypadku możesz chcieć złapać, zarejestrować i ponownie rzucić:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}
Powiązany scenariusz polega na tym, że jesteś we właściwym miejscu, aby wykonać pewne porządki dla operacji zakończonej niepowodzeniem, ale nie zdecydować, jak awaria powinna zostać rozwiązana na najwyższym poziomie. We wcześniejszych wersjach PHP byłby on zaimplementowany jako
$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 wprowadziło finally
słowo kluczowe, więc dla scenariuszy czyszczenia jest teraz inny sposób podejścia do tego. Jeśli kod czyszczący musi działać bez względu na to, co się stało (tj. Zarówno w przypadku błędu, jak i sukcesu), można to teraz zrobić, jednocześnie zezwalając na propagację wszelkich rzuconych wyjątków:
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}
Abstrakcja błędów (z łączeniem wyjątków)
Trzeci przypadek polega na tym, że chcesz logicznie pogrupować wiele możliwych awarii w ramach większego parasola. Przykład logicznego grupowania:
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);
}
}
}
W takim przypadku nie chcesz, aby użytkownicy Component
wiedzieli, że jest on zaimplementowany przy użyciu połączenia z bazą danych (być może chcesz mieć otwarte opcje i korzystać z magazynu opartego na plikach w przyszłości). Więc twoja specyfikacja dla Component
mówi, że "w przypadku niepowodzenia inicjalizacji, ComponentInitException
zostanie wyrzucony". Pozwala to konsumentom Component
na wyłapywanie wyjątków oczekiwanego typu jednocześnie umożliwiając kodowi debugowania dostęp do wszystkich (zależnych od implementacji) szczegółów .
Zapewnienie bogatszego kontekstu (z łączeniem wyjątków)
Wreszcie istnieją przypadki, w których możesz chcieć podać więcej kontekstu dla wyjątku. W takim przypadku sensowne jest zawinięcie wyjątku w inny, który zawiera więcej informacji o tym, co próbujesz zrobić, gdy wystąpił błąd. Na przykład:
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);
}
}
}
Ten przypadek jest podobny do powyższego (a przykład prawdopodobnie nie jest najlepszy, jaki można wymyślić), ale ilustruje cel podania szerszego kontekstu: jeśli zostanie zgłoszony wyjątek, informuje nas, że kopiowanie pliku nie powiodło się. Ale dlaczego się nie udało? Ta informacja jest dostarczana w opakowanych wyjątkach (których może być więcej niż jeden poziom, gdyby przykład był znacznie bardziej skomplikowany).
Wartość zrobienia tego jest zilustrowana, jeśli myślisz o scenariuszu, w którym np. Tworzenie pliku UserProfile
obiektu powoduje kopiowanie plików, ponieważ profil użytkownika jest przechowywany w plikach i obsługuje semantykę transakcji: możesz „cofnąć” zmiany, ponieważ są one wykonywane tylko na kopia profilu do momentu zatwierdzenia.
W tym przypadku, jeśli tak
try {
$profile = UserProfile::getInstance();
}
i w wyniku złapania błędu wyjątku „Nie można utworzyć katalogu docelowego”, miałbyś prawo być zdezorientowanym. Umieszczenie tego „podstawowego” wyjątku w warstwach innych wyjątków, które zapewniają kontekst, znacznie ułatwi rozwiązanie błędu („Tworzenie kopii profilu nie powiodło się” -> „Operacja kopiowania pliku nie powiodła się” -> „Nie można utworzyć katalogu docelowego”).