Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Kiedy używać wyjątków?
Forum PHP.pl > Forum > PHP > Object-oriented programming
zbig13
Witam!

Jestem w trakcie tworzenia obiektowego skryptu w php5. Zacząłem od własnego sterownika bazy danych. Napisałem taki kod:

  1. <?php
  2.  
  3. class DBMySQL
  4. {
  5.  
  6. private static $dbcon;
  7. private static $dbhandle;
  8. private static $query;
  9. private static $liczbaZapytan;
  10. private static $queryResult;
  11. private static $temp;
  12.  
  13. public function __construct()
  14. {
  15. self::$zapytan = 0;
  16. self::$dbcon = FALSE;
  17. }
  18.  
  19. public static function connect($host, $user, $pswd)
  20. {
  21. self::$dbcon = @mysql_connect($host, $user, $pswd);
  22. if (!self::$dbcon) {
  23. throw new Exception('Nie udało się połączyć z bazą danych.');
  24. }
  25. return self::$dbcon;
  26. }
  27.  
  28. public static function selectDb($dbname)
  29. {
  30. self::$dbhandle = @mysql_select_db($dbname);
  31. if (!self::$dbhandle) {
  32. throw new Exception('Nie udało się wybrać bazy.');
  33. }
  34. return self::$dbhandle;
  35. }
  36.  
  37. public static function query($query)
  38. {
  39. self::$liczbaZapytan++;
  40. self::$query = $query;
  41. return mysql_query(self::$query);
  42. }
  43.  
  44. public static function fetchArray($result)
  45. {
  46. self::$queryResult = $result;
  47. self::$temp = mysql_fetch_array(self::$queryResult);
  48. return self::$temp;
  49. }
  50.  
  51. public static function numRows($result)
  52. {
  53. self::$queryResult = $result;
  54. return mysql_num_rows(self::$queryResult);
  55. }
  56.  
  57. public static function realEscapeString($string)
  58. {
  59. return mysql_real_escape_string($string, self::$dbcon);
  60. }
  61.  
  62. public static function startTransaction()
  63. {
  64. self::query('BEGIN');
  65. }
  66.  
  67. public static function acceptTransaction()
  68. {
  69. self::query('COMMIT');
  70. }
  71.  
  72. public static function rollbackTransaction()
  73. {
  74. self::query('ROLLBACK');
  75. }
  76.  
  77. public static function returnQueries()
  78. {
  79. return $liczbaZapytan;
  80. }
  81.  
  82. }
  83.  
  84. ?>


W dwóch sytuacjach użyłem rzucania wyjątków w przypadku błedu (łączenie z bazą i wybieranie bazy). Chciałem też dać obsługe wyjątków do innych funkcji, np. query(), ale jakoś nie wyobrażam sobie później takiego wykonywania zapytań:
  1. <?php
  2. try {
  3. DBMySQL::query("SELECT * FROM tabela");
  4. } catch (Exception $e) {
  5. echo $e->getmessage();
  6. }
  7. ?>

Jest jakiś sposób na rzucanie wyjątków, żeby nie trzeba było ich później za każdym razem obsługiwać w kodzie? I czy warto rzucać wyjątki w przypadku takiej funkcji jak query(), lub numRows()?
Prph
Nie rozumiem, dlaczego query to zle miejsce na wyjatek. Jak dla mnie idealne.

Mozesz tobic tak:

  1. <?php
  2.  
  3.  
  4. $wynik = DB::query('zapytanie');
  5. if(!$wynik)
  6. return fasle;
  7.  
  8. $wiersz = DB::fetch();
  9.  
  10. ..
  11. ..
  12.  
  13. idt.
  14.  
  15. ?>


albo, majac wyjatki w query:
  1. <?php
  2.  
  3. try
  4. {
  5. $this->_oDatabase->insert('session', $aToInsert);
  6.  
  7. $sQuery = 'SELECT id FROM session WHERE phpsessid = "'.$this->_sPHPSessionId.'"';
  8. $this->_oDatabase->query($sQuery);
  9.  
  10. $aRow = $this->_oDatabase->fetch();
  11. $this->_iNativeSessionId = $aRow['id'];
  12.  
  13. $this->_sPHPSessionId = $sPHPSessionId;
  14. }
  15. catch (Exception $oException)
  16. {
  17.  throw $oException;
  18. }
  19.  
  20. ?>


Czy to nie jest wygodne?
zbig13
Nie rozumiem tych Twoich przykładów. Mi chodzi po prostu o to, żeby w tej funkcji:
  1. <?php
  2. public static function query($query)
  3. {
  4. self::$liczbaZapytan++;
  5. self::$query = $query;
  6. return mysql_query(self::$query);
  7. }
  8. ?>

dodać rzucenie wyjątku jeśli nie uda się wykonać zapytania. Mogę zrobić tak jak w funkcji DBMySQL::connect(), ale wtedy przy każdym wykonaniu zapytanie będe musiał pisać:
  1. <?php
  2. try {
  3. DBMySQL::query("SELECT * FROM tabela");
  4. } catch (Exception $e) {
  5. echo $e->getmessage();
  6. }
  7. ?>

A wolałbym prościej:
  1. <?php
  2. DBMySQL::query("SELECT * FROM tabela");
  3. ?>
dr_bonzo
Cytat
A wolałbym prościej: ....

przeciez i tak bedziesz musial sprawdzic czy zaputanie sie powiodlo, np.
  1. <?php
  2. if ( DBMySQL::query("SELECT * FROM tabela") )
  3. {
  4. ...
  5. }
  6. else
  7. {
  8.  ...
  9. } 
  10. ?>


nie musisz lapac wyjatkow po kazdym zapytaniu, tylko otaczasz try/catch od laczenia sie z baza, poprzez wybieranie bazy, zapytania az dorozlaczenia. No chyba ze potrzebujesz to zrobic inaczej.
zbig13
Ok juz rozumiem. Dzięki za pomoc smile.gif
SongoQ
Dorzuc tam transakcjem, logowanie czasow zapytania i explain
zbig13
Mam jeszcze jedno pytanie w związku z tym rzucaniem wyjątków.
Lepiej sprawdzać w metodzie klasy czy zapytanie się powiodło i w razie błędu wyrzucić wyjątek, czy może nie sprawdzać tego w metodzie klasy a po prostu w skrypcie sprawdzac czy wynik zapytania != false? Bo jak sprawdzam w metodzie, to cały skrypt mam objęty instrukcją try...catch i jak wystąpi jeden wyjątek to cały skrypt zostanie zatrzymany, a głupio trochę zatrzymać cały system jak tylko jedno zapytanie się nie powiedzie.
Więc jak lepiej?

Cytat(SongoQ)
  Dorzuc tam transakcjem, logowanie czasow zapytania i explain

A co to są transakcje? tongue.gif
SongoQ
Cytat
A co to są transakcje?

W google znajdziesz. Dziwie sie ze w ogole zapytales, bo to jest podstawa relacyjnych baz danych. To tak jak bys pytal co to FOR w C
zbig13
Ok, już dodałem (kod w moim pierwszym poście) tylko nie wiem czy jest to przydatne w skrypcie, bo przecież zanim się doda jakieś dane do bazy to trzeba to sprawdzić wszystkimi możliwymi sposobami smile.gif więc raczej nie ma potrzeby cofania zapytań. (przynajmniej mi się nigdy coś takiego nie zdarzyło)

A masz jakieś sugestie co do pierwszego pytania? smile.gif
Prph
Jest bardzo wiele sytuacji w ktorzych korzystasz z transakcji. Sprzojrz na przyklad:

Kod
zapytanie = wstaw do bazy nowa glaerie;
if(baza->wykonaj(zaptanie))
{
  if(!utworz_katalog(strona/katalogi/uzytownik/nowa_galeria))
  {
    // okazalo sie nie nie mogl go utworzyc.
    zapytanie = usun z bazy wczesniej dodana galerie.
    if(!baza->wykonaj(zaptanie))
    {
       no i spojnosc danych poszla sie..;)
    }
  }
}


Z wykorzystaniem stansakcji
Kod
start_transakcji;

zapytanie = wstaw do bazy nowa glaerie;
if(baza->wykonaj(zaptanie))
{
  if(!utworz_katalog(strona/katalogi/uzytownik/nowa_galeria))
  {
    // okazalo sie nie nie mogl go utworzyc.
    abort;
  }
  else
    commit;
}


commit mozna w zasadzie pominac, bo zazwyczaj w bazie ustawuone jest autocommit i na koncy dane i tak zostana zapisane w bazie.

Pozdrawiam.
zbig13
No dobra przekonałeś mnie smile.gif

A co do tej funkcji query to chyba nie będe jednak dodawał w niej rzucania wyjątków, bo to troche bezsensu że cały skrypt się zatrzyma w przypadku błędu w zapytaniu.

@SongoQ
Logowanie czasów zapytania i explain postaram się dodać w najbliższym czasie smile.gif

Jeszcze raz dzięki wam za pomoc.

Pozdrawiam,
Zbig
SongoQ
Mozesz ewentualnie zrobic cos w rodzaju debug zapytania. Ustawiasz sobie jakis config gdzie podajesz max dla zapytania i np przy wersji debug wyswietlasz sobie zapytania ktore przeszly zadany czas a ktore nie, ktore sie wywalaja itd. Tam samo mozesz postapic z explain. Jak sobie zorganizujesz to juz zalezy tylko od Ciebie. Dla mnie np takie rzeczy sa bardzo wazne.
Vengeance
Cytat
Bo jak sprawdzam w metodzie, to cały skrypt mam objęty instrukcją try...catch i jak wystąpi jeden wyjątek to cały skrypt zostanie zatrzymany, a głupio trochę zatrzymać cały system jak tylko jedno zapytanie się nie powiedzie.


Jak sama nazwa wskazuje, wyjątek służy do informowania o sytuacjach wyjątkowych, czyli takich po wystąpieniu których aplikacja musi być wstrzymana (ewentualnie jakiś blok operacji w aplikacji).

Dlatego stwierdzenie, że "trochę głupio jest zatrzymać system" jest samo w sobie bardzo głupie winksmiley.jpg Chyba rozumiesz?
zbig13
Tak rozumiem, dlatego zrezygnowałem z wyjątków w funkcji query smile.gif
mike
Cytat(Vengeance @ 2006-03-28 20:42:59)
Cytat

Bo jak sprawdzam w metodzie, to cały skrypt mam objęty instrukcją try...catch i jak wystąpi jeden wyjątek to cały skrypt zostanie zatrzymany, a głupio trochę zatrzymać cały system jak tylko jedno zapytanie się nie powiedzie.


Jak sama nazwa wskazuje, wyjątek służy do informowania o sytuacjach wyjątkowych, czyli takich po wystąpieniu których aplikacja musi być wstrzymana (ewentualnie jakiś blok operacji w aplikacji).

Dlatego stwierdzenie, że "trochę głupio jest zatrzymać system" jest samo w sobie bardzo głupie winksmiley.jpg Chyba rozumiesz?

~Vengeance mylisz się.
Wyjątek jest to nagła styuacja, na którą trzeba zareagować.

A przerwanie działania systemu/aplikacji to najgorsza reakcja.

Nie można sobie (a przynajmniej nie powinno) zatrzymać działania w fowolnej chwili bo wyskoczył jakiś wyjątek, no właśnie po to wyskoczył żeby nam powiedzieć że musimy zrobić coś inaczej niż planowaliśmy.

To zależy od natury wyjątku, ale powinniśmy ładnie dalej poprowadzić aplikacją na tory boczne, które zaprowadzą użytkownika do ładnego ekranu powiadamiającym go o błędzie. Wcześniej możemy wykonać wiele operacji: logować wyjątek, wysyłać maila do admina, ... .

Kill process to bardzo zły wybór.
SongoQ
Cytat
To zależy od natury wyjątku, ale powinniśmy ładnie dalej poprowadzić aplikacją na tory boczne, które zaprowadzą użytkownika do ładnego ekranu powiadamiającym go o błędzie.


Wyjatek moze rowniez sluzyc jako "redukcja" warunkow, wiec dokladnie zejscie na tory boczne jest tylko w wiekszosci przypadkow.
zbig13
To konkretnie jak lepiej robić? :
- Tam gdzie to możliwe umieszczać wyjątki
czy
- Unikać wyjątków i zastępować je warunkami
Vengeance
mike_mech: Chodziło mi raczej oto, że stosowanie wyjątku dla np.:

  1. <?php
  2.  
  3. $request->Get('action');
  4.  
  5. ?>

Gdzie parametr action nie istnieje jest moim zdaniem głupie. Nie lubię, gdy ktoś załkowicie usuwa ify() na rzecz wyjątków.
DeyV
@ Vengeance - ale już w przypadku
system::createAction( 'news' );
wyrzucenie wyjątku może okazać się nazzwyczaj przydatne.
Zawsze wtedy masz możliwośc załadowania innej akcji, zrobienie czegoś innego.

Podobnie sytuacja wygląda np. z autoryzacją - nie masz uprawnień, to się zaloguj, i nie ważne jest to, w którym momencie system to zauważył. Ważne, że to dostrzegł, i w zwiazku z tym może coś z tym zrobić.

Apropo wyjątków i transakcji - mi osobiście bardzo podoba sie taki przykład:

  1. <?php
  2.  
  3.  
  4. try{
  5.  $Db->startTrans();
  6.  
  7.  $Db->Execute( pytanie );
  8.  $Db->Execute( pytanie2 );
  9.  $Db->Execute( pytanie3 );
  10.  
  11.  system::robCos();
  12.  
  13.  $Db->commitTrans();
  14. }
  15. catch( ExceptionDB $e ){
  16.  $Db->abortTrans();
  17. }
  18.  
  19. ?>


I niech ktoś w czytelniejszy sposób zapisze transakcje winksmiley.jpg
Vengeance
Ale czy ja mówię, że wyjątki są złe? Przykłady które podałeś są jak najbardziej dobre... i pokazują jak wyjątki są przydatne. Ale czy cieszyłbyś się, gdyby każda funkcja php zamiast np. false w razie błędu zwracała wyjątek? Ja nie... bo nie do tego są wyjątki by stosować je wszędzie gdzie się da. A że temat brzmi "kiedy używać wyjątków" chciałem zwrócić na to uwagę autora
g00fy
pozatym dobrze sobie zawsze ustawiać zmienna czy funkcja ma wyrzucać wyjątki czy zwracać wartość null lub false.
Prph
Cytat(g00fy @ 2006-04-04 15:15:05)
pozatym dobrze sobie zawsze ustawiać zmienna czy funkcja ma wyrzucać wyjątki czy zwracać wartość null lub false.

Yyy, nie bardzo rozumiem... Opisz to lepiej.
SongoQ
Autorowi chyba chodzilo o tryb pracy czy z z wyjatkami czy bez.
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.