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”).
finally
konstrukcji (przynajmniej jeszcze nie) ... Więc to już koniec, co oznacza, że musimy uciekać się do brudnych rzeczy, takich jak ta ...finally
w PHP nie ma .error_code
właściwością, którą można sprawdzić, aby uzyskać podstawowy błąd kod. Jeśli jesteś w stanie sensownie obsłużyć tylko niektóre z tych błędów, prawdopodobnie chcesz wyłapać, sprawdzić, a jeśli nie możesz sobie poradzić z błędem - powtórz.Cóż, chodzi o utrzymanie abstrakcji. Więc sugerowałbym użycie łańcucha wyjątków do bezpośredniego rzucania. Jeśli chodzi o przyczyny, pozwólcie mi wyjaśnić pojęcie nieszczelnych abstrakcji
Powiedzmy, że tworzysz model. Model ma oddzielić całą trwałość i walidację danych od reszty aplikacji. Więc co się teraz dzieje, gdy pojawia się błąd bazy danych? Jeśli ponownie wrzucisz
DatabaseQueryException
, przeciekasz abstrakcję. Aby zrozumieć dlaczego, pomyśl przez chwilę o abstrakcji. Nie obchodzi cię, jak model przechowuje dane, po prostu to robi. Podobnie nie obchodzi Cię dokładnie, co poszło nie tak w podstawowych systemach modelu, po prostu wiesz, że coś poszło nie tak iw przybliżeniu, co poszło nie tak.Tak więc, ponownie wprowadzając wyjątek DatabaseQueryException, przeciekasz abstrakcję i wymagasz od kodu wywołującego zrozumienia semantyki tego, co dzieje się w modelu. Zamiast tego utwórz rodzaj ogólny
ModelStorageException
i zawiń jegoDatabaseQueryException
wnętrze. W ten sposób kod wywołujący może nadal próbować poradzić sobie z błędem semantycznie, ale nie ma znaczenia podstawowa technologia modelu, ponieważ ujawniasz tylko błędy z tej warstwy abstrakcji. Jeszcze lepiej, ponieważ zawinąłeś wyjątek, jeśli jest on bąbelkowy do góry i musi zostać zarejestrowany, możesz prześledzić zgłoszony wyjątek główny (przejść przez łańcuch), aby nadal mieć wszystkie potrzebne informacje dotyczące debugowania!Nie przechwytuj i nie wyrzucaj ponownie tego samego wyjątku, chyba że musisz wykonać trochę przetwarzania końcowego. Ale taki blok
} catch (Exception $e) { throw $e; }
jest bezcelowy. Ale możesz ponownie opakować wyjątki, aby uzyskać znaczący wzrost abstrakcji.źródło
IMHO, przechwytywanie wyjątku, aby go po prostu wyrzucić, jest bezużyteczne . W takim przypadku po prostu go nie wychwytuj i pozwól, aby wcześniej wywołane metody sobie z tym poradziły (czyli metody, które są „górne” na stosie wywołań) .
Jeśli wyrzucisz go ponownie, łańcuch przechwyconego wyjątku do nowego, który wyrzucisz, jest zdecydowanie dobrą praktyką, ponieważ zachowa informacje, które zawiera przechwycony wyjątek. Jednak ponowne przesłanie jest przydatne tylko wtedy, gdy dodasz jakieś informacje lub obsłużysz coś do przechwyconego wyjątku, może to być jakiś kontekst, wartości, logowanie, zwolnienie zasobów, cokolwiek.
Sposób, aby dodać trochę informacji jest rozszerzenie
Exception
klasy, aby mieć jak wyjątkiNullParameterException
,DatabaseException
itp Więcej na ten pozwoli developper tylko złapać kilka wyjątków, które potrafi obsłużyć. Na przykład, można tylko złapaćDatabaseException
i spróbować rozwiązać przyczynęException
, na przykład ponowne połączenie się z bazą danych.źródło
Musisz przyjrzeć się najlepszym praktykom dotyczącym wyjątków w PHP 5.3
Obsługa wyjątków w PHP nie jest nową funkcją w żadnym wydaniu. W poniższym linku zobaczysz dwie nowe funkcje w PHP 5.3 oparte na wyjątkach. Pierwszy to zagnieżdżone wyjątki, a drugi to nowy zestaw typów wyjątków oferowanych przez rozszerzenie SPL (które jest teraz podstawowym rozszerzeniem środowiska uruchomieniowego PHP). Obie te nowe funkcje znalazły się w księdze najlepszych praktyk i zasługują na szczegółowe zbadanie.
http://ralphschindler.com/2010/09/15/exception-best-practices-in-php-5-3
źródło
Zwykle myślisz o tym w ten sposób.
Klasa może generować wiele typów wyjątków, które nie będą pasować. Więc tworzysz klasę wyjątku dla tej klasy lub typu klasy i rzucasz to.
Zatem kod, który używa tej klasy, musi wychwycić tylko jeden typ wyjątku.
źródło