Ponieważ wyjątki są obiektami, możemy tworzyć własne obiekty poprzez dziedziczenie po klasie Exception. Nasuwa się tutaj pytanie, poco tworzyć własne wyjątki? Bardzo ważna jest nazwa klasy, ponieważ to po niej będziemy wyłapywać odpowiednie wyjątki w blokach catch(){}. Nazwa powinna także odzwierciedlać naturę problemu lub gdzie wystąpił.
Stwórzmy sobie własny wyjątek odpowiedzialny za poinformowanie nas, iż wystąpił problem w jądrze naszego systemy. Odpowiednia nazwa dla tego wyjątku będzie SystemException. Uczynimy go odpowiedzialnym za wyrzucanie wszelkich problemów napotkanych w naszej klasie System.
<?php class SystemException extends Exception {} class System { protected $Router = null; public $sPath = './router.php'; public function runRouter() { if( !file_exists( $this->sPath ) ) { throw new SystemException( 'Plik z routerem nie istnieje! Ścieżka “'.$this->sPath.'”' ); } include $sPath; $this->Router = new Router(); } } $System = new System(); try { $System->runRouter(); } catch( SystemException $Ex ) { // Nie udało sie włączyć routera, ponieważ plik nie istnieje $System->sPath = './Istniejacy/Router.php'; $System->runRouter(); } catch( Exception $Ex ) { echo 'Standardowy wyjątek'; } ?>
Należy zwrócić uwaga na kolejność bloków catch(){}, gdyż jak zostanie już znaleziony wyjątek pasujący do danego bloku catch(){}, dalsze boki nie będą już przeszukiwane (podobnie jak to ma miejsce switch(), a catch( Excpetion $Ex ) można traktować jak odpowiednik default).
Czemu jakbym dał na początku blok catch( Exception $Ex ){} to wyjątek został by nie poprawnie obsłużony? Ponieważ każdy wyjątek powinien dziedziczyć po Exception. Ustawiając na samym początku bloki catch(){} poszukujący właśnie tego wyjątku(Exception), będzie zawsze on uznany za ten najbardziej pasujący, co oczywiście nie jest prawda. Dlatego należy pamiętać aby bloki catch(){} określających najmniejszy błąd umieszczać na samej gorze i stopniowo przechodzić w dół, aż w końcu dotrzemy do wyjątku Exception.
Wracając do samego kodu, działa on następująco. Wewnątrz bloku try{} uruchamiamy metodę klasy, która może zwrócić wyjątek. U nas metoda sprawdza czy istnieje plik, tak się akurat dzieje że plik nie istnieje, zostaje zwrócony wyjątek (i zatrzymane wykonywanie metody, tak jak by tam było return). Ponieważ kod był objęty blokiem try{} poszukiwany jest blok catch(){} pasujący do zwróconego wyjątku, tam następuje obsługa wyjątku. W klasie system zostaje zmieniona ścieżka na istniejącą i ponownie zostaje uruchomiona metoda System::runRouter(). Tym razem operacja jest wykonana poprawnie i nie zostaje zwrócony wyjątek (musisz pamiętać tylko aby plik istniał, także nie zdziw się jeżeli zobaczysz komunikat wyjątku jak odpalisz przykład – to nie błąd :-) )
Oczywiście możemy robić także bardziej skomplikowane konstrukcje. Przykładowo w klasie Router może zostać także zostać zgłoszony wyjątek gdy coś będzie powodować problemy.
Napisze prosty przykład.
<?php class RouterException extends Exception{} class Router { public function __construct() { // Prowadzimy jakąś działalność, po czym okazuje się // że coś nie działa throw new RouterException( 'Ojojoj...' ); } } class NewSystem extends System { public function runRounter() { try { parent::runRouter(); } catch( SystemException $SystemEx ) { // Wywalany wyjątek, ponieważ juz mamy jego obsługę niżej throw $SystemEx; } catch( RouterException $RouterEx ) { // Próbujemy rozwiązać problem naszego Router'a // Jeżeli sie nie uda to możemy zdecydować sie na wyrzucenie wyjątku , np: throw new $RouterEx; } } } $System = new System(); try { $System->runRouter(); } catch( SystemException $Ex ) { // Nie udało sie włączyć routera, ponieważ plik nie istnieje $System->sPath = './Istniejacy/Router.php'; $System->runRouter(); } catch( Exception $Ex ) { // Tutaj zostanie obsłużony RouterException, ponieważ u góry nie ma nic pasującego do niego echo 'Wystąpił problem: ',$Ex; } ?>
Leave a reply