W trakcie wykonywania skryptów, często okazuje się, że natrafiamy na błędy. W takiej sytuacji skrypt kończy swoje działanie, a istnieją sytuacje, w których nie jest to konieczne np. praca na nieistniejącym pliku.
I tutaj z pomocą przychodzą nam wyjątki, które wyświetlają stosowną informację dla użytkownika w przypadku błędu i umożliwiają kontynuację pracy skryptu.
Mówimy, że wyjątki przechwytują błędy.
throw…, try…, catch…
Wyjątki są obiektami klasy Exception, które posiadają własny komunikat i kod błędu. Mogą one zostać wyłapane (try), rzucone (throw) i przechwycone (catch).
Wyobraźmy sobie, że mamy funkcję zwracającą odwrotność podanej liczby, która nie ma zabezpieczenia przed odwracaniem zera. Jak zabezpieczyć taki kod?
Przykład z nieobsłużonym błędem:
<?php
function odwrotnosc($x){
if(!$x){
throw new Exception('Dzielenie przez 0'); //jeżeli nastąpi dzielenie przez 0 lub null to wyrzucimy wyjątek
}
return 1/$x;
}
//przykład bez przechwycenia błędu
echo 'Odwrotność 5 to '.odwrotnosc(5).'<br>';
echo 'Odwrotność 0 to '.odwrotnosc(0).'<br>';//problematyczny fragment kodu, który generuje błąd
echo 'Odwrotność 7 to '.odwrotnosc(7).'<br>';//to nie zostanie wywołane
echo 'Skrypt kontynuuje swoją pracę'; //to też nie zostanie wykonane
?>
Wynik w przeglądarce:
Fatal error: Uncaught Exception: Dzielenie przez 0 in D:\xampp\htdocs\zajęcia\wyjatki.php:4 Stack trace: #0 D:\xampp\htdocs\zajęcia\wyjatki.php(20): odwrotnosc(0) #1 {main} thrown in D:\xampp\htdocs\zajęcia\wyjatki.php on line 4
<?php
function odwrotnosc($x){
if(!$x){
throw new Exception('Dzielenie przez 0');
}
return 1/$x;
}
//przykład z przechwyconym błędem
try{
echo 'Odwrotność 5 to '.odwrotnosc(5).'<br>';
echo 'Odwrotność 0 to '.odwrotnosc(0).'<br>';//problematyczny fragment kodu, który generuje wyjątek
echo 'Odwrotność 7 to '.odwrotnosc(7).'<br>';//to nie zostanie wywołane
} catch (Exception $e){
echo 'Przechwycony wyjątek: '.$e->getMessage().'<br>';
}
echo 'Skrypt kontynuuje swoją pracę';
?>
MOŻLIWE METODY WYJĄTKÓW
final public function getMessage(); // message of exception
final public function getCode(); // code of exception
final public function getFile(); // source filenamefinal public function getLine(); // source line
final public function getTrace(); // an array of the backtrace()
final public function getPrevious(); // previous exception
final public function getTraceAsString(); // formatted string of trace
try{
echo 'Odwrotność 5 to '.odwrotnosc(5).'<br>';
echo 'Odwrotność 0 to '.odwrotnosc(0).'<br>'
} catch (Exception $e){
echo 'Przechwycony wyjątek: '.$e->getMessage().' - metoda getMessage()<br>';
echo 'Przechwycony wyjątek: '.$e->getCode().' - metoda getCode()<br>';
echo 'Przechwycony wyjątek: '.$e->getFile().' - metoda getFile()<br>';
echo 'Przechwycony wyjątek: '.$e->getLine().' - metoda getLine()<br>';
echo 'Przechwycony wyjątek: '.var_dump($e->getTrace()).' - metoda getTrace()<br>';
echo 'Przechwycony wyjątek: '.$e->getPrevious().' - metoda getPrevious()<br>';
echo 'Przechwycony wyjątek: '.$e->getTraceAsString().' - metoda getTraceAsString()<br>';
}
echo 'Skrypt kontynuuje swoją pracę';
FINALLY
Finally służy do określenia kodu, który będzie wykonywany zawsze, niezależnie od wykrycia wyjątku.
try{
echo 'Odwrotność 5 to '.odwrotnosc(5).'<br>';
} catch (Exception $e){
echo 'Przechwycony wyjątek: '.$e->getMessage().' - metoda getMessage()<br>';
} finally{
echo 'konstrukcja finally, wyjątek nie zaistniał<br><br>';
}
try{
echo 'Odwrotność 0 to '.odwrotnosc(0).'<br>';
} catch (Exception $e){
echo 'Przechwycony wyjątek: '.$e->getMessage().' - metoda getMessage()<br>';
} finally{
echo 'konstrukcja finally, wyjątek zaistniał<br><br>';
}
echo 'Skrypt kontynuuje swoją pracę';
Należy jednak uważać podczas używania finally z return!
Jeżeli w bloku try… catch… zostanie wykryta instrukcja return, blok finally i tak zostanie wykonany. Dodatkowo, jeżeli blok finally też zawiera return, to zwrócona zostanie zawartość finally.
function test_f_r(){
try {
throw new Exception;
}catch(Exception $e){
echo "złapany wyjątek<br>";
return 1;
}finally{
echo "finally<br>";
return 2;
}
return 3;
}
//możemy zauważyć, że pomimo tego, że wyjątek został przechwycony zwracana jest wartość z bloku finally
$out=test_f_r();
var_dump($out);
echo '<br>Skrypt kontynuuje swoją pracę';
TWORZENIE WŁASNYCH WYJĄTKÓW
Wyjątki możemy tworzyć wg własnego uznania. Wystarczy, że stworzymy klasę dziedziczącą po klasie Exception.
class MyException extends Exception{
public function __construct(){
parent::__construct("To jest mój autorski wyjątek");
}
}
function test_MyException(){
throw new MyException;
}
try{
test_MyException();
}catch(MyException $e){
echo "błąd: ".$e->getMessage();
}
Zadanie:
Napisz funkcję, która będzie zwracała długość ciągu. W przypadku przekazania pustego ciągu lub null „rzuć” nowy wyjątek i wypisz „Ciąg jest pusty”. Za pomocą bloku try..catch.. przechwyć go.