Lekce 13: Výjimky
- Co je to vlastně výjimka? Je to způsob, jak ošetřovat chyby.
- Pokud někde v kódu nastane chyba a výjimka to zachytí, tak se současný kód přeruší a vykoná se jiný, který je jakoby v záloze, kdyby nastala chyba.
Takže uživatel nemusí vůbec vědět o tom, že chyba nastala, protože program si ji dokáže ošetřit.
Jak na výjimky
- Pro výjimky slouží hlavní třída Exception, která je přímo v PHP. Pokud by jsme chtěli vytvořit vlastní výjimku, tak třída musí opět dědit z Exception.
To si ukážeme o něco níže.
- Základní struktůra vypadá takto:
try
{
// kod, kde muze dojit k vyhozeni vyjimky
}
catch(Exception $e) // pokud nastane vyjimka
{
echo "Výjimka: ".$e; // vypise se zprava o chybe
}
- Bloky mezi
try označují část kódu, kde obvykle může dojít k chybě. Část s
catch zachycuje typ výjimky a následně její zpracování.
- Nyní než si ukážem tvorbu vlastní výjimky, tak si rozmyslete, jestli je to opravdu nutné, protože PHP krom základní
Exception disponuje dalšími výjimkami.
Tak se pojďme podívat, jestli by nám nějaká nevyhovovala.
- Výjímky jsou jako strom, úplně na vrchu je již zmíněná
Exception a od té se dělí na dvě další a ty opět na další.
+ Exception - hlavní výjímka, která zachycuje všechny chyby
|
+----+ LogicException - předvídatelné již při návrhu programu
| |
| + BadFunctionCallException - chyba při volání funkce, funkce nenalezena, volání nepovoleno
| |
| +----- BadMethodCallException - totéž pro metody
| |
| +----- InvalidArgumentException - špatný argument předaný funkci
| |
| +----- OutOfRangeException - index mimo rozsah pole či kolekce
| |
| +----- LengthException - hodnota překračuje povolenou délku
| |
| +----- DomainException - hodnota nespadá do požadované domény, rozsahu
|
+ RuntimeException - zjistitelné pouze za běhu programu
| |
| + OverflowException - přetečení bufferu či aritmetické operace; více dat než očekáváno
| |
| +----- UnderflowException - podtečení bufferu či aritmetické operace; méně dat než očekáváno
| |
| +----- OutOfBoundsException - index mimo rozsah pole či kolekce
| |
| +----- RangeException - hodnota nespadá do požadovaného rozsahu
| |
| +----- UnexpectedValueException - neočekávaná hodnota (např. návratová hodnota funkce)
|
-----+
- Jak vidíte, tak výjimek neni zrovna málo a ve většině případech si vyberete.
Dodatek: Ve většině případů, lze chybu ošetřit bez výjímky a to pomocí podmínek.
- Lze si novou výjimku i zaregistrovat pomocí
throw.
try
{
$zprava = "Nějaký text";
throw new Exception($zprava);
echo "bagr"; // bagr se uz nevypise, protoze byla zachycena vyjimka
}
catch (Exception $e)
{
echo "Výjimka: ".$e->getMessage(); // vypise: "Výjimka: Nějaký text"
}
- V bloku
catch si můžete všimnou odkazu na funkci
getMessage(). Ta je umístěna ve tříde
Exception, která by měla vypadat nějak takto:
class Exception
{
protected $message = "Unknown exception"; // zprava vyjimky
protected $code = 0; // kod vyjimky
protected $file; // soubor, kde byla vyjimka zachycena
protected $line; // radek, kde byla vyjimka zachycena
public function __construct($message = null, $code = 0);
final function getMessage(); // zprava vyjimky
final function getCode(); // kod vyjimky
final function getFile(); // soubor
final function getLine(); // radek
public function __toString(); // formatovani retezce pro vystup
}
- Samozřejmě lze použít více bloku
catch na jeden
try.
- Protože čím je náročnejší a delší script, tím se zvyšuje riziko vyhození výjimky a možnost, že nějakou nezachytíme.
- Rovnou si i ukážem, jak si vytvořit vlastní výjimku.
// seznam vlastnich vyjimek
class MojeVyj_1 extends Exception {} // vyjimka 1, ktera dedi z hlavni tridy Exception
class MojeVyj_2 extends MojeVyj_1 {} // vyjimka 2, ktera dedi z vyjimka 1
class MojeVyj_3 extends MojeVyj_2 {} // vyjimka 3, ktera dedi z vyjimka 2
function faktorial($cislo)
{
if ($cislo < 0)
{
throw new MojeVyj_1; // pokud cislo bude mensi jak 0
}
else if ($cislo == 0 || $cislo == 1)
{
return $cislo; // pokud cislo bude 0 nebo 1
}
else if ($cislo > 170)
{
throw new MojeVyj_3; // pokud cislo bude vetsi, nez 170
}
else
{
// jinak se provede rekurze funkce
return $cislo * faktorial($cislo - 1);
}
}
try // testovaci blok
{
$cislo = 5;
$faktorial = faktorial($cislo);
echo $cislo." = ".$faktorial;
}
catch(MojeVyj_2 $e) // pokud cislo bude vetsi nez 170
{
echo "Příliš velká hodnota!";
}
catch(MojeVyj_1 $e) // pokud cislo bude mensi jak 0
{
echo "Špatná hodnota!";
}
catch (Exception $e) // pokud nastane chyba mimo nase vyjimky
{
echo "Vyskytla se jiná chyba!";
}
- Ke konci bych ještě zmínil, že je možné vyvolat výjimku třeba v konstruktoru třídy. To znamená, že dokud konstruktor neproběhne celý,
tak objekt neexistuje, takže ani destruktor se nezavolá.
class ChybnaTrida
{
public function __construct()
{
throw new Exception; // vyvola vyjimku po vytvoreni objektu tridy
}
public function __destruct() // neprobehne
{
echo "Zde nic neni";
}
}
try
{
$pom = new ChybnaTrida(); // zkusi vytvorit objekt tridy
}
catch(Exception $e) { } // nic se nestane
Volání výjimek
- PHP umožňuje nastavit výchozí funkci, která bude automaticky volána, pokud výjimka nebude zachycena.
- Funkce se jmenuje set_exception_handler() a má pouze jeden parametr a to název funkce.
Pozor: Tato funkce musí být v kódu uvedena dříve, než set_exception_handler()!
function automat($exception) // funkce, ktera bude automaticky volana
{
echo "Zpráva výjimky: ".$exception->getMessage();
}
// nastavi vychozi funkci, pro volani nezachycene vyjimky
set_exception_handler('automat');
throw new Exception('Nezachycená výjimka'); // zachytavac vyjimky
- Pokud funkci
set_exception_handler() přiřadíme nějakou proměnnou, tak to má i jisté výhody.
- Naše definované funkce jsou uloženy v zásobníku, takže můžete obnovit starou funkci, buď uložením další kopie staré funkce do zásobníku nebo vyzvednutím ze zásobníku.
function automat($exception) // nase lehce upravena funkce
{
echo "Zpráva výjimky: ".$exception->getMessage();
echo "Kód výjimky: ".$exception->getCode();
}
// zde se to trochu lisi, jakmile nastaveni putuje do promenne, tak putuje do zasobniku,
// takze stare funkce lze obnovit
$funkce = set_exception_handler('automat');
throw new Exception('Nějaká chyba', 20); // zachytavac vyjimky i s kodem chyby
- Obnovení funkce he možné dvěmi způsoby.
// prvni moznost - ulozime starou kopii funkce do zasobniku
set_exception_handler($funkce);
// druha moznost - vyzvedneme ze zasobniku
restore_exception_handler();
- Funkce
restore_exception_handler() je pro nás nová, dělá pouze to, že ze zásobníku vyzvedáva funkce výjimek.
- Touto funkcí se moc zabývat nebudem, ale je dobré vědět, že něco takového existuje.
Dobrovolný domácí úkol
- V této lekci žádný dávat nebudu, ani mě už momentálně nic nenapadá na výjimky.