PHP: przechwytywanie wyjątków

#jedenasta37 Kiedy dane walidujemy i obsługujemy, a kiedy rzucamy wyjątkiem? Czy wyjątek to rzecz normalna, czy wkurw ostateczny? I co ma z tym wspólnego kasza manna?

Od razu zdradzę, że kasza manna i wyjątki są o tyle do siebie podobne, że jednego i drugiego nie lubiłam i nie używałam, aż do czasu. Dzisiaj nie wyobrażam sobie kodowania bez try {} catch (Exception $e) {} tak samo, jak pilnuję, żeby w kuchni zawsze kasza manna była – dzieci lubią 😉

Chrzanisz, mów co to try-catch!

Pisałam w ubiegłym tygodniu o rzucaniu wyjątków za pomocą

throw new Exception("Zabrakło kaszy manny");

i możnaby przy tym zostać, gdyby nie to, że każdy zgłoszony (in. rzucony) wyjątek powinien być w aplikacji obsłużony. Chciałam napisać, że musi być, ale prawda jest taka, że czasem nam im-onym-oczywiście-że-nie-nam nie wychodzi i wtedy wyjątek ląduje w przeglądarce użytkownika i robi bardzo złe wrażenie o firmie, której aplikację kodujemy. Do tego nie możemy dopuścić.

Globalne przechwytywanie wyjątków

Od razu powiem, że w poważnych aplikacjach na najwyższym poziomie sterowania i aplikacji, mamy try catcha, którzy przechwytuje wszystkie wyjątki globalnie i wypuszcza do przeglądarki jakąś ładną stronę błędu – dzięki temu unikamy białej kartki z napisem „Fatal error” okraszonym kilkoma szczegółami technicznymi:

Try {} catch (Exception $e) {}

Wróćmy teraz do przykładu z poprzedniego wpisu, krytykuję go od razu jako kod nie nadający się na produkcję i wyjaśnię, jaki byłby lepszy. 🙂 Przykład:

public function quotient($a, $b) {
    if($b == 0) {
        throw new Exception('Division by zero.');
    } 
    return $a / $b;
}

Przypomnę, że tutaj napisałam funkcję, która ma zwrócić wynik dzielenia jednej liczby przez drugą. Problem polega na tym, że dzielnik, czyli $b, nie może być równe 0, bo królowa nauk nie pozwala dzielić przez 0. Kiedy jednak spróbujesz wykonać w skrypcie dzielenie przez 0, interpreter sam rzuci wyjątkiem. W związku z powyższym moje throw new Exception z liniii trzeciej jest niepotrzebnym dublowaniem dobrze działającego mechanizmu. Moim zadaniem jest to wiedzieć. Skąd można wiedzieć – z dokumentacji, oczywiście. Sprawdź zawsze z czego korzystasz 🙂

Jeśli wiem, że jakaś instrukcja, którą piszę, może rzucić wyjątkiem, muszę użyć try-catch do przechwycenia możliwego wyjątku. Może się tak stać np. gdy łączę się z bazą danych – mogę po prostu nie uzyskać połączenia i dostać wyjątkiem w twarz.

Instrukcja try-catch w takiej podstawowej wersji wyglada następująco:

try {
    $a = 9;
    $b = 0;
    $quotient = quotient($a, $b);
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";
}

Wszystko, co znajduje się w bloku try (wiersze 2-4), objęte jest „łapaniem” – jeśli w tym bloku zostanie wyrzucony wyjątek, sterowanie i obiekt złapanego wyjątku, zostaną przekierowane do bloku catch, gdzie w linii 6 zostaje wyświetlona wiadomość z przechwyconego błędu. Oczywiście tak na produkcji nie wyświetlajcie, trzeba wymyślić coś bardziej user friendly.

Sterowanie zostanie przekazane do.. oznacza, że wykonanie skryptu w tym miejscu zostaje przerwane i dalej będzie się wykonywać to, co wymienione po słówku „do”. To często stosowane stwierdzenie, trzeba znać jego znaczenie 🙂 Tak, jakby komp (de facto interpreter) przeskakiwał do innej linii kodu. Właściwie przeskakuje, bez „tak, jakby”.

Obsługa błędu w bloku catch

W powyższym przykładzie blok catch wyświetla na ekranie zmienną message obiektu wyjątek. Oczywiście jest to kompletnie nieprzydatne działanie na produkcji, wręcz niewskazane. Możesz tego użyć we wczesnej fazie developmentu produktu, ale nie warto – warto od razu pisać prawidłowo. Uczymy się czystego kodu, a nie gównokodu.

Prawidłowo w bloku catch przede wszystkim dokonujemy logowania błędu!

Dlaczego? Logi to jest podstawa pracy programisty. Nie jesteś w stanie zgadnąć, co się stało w systemie. Setki, tysiące użytkowników, codziennie klikają w interfejs Twojej aplikacji, przechodzą masę różnych ścieżek – wystarczy, że jakikolwiek element nie odpowie tak, jakbyśmy chcieli, a nasz użytkownik nie uzyska tego, po co przyszedł. Jeśli przewidziałaś w systemie miejsca, gdzie może wystąpić nieoczekiwana sytuacja i elegancko ją obsługujesz, Twoja aplikacja jest dobra i stwarza szansę na to, że użytkownik jednak da radę. Kiedy jednak otrzymujesz zgłoszenie od klienta, że coś jest nie tak, pierwsze co robisz – sprawdzasz logi, żeby spróbować odtworzyć co się właściwie stało.

Co dalej?

Poza throw, try i catch, musimy powiedzieć jeszcze o przechwytywaniu wielu typów wyjątków oraz o tworzeniu własnych typów – mamy więc plan na dalsze artykuły 🙂 Ale tak sobie myślę, że za tydzień może powiemy znów o czymś mniej technicznym? Co myślisz?

#programujBoWarto !

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *