Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: obsluga wyjatkow
Forum PHP.pl > Forum > PHP > Object-oriented programming
areekz
Czy jest sposob na obsluge wyjatkow funkcji wbudowanych w php. Chodzi mi o include ('plik'), gdzie po wpisaniu nieistniejacego pliku bede mogl wykonac wlasna funkcje a nie ze parser informuje mnie o tym ze pliku nie znaleziono.
DeyV
Niestety - nie.
W tej chwili php nie ma jeszcze pełnej obsługi wyjątków, to znaczy, że większość wbudowanych funkcji nie zwraca wyjątków, tylko errory i notice.

W takim przypadku jedynym rozwiazaniem jest napisanie funkcji myInclude i samodzielna obsługa i weryfikacja takich wydażeń.
mike
Poczytaj o funkcji __autoload() i poszukaj na forum. Temat był poruszany nie raz, jest pewna możliwość, ale jest trochę zabawy.
forum.php.pl :: search :: autoload
bregovic
Może się mylę, ale chyba można coś zwojować używając set_error_handler" title="Zobacz w manualu php" target="_manual()...?
bela
Jasne że można, ale to nie to samo co natywna obsługa winksmiley.jpg
krzysztof f.
Niestety mimo znacznego usprawnienia modelu obiektowego w php5, w tym obsługi wyjątków, większość standardowych funkcji języka, z których korzystamy na co dzień ciągle zorganizowane są w sposób proceduralny i nie zwracają wyjątków w razie niepowodzenia wykonania. Więc nie jest możliwe zamknięcie ich wywołania w bloku try i przechwytywania wyjątków. W takich przypadkach php zwraca błędy różnych poziomów w zależności od błędu.

Korzystając z funkcji set_error_handler mamy możliwość część tych błędów przechwytywać. Oto w jaki sposób przy wykorzystaniu tej funkcji + set_exception_handler ja radzę sobie z obsługą zwykłych błędów za pomocą wyjątków. Pomysł polega na tym, żeby funkcja set-error-handler wyrzucała napisany na tą potrzebę przez nas wyjątek. Dla pewności ustawiamy obsługę nieprzechwyconych wyjątków w set_exception_handler. W bardzo uproszczonej formie wygląda to następująco (kod w php5):
  1. <?php
  2. /**
  3.   * Klasa obsługi błędów i nieprzechwyconych wyjątków
  4.   **/
  5. class ErrorHandler
  6. {
  7.    /**
  8.     * Konstruktor 
  9.     *
  10.     * Możemy w tym miejscu ustawić o jakich błędach php ma nas informować
  11.     *
  12.     * @return ErrorHandler
  13.     * @access public
  14.     **/
  15.    public function __construct()
  16.    {
  17.       // trzeba pamiętać że E_ALL ma wartość 2047 podczas gdy 2048
  18.       // a chcemy przecież wiedzieć o wszystkich błędach
  19.       error_reporting( E_ALL | E_STRICT );
  20.    }
  21.  
  22.    /**
  23.     * Obsługa błędów 
  24.     *
  25.     * @param integer Kod błędu
  26.     * @param string Opis błędu
  27.     * @param string Plik w którym wystąpił błąd
  28.     * @param integer Numer linni kodu
  29.     * @return void
  30.     * @access public
  31.     * @throws ErrorException
  32.     **/
  33.    public function handleError( $iErrNo, $sErrStr, $sErrFile, $sErrLine )
  34.    {
  35.       set_exception_handler( array( $this, 'handleException' ) );
  36.  
  37.       // funkcja error handler przechwytuje wszystkie błędy nawet w przypadku
  38.       // wywyołań  funkcji poprzedzonych @ dla których error_reporting = 0
  39.       if ( error_reporting() != 0 )
  40.       {
  41.          throw new ErrorException( $sErrStr, $iErrNo, $sErrFile, $sErrLine );
  42.       } 
  43.    }
  44.    
  45.    /**
  46.     * Obsługa nieprzechwyconych wyjątków 
  47.     *
  48.     * @param Exception
  49.     * @return void
  50.     * @access public
  51.     **/
  52.    public function handleException( $oE )
  53.    {
  54.       echo '<pre>';
  55.       echo $oE->__toString();
  56.       echo '</pre>';  
  57.    }
  58. }
  59.  
  60. ?>

Klasa wyjątku mogłaby wyglądać tak:
  1. <?php
  2. class ErrorException extends Exception
  3. {
  4.    /**
  5.     * Konstruktor 
  6.     *
  7.     * @param string Opis błędu
  8.     * @param integer Kod błędu
  9.     * @param string Plik w którym wystąpił błąd
  10.     * @param integer Numer linni kodu
  11.     * @return ErrorException
  12.     * @access public
  13.     **/
  14.    public function __construct( $sMessage, $sCode, $sFile, $sLine )
  15.    {
  16.       parent::__construct( $sMessage, $sCode );
  17.       $this->file = $sFile;
  18.       $this->line = $sLine;
  19.    }
  20.    
  21.    /**
  22.     * Reprezentacja obiektu w formie łańcucha tekstu
  23.     *
  24.     * @return string
  25.     * @access public
  26.     **/
  27.    public function __toString()
  28.    {
  29.       $sMsg = $this->message . '<br />' . $this->file . '(' . $this->line . ')<br/ >';
  30.       return $sMsg;
  31.    }
  32.      
  33. }
  34.  
  35. ?>

Należałoby oczywiście dla każdego typu błędu stworzyć specjalistyczną klasę wyjątku i odpowiednio je obsługiwać.

Użycie powyższego kodu sprowadza się już tylko do ustawienia napisanej przez nas funkcji obsługi błędów i przechwycenia odpowiedniego wyjątku:
  1. <?php
  2. ...
  3.    public function testErrorAsException()
  4.    {
  5.       set_error_handler( array( new ErrorHandler(), 'handleError' ) );
  6.       $bExceptionCaught = false;
  7.       try
  8.       {
  9.          constant( UNDEFINED_CONSTANT );
  10.       }
  11.       catch ( ErrorException $oE )
  12.       {
  13.          $bExceptionCaught = true;
  14.       }
  15.       $this->assertNoErrors();
  16.       $this->assertTrue( $bExceptionCaught );
  17.       $this->assertEqual( 38, $oE->getLine() );
  18.       $this->assertWantedPattern( '/UNDEFINED_CONSTANT/', $oE->getMessage() );
  19.    }
  20. ...
  21.  
  22. ?>

Podejście jest oczywiście bardzo ubogie i nie wolne od błędów. Trzeba między innymi pamiętać o tym, że nasz error_handler nie będzie przechwytywał błędów typu E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING i części E_STRICT, z którymi nic nie zrobimy.

Cytat
Poczytaj o funkcji __autoload() i poszukaj na forum. Temat był poruszany nie raz, jest pewna możliwość, ale jest trochę zabawy.
forum.php.pl :: search :: autoload

Nie bardzo rozumiem co ma wspólnego funkcja __autoload() z pytaniem zadanym przez reekz

Z poważaniem,
kf

P.S. Takiego samego posta na podobne pytanie umieściłem na innym polskim forum. Zdecydowałem się zamiast podać linka napisać go tutaj jeszcze raz. Mam nadzieje, że jest to zgodne z tutejszymi zasadami.


poprawiam
---
nospor
hwao
Cytat(areekz @ 2005-11-07 12:48:28)
Czy jest sposob na obsluge wyjatkow funkcji wbudowanych w php. Chodzi mi o include ('plik'), gdzie po wpisaniu nieistniejacego pliku bede mogl wykonac wlasna funkcje a nie ze parser informuje mnie o tym ze pliku nie znaleziono.

  1. <?php 
  2.  
  3. class IOException extends Exception {}
  4.  
  5. function ExceptionHandler( $sReson = null, $sException = 'Exception' ) {
  6.  throw new $sException( $sReson );
  7. }
  8.  
  9. try {
  10.  
  11.  $aFile = File( 'hwao' ) or ExceptionHandler();
  12.  
  13. }
  14. catch( exception $ex ) {
  15.  echo $ex;
  16. }
  17.  
  18. try {
  19.  $aFile = file( 'cos' ) or ExceptionHandler( 'Plik nie istnieje!', 'IOException' );
  20. }
  21. catch( IOException $e ) {
  22. echo $e;
  23. }
  24. catch( Exception( $e ) {
  25. echo $e;
  26. }
  27.  
  28. // Bardziej hard mozna tak
  29. function exFile( $sFile ) {
  30.  return file( $sFile ) or ExceptionHandler( 'Plik nie istnieje!', 'IOException' );
  31. }
  32.  
  33. try {
  34. exFile( 'aaa' );
  35. }
  36. catch( exception $e ) {
  37. echo $e;
  38. }
  39. ?>


Jak zwykle inprowizuje i nie sprawdzalem tego.. ale powino dzialac.
dtb
  1. <?php
  2. if (file_exists ('plik')) include ('plik');
  3. else throw new Exeception ('Nima pliku');
  4. ?>
krzysztof f.
To że na każdy problem istnieje wiele rozwiązań, nie znaczy że są one dobre.

Jednym z podstawowych zadań i ogromną zaletą wyjątków jest oddzielenie kodu odpowiedzialnego za obsługę błędów od kodu aplikacji. Pozbycie się uciążliwego i koszmarnego do opanowania w bardziej skomplikowanych przypadkach tzw. inline error checking, na rzecz bloku try.

Więc propozycje typu:
Cytat
  1. <?php
  2. if (file_exists ('plik')) include ('plik');
  3. else throw new Exeception ('Nima pliku');
  4. ?>

Cytat
  1. <?php
  2. try {
  3.  
  4.  $aFile = File( 'hwao' ) or ExceptionHandler();
  5.  
  6. }
  7. catch( exception $ex ) {
  8.  echo $ex;
  9. }
  10. ?>

Nie są najlepszym rozwiązaniem i mijają się z celem. Zamiast pomagać, wprowadzają niepotrzebne zamieszanie utrudniając czytanie kodu.
dtb
napisz wlasny odpowiednik funkcji:
  1. <?php
  2.  
  3. function dolacz ($plik)
  4. {
  5. if (file_exists ($plik)) include ($plik);
  6. else throw new Exeception ('Nima pliku '. $plik);
  7. }
  8.  
  9. ?>



poprawiam
---
nospor
krzysztof f.
Cytat(dtb @ 2005-11-28 14:12:22)
napisz wlasny odpowiednik funkcji:
  1. <?php
  2.  
  3. function dolacz ($plik)
  4. {
  5. if (file_exists ($plik)) include ($plik);
  6. else throw new Exeception ('Nima pliku '. $plik);
  7. }
  8.  
  9. ?>

To też nie jest najlepszy pomysł i nie polecałbym go. Zaśmieca kod, który staje się niezrozumiały dla innych programistów, ze względu na przysłanianie standardowych funkcji własnymi nazwami.
Ozzy
Wszystko ok, tyle że klasa ErrorException już istnieje:
  1. <?php
  2. ReflectionClass::export('ErrorException');
  3. ?>
Ociu
Cytat(mike_mech @ 2005-11-07 12:57:20)
Poczytaj o funkcji __autoload() i poszukaj na forum. Temat był poruszany nie raz, jest pewna możliwość, ale jest trochę zabawy.
forum.php.pl :: search :: autoload

Funkcja __autoload() służy do automatycznego ładowania pliku.
Koledze chodzi właśnie o własne błęd z wyjątkami.
pozdrawiam
Ozzy
Ja drążę wypowiedź Krzystofa f. z ciekawym pomysłem.
Próbowałem go zastosować ale napotkalem na pewien problem:

  1. <?php
  2.  
  3. public function handleError($sErrorCode, $sErrorMsg, $sErrorFile, $sErrorLine) {
  4.  
  5. if(error_reporting() != 0) {
  6. throw new ErrorException($sErrorMsg, 0, $sErrorCode, $sErrorFile, $sErrorLine);
  7. } 
  8.  
  9. return true;
  10.  
  11. }
  12.  
  13. ?>


gdy handleError wyrzuca wyjątek to wykonywanie metody się konczy, a poniewaz nie zwrocila true to nie jest wykonywany dalszy kod. Ma ktoś pomysl jak to obejść?
dr_bonzo
Co ty chcesz obchodzic? Jak metoda wyrzuca wyjatek to nie moze jednoczesnie zwracac wartosci. Ta funkcja zamienia errory na wyjatki, ktore jest duzo prosciej obslugiwac. Obsluga wyjatkow + wyjatki oddziela obsluge "bledow" aplikacji o jej normalnego kodu. Nie musisz zwracac wartosci np. -1, NULL gdy nastapi jakis blad, tylko wyrzucach wyjatek i go lapiesz. Unikasz wielu zagniezdzonych ifow ktore testuja wynik funkcji na obecnosc bledow. Jak poznasz dobrze wyjatki to zobaczysz, ze nie bedziesz bez nich mogl zyc (no dobra -- chociaz pisac smile.gif).
Ozzy
bonzo, nie zrozumiałeś problemu. Wszystko od trzeciego zdania praktycznie nie wnosi nic do dyskusji i co więcej opiera się na błędnym założeniu.

Podejrzewam, że tylko krzychu zaczaci o czym myslę;)
W obecnej postaci ten sposób jest nie do zaakceptowania, ponieważ zatrzymuje egzekucję kodu po błędzie pomimo tego, że wyjątek został obslużony przez handleException (zatrzymuje go handleError).
Ociu
I kod powinien być zatrzymany. Jeśli masz błąd w aplikacji, to jaki jest cel dalszej egzystencji uruchamiania strony ?

A co do wzracania true. Jeżeli nie ma błędu, lub error_reporting nie jest ustawiony, wtedy moteda nie jest używana, czyli wzraca true.
dr_bonzo
Ozzy: Maual sie klania - http://pl.php.net/manual/en/function.set-e...ion-handler.php
Cytat
Sets the default exception handler if an exception is not caught within a try/catch block. Execution will stop after the exception_handler is called.


Wg. mnie exception handler nie powinien byc w ogole uzywany - wyrzucane wyjatki trzeba lapac i obslugiwac w kodzie programu. Przeciez brak polaczenia z baza nie powinien zamykac aplikacji -- przeciez mozna np. content wczytac z cache'u w plikach.

Cytat
Wszystko od trzeciego zdania praktycznie nie wnosi nic do dyskusji i co więcej opiera się na błędnym założeniu.
Hmmm, a ktore to bledne zalozenie?

Cytat
gdy handleError wyrzuca wyjątek to wykonywanie metody się konczy
to prawda -- tak dzialaja wyjatki.
Cytat
a poniewaz nie zwrocila true to nie jest wykonywany dalszy kod.

ktory dalszy kod? ten po wywolaniu funkcji ktora wyrzucila error, co spowodowalo wywolanie error_handlera?
A jak lapiesz ten wyjatek? Przechwytujesz go exc. handlerem?
Ozzy
W ogóle nie rozumiecie jaki jest problem, spróbujcie napisać to u siebie i sprawdzić jak działa...

W tej postaci jakiej jest, nawet niezłapany NOTICE zatrzymuje całą aplikację, co więcej, try trzeba by było robić dla kazdej linijki, ponieważ każdy nieobslużony błąd powodowalby zatrzymanie aplikacji, a zwykly błąd nie powoduje zatrzymania i nie powinien.
A taki jest Ociu sens, że większośc aplikacji potrafi się wykonać nawet gdy wystąpi błąd i nie powinna być zatrzymana.

Jak dla mnie cała koncepcja zamiany błędów na wyjątki w tej postaci jest błędna i ma więcej wad niż zalet. Nie widze dla niej zastosowania.
dr_bonzo
Cytat
nawet niezłapany NOTICE zatrzymuje całą aplikację, co więcej, try trzeba by było robić dla kazdej linijki
Bo trzeba go lapac! Nie try dla kazdje linijki - bo to nie sa errory tylko wyjatki - mozesz blokiem try objec wieksza ilosc kodu.

Dodaj do kodu krzysztofa
  1. <?php
  2. try
  3. {
  4. set_error_handler( array( new ErrorHandler(), 'handleError' ) );
  5. $x = file_get_contents( 'aaaa' );
  6. echo 'kontynuujemy';
  7. }
  8. catch ( Exception $e )
  9. {
  10. print( 'no i zlapalismy wyjatek: ' . $e->getMessage() . '<br />' );
  11. print( 'mozemy kontynuowac pisanie kodu...' );
  12. }
  13. ?>
i zobacz jak to dziala.
Ozzy
No to powodzenia w łapaniu każdego błędu.
dr_bonzo
Juz nie wiem jak ci wytluamczyc obsluge wyjatkow - dorwij kilka tutoriali, ksiazek, poczytaj, popatrz z na kod z wyjatkami...

Tu http://northslope.lap.pl/dev/jaco/wyjatki.phps jest przykladowy kod (nie dzialjacy!) prezentujacy wyjatki i ich oblsuge.
krzysztof f.
Easy guys... Proszę Was, taka dyskusja do niczego nie prowadzi.
Jak zwykle każdy ma trochę racji i wszystko da się rozwiązać, ale po kolei.

Cytat
Funkcja __autoload() służy do automatycznego ładowania pliku.
Koledze chodzi właśnie o własne błęd z wyjątkami.


Tak ale jej wykorzystanie do niczego nie prowadzi w tym konkretnym przypadku. Funkcja ta jest wywoływana w przypadku kiedy nie została znaleziona definicja klasy, a nie dołączany plik.

Cytat
Wszystko ok, tyle że klasa ErrorException już istnieje


No jakby nie było. A ta skąd się wzięła? Ktoś wie gdzie jest jej definicja? Swoją drogą kolejny przykład, który utwierdza mnie w przekonaniu, że brak przestrzeni nazw w php jest dużym minusem i powinny się pojawić w 6.

Cytat
W obecnej postaci ten sposób jest nie do zaakceptowania, ponieważ zatrzymuje egzekucję kodu po błędzie pomimo tego, że wyjątek został obsłużony przez handleException (zatrzymuje go handleError).


Jest to prawdę mówiąc dobra uwaga. Mój przykład wygląda tak, ponieważ osobiście mam bardzo restrykcyjne podejście do błędów. Staram się z takim samym naciskiem eliminować błędy typu fatal jak i notice. Myślę że takie podejście się sprawdza, ale rzeczywiście php w stosunku do pewnych błędów zachowuje się inaczej i pozwala aby wykonywanie kodu było kontynuowane.

Decydując się na wyjątki w pewnym sensie zrezygnowaliśmy z takiej możliwości. Czy to źle? czy dobrze? ..tutaj każdy będzie miał swoje zdanie.

Spróbujmy zrobić mały refactoring, aby rozwiązać problem jaki ma Ozzy.
Po pierwsze wyrzucając wyjątek w bloku try, nie mamy możliwości kontynuowania kodu w tymże bloku. Aplikacja będzie działać dalej, przechodząc do odpowiedniego bloku catch. Więc będziemy musieli zrezygnować z bloku try (tam gdzie nie jest konieczne jego używanie) i polegać na funkcji set_exception_handler.

Potrzebna zmiana dotyczy metody ErrorHandler::handleError()
  1. <?php
  2.  public function handleError( $iErrNo, $sErrStr, $sErrFile, $sErrLine )
  3.  {
  4. set_exception_handler( array( $this, 'handleException' ) );
  5.  
  6. if ( error_reporting() != 0 )
  7. {
  8.  if ($iErrNo & (E_NOTICE | E_USER_NOTICE)) {
  9. $this->handleException( new NoticeErrorException( $sErrStr, $iErrNo, $sErrFile, $sErrLine ) );
  10.  } else if ($iErrNo & (E_WARNING | E_USER_WARNING | E_CORE_WARNING | E_COMPILE_WARNING)) {
  11. $this->handleException( new WarningErrorException( $sErrStr, $iErrNo, $sErrFile, $sErrLine ) );
  12.  } else if ($iErrNo & (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR)) {
  13. throw new FatalErrorException( $sErrStr, $iErrNo, $sErrFile, $sErrLine ); 
  14.  } else {
  15. throw new UnknownErrorException( $sErrStr, $iErrNo, $sErrFile, $sErrLine );
  16.  }
  17.  
  18. } 
  19.  }
  20. ?>


Teraz np. kod
  1. <?php
  2. set_error_handler( array( new ErrorHandler(), 'handleError' ) );
  3. include 'wrong_file.inc.php';
  4. echo 'Kolejne instrukcje';
  5.  
  6. ?>

wykona się w całości. Natomiast ten
  1. <?php
  2. set_error_handler( array( new ErrorHandler(), 'handleError' ) );
  3. trigger_error( 'Błąd', E_USER_ERROR );
  4. echo 'Kolejne instrukcje';
  5.  
  6. ?>

z wiadomych przyczyn nie.

Jeśli spodziewalibyśmy się jakiegoś konkretnego wyjątku, który wymaga obsłużenia należałoby oczywiście bezwzględnie zamknąć taki kod w bloku try.
Ozzy
No i pięknie. Jak widać jest to możliwe:)
Jak będę miał trochę czasu to napiszę prototyp i potestuję, ale pomysł wydaje się jak najbardziej działający.
Na użycie operatora bitowego też bym nie wpadłsmile.gif

Dzięki Krzysiek:)

Jedno pytanie jeszcze: dlaczego set_exception_handler jest w handleError, a nie na przykład w konstruktorze?
Przy kilku błędach będzie kilkakrotnie wywoływana, czyż nie?

ErrorException został wprowadzony do SPL od php 5.1 i na razie nie ma go w dokumentacji, jedynie w Reflection API.
Nawet buga raportowałem, bo był jeszcze niedopracowany:)

Jest też możliwość korzystania z obydwu zalet, tzn. z bloku try i "cichego" radzenia sobie z błędem - można utworzyć publiczną metodę klasy ErrorHandler, która zmieniałaby zachowanie metody handleError, raz wyrzucając wyjątek, a raz wywołując handleException...
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.