Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP]Wyjątki PHP, na przykładzie
Forum PHP.pl > Forum > Przedszkole
wNogachSpisz
Witam

Jestem w trakcie pisania biblioteki - ujednoliconego API - służącej do obsługi usług rozwiązywania captch. Nie muszę chyba nikogo przekonywać, że dobrze zaprojektowany system wyjątków to podstawa. Niestety nigdy się w to nie bawiłem, tzn. często używam wyjątków, ale nigdy nie próbowałem definiować własnych klas wyjątków. Przeczyłem ten artykuł: http://blogs.msdn.com/b/kcwalina/archive/2...ierarchies.aspx
Uznałem że zaproponowany tam podział na "usage exception" oraz "system exception" wydaje się rozsądny.
Moj kod wygląda następująco:
  1. class Decaptcha_Exception extends Exception
  2. {}
  3. class Decaptcha_Exception extends Exception
  4. {}
  5. /*
  6. 99: unknown error
  7.  */
  8.  
  9. class DecaptchaUsage_Exception extends Decaptcha_Exception
  10. {}
  11. /*
  12. 101: invalid login or password
  13. 102: captcha file not found / file empty
  14. 103: not logged in
  15. */
  16.  
  17. class DecaptchaSystem_Exception extends Decaptcha_Exception
  18. {}
  19.  
  20. class DecaptchaSystemLogical_Exception extends DecaptchaSystem_Exception
  21. {}
  22. /*
  23. 201: unable to decode captcha (too hard or malformed image)
  24. 202: banned
  25. 203: insufficient funds
  26. 204: service overload
  27. 205: invalid response
  28. 206: connection failed/lost/timeout
  29. 207: network error
  30. */
  31.  
  32. class DecaptchaSystemFailure_Exception extends DecaptchaSystem_Exception
  33. {}
  34. /*
  35. 301: php extension not found
  36. 302: php function not found
  37. */

(przepraszam za tabulatory ale odzwierciedlają one hierarchę).

Pytanie: Co należałoby tu zmienić, czy np. "202: insufficient funds" pasuje bardziej do "SystemLogical_Exception" czy może "Usage_Exception". Czy to co zrobiłem ma jakikolwiek sens? smile.gif
Crozin
1. W PHP powinieneś rozróżnić dwie podstawowe "rodziny" wyjątków: LogicalException oraz RuntimeException (hierarchia wbudowanych wyjątków). Te pierwsze wynikają z "błędów programisty", te ostatnie zaś z błędnego użycia przez użytkownik, konfiguracji środowiska i ogóle wszystkiego co możne wystąpić wyłącznie w trakcie działania aplikacji.
2. Twoje wyjątki powinny u swojej podstawy rozszerzać, którąś z tych dwóch gałęzi, nie zaś bezpośrednio klasę Exception.
3. Na dobrą sprawę w większości przypadków nie potrzebujesz nawet własnych klas dla tych wyjątków.
wNogachSpisz
Cytat(Crozin @ 26.02.2013, 18:36:28 ) *
1. W PHP powinieneś rozróżnić dwie podstawowe "rodziny" wyjątków: LogicalException oraz RuntimeException (hierarchia wbudowanych wyjątków). Te pierwsze wynikają z "błędów programisty", te ostatnie zaś z błędnego użycia przez użytkownik, konfiguracji środowiska i ogóle wszystkiego co możne wystąpić wyłącznie w trakcie działania aplikacji.

Chyba odwrotnie, LogicalException to błąd użycia, a RuntimeException, błąd programu. Tak czy inaczej, podział ten wydaje gorszy od zaproponowanego przez P. Krzysztofa. Podam przykład: "network error" problem z siecią nie powstaje z powodu błędnego inputa, ani tego że program jest źle napisany, tylko.. no wlasnie, z przyczyn niezależnych :-]
Rozumiem że według Ciebie powiniem rozszerzyc RuntimeException o NetworkException, ale takie podejście to pułapka, czytamy:
„I would consider not creating new exception types till you are sure you need them. At which point, you can create a new exception type by inheriting from the currently throw type. Sometimes it might result in slightly strange exception hierarchy (for example, a custom exception inheriting from InvalidOperationException), but it’s not a big deal in comparison to the cost having unnecessary exception types in your library, which makes the library more complex, adds development cost, increases working set, etc.”
Autor radzi aby nie tworzyć nowych klas, bo program staje się zbyt skomplikowany, w tym punkcie się z nim zgadzam. Błąd "network error" klasyfikuje w tym podziale jako "System Logical Exception" , czyli błąd ktory nie musi spowodować zakończenia programu i nie jest spowodowany błędnym inputem.

Cytat(Crozin @ 26.02.2013, 18:36:28 ) *
2. Twoje wyjątki powinny u swojej podstawy rozszerzać, którąś z tych dwóch gałęzi, nie zaś bezpośrednio klasę Exception.

Przyjrzyj się dokładniej, dziedziczą z „Decaptcha_Exception”.

Cytat(Crozin @ 26.02.2013, 18:36:28 ) *
3. Na dobrą sprawę w większości przypadków nie potrzebujesz nawet własnych klas dla tych wyjątków.

Nigdy nie potrzebowałem, ale dzisiaj uznałem że czas na małe rozwarstwienie. Warto zauważyć, że zostawiam sobie fallback, mianowicie, wszystkie wyjątki można rozpoznać bez podziału na klasy po ich unikatowych kodach błędu.
Crozin
Cytat
Chyba odwrotnie, LogicalException to błąd użycia, a RuntimeException, błąd programu.
Nie, LogicException to błędy w samym kodzie, np.:
  1. <?php
  2.  
  3. $command->stop(); // LogicException: Komenda nigdy nie została wystartowana, więc nie może zostać zatrzymana
  4.  
  5. $command->start();
  6. $command->stop(); // OK
RuntimeException to zaś wszystkie błędy, które wynikły ze względu na otrzymane z zewnątrz dane (od użytkownika, z pliku czy z sieci).
Cytat
Tak czy inaczej, podział ten wydaje gorszy od zaproponowanego przez P. Krzysztofa. Podam przykład: "network error" problem z siecią nie powstaje z powodu błędnego inputa, ani tego że program jest źle napisany, tylko.. no wlasnie, z przyczyn niezależnych :-]
Nie zgodziłbym się. Jeżeli próbujesz skorzystać np. z socketu, który nie jest w ogóle podpięty (zbindowany) możemy mówić o LogicException - bo jako programista powinieneś go zbindować. Jeżeli zaś problem pojawił się z powodu wprowadzenia poprawnej, lecz nieistniejącej nazwy hosta, albo akurat internet nam padł jest to RuntimeException. Jeżeli jakiś błąd wynika z przyczyn niezależnych niemal zawsze będzie to RuntimeException.
Cytat
Nigdy nie potrzebowałem, ale dzisiaj uznałem że czas na małe rozwarstwienie. Warto zauważyć, że zostawiam sobie fallback, mianowicie, wszystkie wyjątki można rozpoznać bez podziału na klasy po ich unikatowych kodach błędu.
Podobnie jak autor artykułu czy Ty sam, nie jestem zwolennikiem tworzenia dziesiątek klas dla wyjątków. Zauważ, że u Ciebie spokojnie można wykorzystać wbudowane wyjątki PHP do osbługi przynajmniej części. Resztę z nich mógłbyś zapewne obsłużyć przy pomocy dużo "płytszej" hierarchii.

Hierarchia wyjątków zawsze będzie stanowiła dosyć poważny problem, ale przy raptem 13 wyjątkach jakie tutaj zaprezentowałeś odwzorowywanie hierarchii z C#/.NET nie jest dobrym pomysłem. Szczególnie, że pokrywa się częściowo z SPL-owską.
irmidjusz
W tym przykładzie z
  1. $command->stop();
jeszcze lepiej by było rzucić BadMethodCallException.
A tak w ogóle, to hierarchia wyjątków w PHP jest dziwna, słabo wyjaśniona w manualu i nie wiadomo w zasadzie jak z niej korzystać w spójny sposób (brak zgodności w tej kwestii wśród programistów). Same opisy tych klas są momentami śmieszne - szczególnie, gdy opisują błędy czasu kompilacji i wykonania (sic!).
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.