Wyjątki są lepszą alternatywą dla obsługi błędów w aplikacjach od wywoływania trigger_error
czy zwracania jakiegoś umówionego kodu w wypadku wystąpienia błędu. W związku z tym w ramach kontynuacji wątku z poprzedniego wpisu dziś kilka słów na temat wyjątków w PHP z nastawieniem na obsługę niewyłapanych przez catch
wyjątków.
Obsługa wyjątków
Klasyczne podejście do obsługi wyjątków zakłada użycie throw
i try
/catch
(od PHP 5.5 można również używać finally
). Może to wyglądać w taki sposób:
<?php $id = 1; // kod wykonany bez wzgledu na wystapienie wyjatku echo 'punkt 1' . PHP_EOL; try { echo 'punkt 2' . PHP_EOL; // kod wykonany bez wzgledu na wystapienie wyjatku if (is_int($id)) { echo 'punkt 3' . PHP_EOL; // zrob cos dla podanego ID } else { echo 'punkt 4' . PHP_EOL; throw new Exception('Błędne ID'); } echo 'punkt 5' . PHP_EOL; // kod wykonany TYLKO jezeli nie bylo wyjatku } catch (Exception $ex) { echo 'punkt 6' . PHP_EOL; // kod wykonany tylko w razie wystapienia wyjatku // np. "uratowanie zaistniałej sytuacji", zapis do logu, e-mail do admina } echo 'punkt 7' . PHP_EOL; // kod wykonany bez wzgledu na wystapienie wyjatku
Dla wartości $id = 1;
przebieg będzie wyglądał następująco, punkty: 1, 2, 3, 5, 7. Natomiast dla $id
nie będącego liczbą całkowitą wykonanie podąży ścieżką: 1, 2, 4, 6, 7.
Powyższy przykład jest mocno uproszczony, ale można zaobserwować pewną rzecz – wyjątki pozwalają na łatwe poinformowanie o jakimś problemie jak również jego obsługę i w dodatku są luźno powiązane. W jednym fragmencie wykonanie kodu jest przerywane poprzez rzucenie wyjątku i nie ma potrzeby zajmowania się jego obsługą. Tym zajmuje się inny fragment kodu, który w bloku try
/catch
zamyka wywołania mogące sprawiać problemy.
Większego sensu nabiera to w momencie, kiedy w różnych warstwach aplikacji ma miejsce rzucenie wyjątku i jego złapanie. Dodatkowo wyjątki mogą być rzucane również z poziomu bloku catch
, na przykład warstwa utrwalania danych łapie wyjątek związany z błędem zapisu do bazy, wykonuje cofnięcie transakcji i ponownie wrzuca wyjątek. Następnie jest on łapany w „wyższej” warstwie aplikacji i w ramach jego obsługi jest pokazywany komunikat dla użytkownika o „problemach technicznych z wykonaniem operacji”. Kolejnym przykładem jest stosowanie w aplikacji bibliotek zewnętrznych – autor biblioteki umieszcza w kodzie tylko rzucanie wyjątków. Natomiast złapaniem i obsługą tych wyjątków zajmuje się programista pracujący nad projektem, w którym dana biblioteka została wykorzystana.
Przechwytywanie nie wyłapanych wyjątków
W ramach ludzkiego błędu, zmiany w kodzie biblioteki lub innego powodu może się pojawić rzucenie wyjątku w miejscu, które nie było na to przygotowane. W efekcie zamiast strony/aplikacji w przeglądarce pojawia się komunikat:
Fatal error: Uncaught exception '[typ_wyjatku]' with message '[komunikat_wyjatku]' in [sciezka_pliku] on line [numer_linii]
Sposobem na takie sytuacje jest utworzenie i zarejestrowanie własnej funkcji obsługującej globalnie przypadki nie złapania rzuconych wyjątków – chodzi o funkcję set_exception_handler
. Oprócz wywołania tej funkcji należy wcześniej zdefiniować funkcję, która będzie odpowiadała za faktyczną obsługę wyjątków. Poniżej przykład:
<?php function obsluzWyjatek($wyjatek) { echo 'Wyjatek [' . get_class($wyjatek) .']: '. $wyjatek->getMessage() . PHP_EOL; } set_exception_handler('obsluzWyjatek'); echo 'punkt 1' . PHP_EOL; throw new UnexpectedValueException('Podales zla wartosc'); echo 'punkt 2' . PHP_EOL;
Uruchomienie tego kodu spowoduje wyświetlenie:
punkt 1 Wyjatek [UnexpectedValueException]: Podales zla wartosc
Co istotne nie zostanie wykonany żaden kod po rzuceniu wyjątku (tutaj: wypisanie „punkt 2”) – w zwykłym podejściu „pole rażenia” wyjątku (czyli operacje, które nie zostaną wykonane jeżeli wyjątek się pojawi) jest określone blokiem try
. natomiast w tym wypadku go nie ma, więc wykonanie całej reszty skryptu zostaje udaremnione. Wyjątkiem jest uruchomienie zdefiniowanej funkcji obsługującej nieprzechwycone wyjątki. Co się w niej znajdzie zależy od programisty, natomiast najważniejszy jest fakt, że można się przed takimi sytuacjami zabezpieczyć i zamiast błędu PHP pokazać użytkownikowi chociażby własną wersję „blue screen”. Można również nieco rozbudować obsługę wyjątków w funkcji w zależności od typu (w przykładzie został użyty wyjątek UnexpectedValueException
). W przeciwieństwie do wielu instrukcji catch
wyłapujących wyjątki o określonych, coraz bardziej ogólnych typach, funkcja obsługi jest jedna dla wszystkich typów.
Linki:
- wyjątki w PHP
set_exception_handler()
– manualrestore_exception_handler()
– manual