Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Exception w dwoch postaciach:)
Forum PHP.pl > Forum > PHP > Object-oriented programming
kicaj
Zastanawiam sie jak rozwiazac problem z wyjatkami, otoz:
Tworze sobie pochodne klasy Exception:
MessageException (mniej powazne bledy)
FatalException (powazny blad zatrzymujacy dzialanie aplikacji)

MessageException chcialbym uzywac np. do problemow typu: Niepoprawne ID, Nie ma takiego rekordu w bazie, etc., a FatalException do sytuacji gdy np. nie ustanowiono poleczenia z db

  1. <?php
  2. class DB
  3. {
  4.  // laczy z baza, w razie niepowodzenia zwraca:
  5.  throw new FatalException( 'Niestety nie ustanowiono polaczenia z baza!' );
  6. }
  7.  
  8. class Users
  9. {
  10. function __construct( $iID )
  11. {
  12. if( !is_numeric( $iID ) )
  13. {
  14.  throw new MessageException( 'Niepoprawne ID' );
  15. }
  16. }
  17. ?>



  1. <?php
  2. $oDB = new DB; //laczenie
  3.  
  4. try
  5. {
  6. // wyswietlanie podstawowego widoku (header)
  7.  
  8. $oU = new Users( 'a' );
  9.  
  10. // wyswietlanie podstawowego widoku (footer)
  11. }
  12. catch( Exception $oE )
  13. {
  14. echo $oE -> printError();
  15. }
  16. ?>


Efektem tego ma byc ze jezeli nie polaczy sie z baza dostanie na ekranie tylko komunikat o bledzie (FatalException), zas jesli wystapi blad w klasie Users (MessageException) to wsywietlony zostanie szablon z komunikatem o bledzie (header, komunikat, footer)

Jak to rozwiazac?
Helios
Nie wiem czy dobrze rozumiem?
Skonkretyzowac instrukcje catch dla 2 typow wyjatkow?

  1. <?php
  2. try{
  3. // instrukcje
  4. }catch(MessageException $e){
  5. // akcje implementujace wyswietlanie komunikatu
  6. }catch(FatalException $e){
  7. // akcje implementujace obsluge szablonu i przesylanie/wyswietlanie komunikatu
  8. }
  9. ?>


FatalException moze zawieszac wykonywanie programu i rzucac na ekran tresc bledu, a MessageException moze rejestrowac gdzies tresc komunikatu (zakladam, ze moze byc kilka bledow przy 1 uruchomieniu programu a header masz juz wyslany) po czym wyswietlic zarejestrowane komunikaty i footer.
Cezar708
... chyba chodzi o to, że po wywołaniu wyjątku typu MessageException() ma się gdzieś zapisać informacja o błędzie, i program ma wrócić do dalszego wywoływania kodu. Natomiast w przypadku wywołania FatalException() ma przerwać działanie aplikacji.

Niestety nie da się (albo nie znam sposobu) zrobić to w taki prosty sposób, Ponieważ wyrzucenie wyjątku to wyrzucenie wyjątku, więc program i tak pójdzie do obsługi... a stamtąd raczej nie ma prostego powrotu. Chyba lepszym wyjściem będzie po prostu użycie jakiegoś innego rodzaju klasy do notowania MessageException(). Coś na styl:

  1. <?php
  2. class MessageException{
  3. function __construct($msg){
  4. $this->writeToLogFile($msg);
  5. $this->notifyUser($msg);
  6. }
  7. //... itd
  8. }
  9. ?>


Po prostu ją wywołujesz a potem skrypt działa dalej.
kicaj
@Cezar708: Chyba tez tak to widze, innego rozwiazania nie ma, MessageException to imitacja wyjatku:/
splatch
Używanie wyjątków do informowania o tym, że coś poszło nie tak jest jak najbardziej na miejscu, jakkolwiek ograniczanie się tylko do 2 typów już nie jest trafne. Skorzystaj z pełni możliwości jakie oferują Ci wyjątki do tego by komunikować różne sytuacje. Generalizacja na dwa stopnie - wyjątek groźny i niegroźny jest złe, ponieważ po drodze jest cała gama problemów z którymi radzisz sobie w różny sposób a typ złapanego wyjątku od razu ułatwia Ci diagnozę (stacktrace + typ). Rozróżnienie fatal/warning/notice to w gruncie rzeczy poziom programowania strukturalnego i trigger_error. Zrób coś więcej..

  1. <?php
  2. class BaseSystemException extends Exception {}
  3.  
  4. class DatabaseException extends BaseSystemException {}
  5. class ConnectionException extends DatabaseException {}
  6. class QueryException extends DatabaseException {}
  7.  
  8. class IOException extends BaseSystemException {}
  9.  
  10. // z takim kodem możesz zrobić np:
  11. try {
  12. new DatabaseManager($url);
  13. } catch (ConnectionException $e) {
  14. // problem z połączeniem
  15. } catch (QueryException $e) {
  16. // np problem z SET NAMES po połączeniu
  17. } catch (DatabaseException $e) {
  18. // nieznany błąd związany z bazą danych
  19. } catch (IOException $e) {
  20. // błąd odczytu np konfiguracji
  21. } catch (BaseSystemException $e) {
  22. // błąd zgłoszony w Twoim kodzie
  23. } catch (Exception $e) {
  24. // błąd, którym sypnęło np PDO
  25. }
  26.  
  27. ?>
kicaj
No nie zabardzo splatch, ja widze to raczej tak (podzial na bledy i bledy krytyczne):
  1. <?php
  2. try
  3. {
  4. // szablon header
  5.  
  6. if( MessageException )
  7. {
  8. // szablon bledu maloznaczacego/ostrzezenia
  9. }
  10. else
  11. {
  12. // szablon wlasciwy wywlanej poprawnie akcji
  13. }
  14.  
  15. // szablon footer
  16. }
  17. catch( FatalException )
  18. {
  19. // tylko szablon bledu krytycznego, brak wyswietlania jakichkolwiek elementow stro
    ny
  20. }
  21. ?>


Jak widac pierwsza czesc bloku try wyswietla szablon calej strony z ostrzezeniem (np. nie podano id) lub zawartoscia akcji (wyswietlony user), zas druga czesc bloku zatrzymuje cala aplikacje i wyswietla komunikat o krytycznym bledzie (np. brak polaczenia z db)
splatch
Cytat(kicaj @ 5.12.2007, 12:18:32 ) *
No nie zabardzo splatch, ja widze to raczej tak (podzial na bledy i bledy krytyczne):
  1. <?php
  2. try
  3. {
  4. // szablon header
  5.  
  6. if( MessageException )
  7. {
  8. // szablon bledu maloznaczacego/ostrzezenia
  9. }
  10. else
  11. {
  12. // szablon wlasciwy wywlanej poprawnie akcji
  13. }
  14.  
  15. // szablon footer
  16. }
  17. catch( FatalException )
  18. {
  19. // tylko szablon bledu krytycznego, brak wyswietlania jakichkolwiek elementow stro
    ny
  20. }
  21. ?>


Jak widac pierwsza czesc bloku try wyswietla szablon calej strony z ostrzezeniem (np. nie podano id) lub zawartoscia akcji (wyswietlony user), zas druga czesc bloku zatrzymuje cala aplikacje i wyswietla komunikat o krytycznym bledzie (np. brak polaczenia z db)


kicaj zanim zapniesz wyjątki do walidacji zastanów się kto powinien obsługiwać błędy walidacji bo sprawa obsługi błędów i walidacji to dwie różne rzeczy, no chyba że chcesz traktować błędy z inputu na równi z błędami systemowymi. W końcu możesz mieć FatalException przy połączeniu do bazy danych i FatalException przy walidacji, bo ktoś nie podał wartości w polu user_login. Z resztą walidację omawiamy już w oddzielnym temacie i sugestie z użyciem wyjątków do walidacji powinny trafiać tam, a nie tu, jak sądzę.
Myślenie o wyjątkach w dwóch kategoriach - krytyczne i "nie krytyczne" jest złe bo nie masz rozróżnienia na kontekst wystąpienia błędu i zawężasz sobie pole do ich obsługi. W końcu nie wszystkie FatalException-y obsługujesz tak samo. Obsługa wyjątków w try/catch przez instanceof jest prawdę powiedziawszy lekkim naginaniem zasad związanych z przeznaczeniem wyjątków i zastosowaniem bloku catch. Nie odbiega to od trigger_error zatem zastanów się czy piszesz obiektowo czy też bawisz się w przeplatanie.

Kwestia wyjątków w akcji - obsługujesz tylko te, które rzeczywiście musisz. W większości przypadków możesz wydelegować ich obsługę do FrontControllera i pozbyć się śmieci z logiki.
Whisller
I może ja się wypowiem kilka słów na temat wyjątków.
Ograniczenie klas wyjątków do właśnie dwóch klas Fatal i Notice ( czy jak je tam nazwiesz ), nie jest zbyt dobrym rozwiązaniem.
Z tego względu że doprowadza do sytuacji w której nie wiesz tak na prawdę na jakim etapie coś padło.
Nie jesteś w stanie dla odpowiedniego błędu przygotować akcji zastępczej, no chyba że zrobisz duuuży switch z numerami błędów, nie wspominając o tym aby pamiętać który numer co robi.
A dolicz sobie do tego zle wyłapywanie wyjątków w zagnieżdzonym try...catch smile.gif Popatrz:
  1. <?php
  2. class Klasa
  3. {
  4. public function add()
  5. {
  6.  try {
  7. ...
  8. $this->db->insert(); // Tutaj takze moze wywalic wyjatek FatalErrorException
  9. ...
  10.  // Jakies operacje ktore wywala wyjatek FatalErrorException
  11. throw new FatalErrorException("Uhh nie udalo sie");
  12.  ...
  13. } catch ( FatalErrorException $e ) {
  14.  // Który throw złapaliśmy?
  15. }
  16. }
  17. }
  18.  
  19. try {
  20. $o->add();
  21. } catch( FatalErrorException $e ) {
  22.  // Czy to nam się wywoła? tongue.gif
  23. }
  24. ?>
Cysiaczek
Ja jestem za tym, żeby wyjatki były obdługiwane na odpowednim poziomie. Jeśli mamy błędy powstałe w samej logice aplikacji (coś źle poszło), to wypluty wyjątek powinien byc zwiazany z akcją. np.
  1. <?php
  2. class cosNieTakException extends actionException{}
  3. ?>


Jeśli walidacja jest we froncie, to powinna mieć odpowiednio
  1. <?php
  2. class validatorException extends frontException{}
  3. class unexpectedInputDataException extends validatorException{}
  4. ?>

i powinny być obsługiwane przez odpowiedni handler błędów (obiekt) we front controlerze (choc w sumie nie wiem, czy to dalej jest Front Controller. U mnie rozrósł się tak, ze chyba będę potrzebował jakiejś dobrej refektoryzacji.)

  1. <?php
  2. $err=new frontExceptionHandler(); 
  3. try{}
  4. catch()
  5. {
  6. $err->handle(validatorException $exception) //pokazuje wymuszanie typu tylko aby zobrazować
  7. }
  8. ?>
kicaj
Pewnie macie racje z tym nie slusznym rozdzieleniem wyjatkow na krytyczne i mniej krytyczne...

Jednak na tyle ile mi potrzebne spelnia moje oczekiwania, efektem czego jest przy np. braku polaczenia z db, zachowuje sie jak normalny wyjatek, zatrzymuje aplikacje i informuje o bledzie, drugi sposob jak juz wczesniej bylo powiedziane jest to czesciowa imitacja wyjatkow z tym ze aplikacja wykonuje sie, tylko jest informowana o danym bledzie w danej akcji, reszta dziala bezproblemu-dalej. Pewnie z czasem rozbudowy tegoz kodu odczuje wasze uwagi i wtedy przejde na prawdziwy OOPowe wyjatki, tymczasem mam jak to nazwal splatch "przeplatanie"...

smile.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.