Kontrola chyb

Školení, která pořádám

Článek vyšel v rámci PHP okénka na serveru Root.cz.

Dobrou zásadou při programování je ošetřovat případně vzniklé chyby. Naopak špatnou zásadou pro tvorbu elegantního kódu je dělat to za všech okolností přímo v kódu, protože tím dochází k jeho zamořování nepřehledným balastem.

<?php
// bez kontroly chyb
$result = mysql_query("SELECT * FROM tabulka");

// kontrola chyb přímo v kódu - nepřehledné
$result = mysql_query("SELECT * FROM tabulka");
if (!$result) {
    echo "<p>Chyba v SQL dotazu!</p>\n";
}

// ošetření je skryto v k tomu určené funkci
$result = mysql_q("SELECT * FROM tabulka");
?>

Druhý způsob je z mého pohledu vyloženě špatně, protože nosná myšlenka kódu je skryta mezi spoustou kódu pro ošetření chyb. Třetí způsob dotažený do důsledku podle mě také není ideální, protože místo standardních funkcí se všude používají funkce vlastní, což cizímu programátorovi znesnadňuje čtení a případné vytváření kompatibilního kódu. Pokud navíc ve funkci dojde k chybě, tak se dozvíme název souboru a číslo řádku s definicí funkce a ne místo, odkud byla volána. Toto místo můžeme zjistit např. funkcí debug_backtrace. V tomto konkrétním případě mi přijde nejlepší povolit standardní direktivu mysql.trace_mode a používat první způsob. Chyby v SQL dotazech se pak budou zpracovávat stejně jako ostatní PHP chyby.

V některých případech se dá bez kontroly chyb zcela obejít (např. když se nepodaří uložit informaci o shlédnutí stránky, tak tím návštěvníka určitě nebudeme obtěžovat), na kritických místech je ale správná kontrola chyb velice důležitá:

<?php
// špatný kód
mail("info@example.com", "Test", $_POST["zprava"]);
echo "<p>Děkujeme za odeslání zprávy.</p>\n";

// s ošetřením chyb
if (mail("info@example.com", "Test", $_POST["zprava"])) {
    echo "<p>Děkujeme za odeslání zprávy.</p>\n";
} else {
    echo "<p>Zprávu se nepodařilo odeslat, kontaktujte nás prosím <a href='mailto:info@example.com'>e-mailem</a>.</p>\n";
}
?>

Opět by samozřejmě bylo možné chybu ošetřit ve funkci za tím účelem vytvořené, pokud se ale ošetření kritické chyby liší podle kontextu, je asi nejlepší ho přeci jen dát přímo do kódu.

PHP 5 nabízí podobně jako řada dalších jazyků mechanismus výjimek. Ale vzhledem k tomu, že interní funkce PHP výjimky nepoužívají, nemusí být jejich volba tím nejlepším možným řešením, protože následně je nutné odděleně ošetřovat výjimky a standardní PHP chyby.

Málo věcí je trapnějších než zobrazování standardních chybových hlášek PHP typu Parse error: syntax error, unexpected … in … on line … přímo v prezentaci. Ve správně napsané aplikaci by k takovýmto chybám docházet vůbec nemělo, ale i tak je jistější zobrazování těchto chyb na produkčních serverech zakázat direktivou display_errors. I v aplikaci bez chyb totiž může dojít k zobrazení chybových hlášek při změně verze PHP nebo konfigurace. Spolu s vypnutím zobrazování chyb je vhodné zapnout direktivu error_log a vzniklý log čas od času (nebo pravidelně např. skriptem posílajícím mail) kontrolovat. To se ostatně hodí i při vývoji, protože chybové hlášky zobrazované přímo v prezentaci je možné za určitých okolností přehlédnout.

Jakub Vrána, Řešení problému, 25.4.2005, diskuse: 10 (nové: 0)

Diskuse

Nereknu:

Me osobne prijde ten treti zpusob nejidealnejsi. Prece jen pouziti funkce s oznacenim mysql_q, ktera vykonava praci za mysql_query neni prece jen tak spatne.
Za prve: Muzeme lepe zdelit uzivateli co se stalo, namisto chyboveho hlaseni MySQL
Za druhe: Ve funkci mysql_q muzeme pouzit funkci mail, ktera odesle na zadany e-mail report o chybe, ktera se prave stala. V reportu muze byt cokoli (cas chyby, jmeno souboru, kde se chyba stala, cislo radku, kde se spatne vykonal SQL prikaz)
Za treti: Pokud budu tvurcem vice aplikaci a budu chtit mit kontrolu nad jednotlivymi aplikacemi, tak pomoci reportu ve funkci mysql_q mohu okmazite zjistit, ze se neco "deje"
Co se tyce te spatne prehlednosti, myslim, ze neni problem v dokumentaci ci na zacatku kodu napsat, ze funkce mysql_q je zamenena za mysql_query z toho a toho duvodu. Osobne jsem videl mnohem horsi  a zavaznejsi nedostatky programatora, ktery z kodu udelal nectenou zmet znaku :)

afu:

Jak se díváte na konstrukci typu ...or die('text chyby'), případně přesměrování na stránku s chybovou hláškou?

zajímavá je i konstrukce do { ... } while (0); kdy se dovnitř bloku umístí operace s databází a v případě neúspěchu se nastaví určitá proměnná na určitou hodnotu a cyklus se breakne. Za koncem cyklu může být test na tu určitou proměnnou.

ikona Jakub Vrána OpenID:

Konstrukce "or die()" se mi nelíbí, protože se často zvrtne v pouhé ... or die('Chyba v SQL dotazu') a chyba je navíc málokdy tak fatální, že by musela znamenat ukončení skriptu. Navíc je slušné správně ukončit HTML kód, což jde zařídit funkcí register_shutdown_function(), ale málokdo to dělá.

Konstrukce "do { ... } while (0);" se mi také nelíbí, mnohem milejší mi je "if () { ... } elseif () { ... }". Právě zneužívání této konstrukce bylo jedním z důležitých argumentů v debatě vývojářů PHP o konstrukci goto. Pro tento způsob ošetření chyb se nejlépe hodí výjimky.

ikona spaze:

or die() se mi taky nelibi, nelze na to nijak rozumne povesit error handler -- neda se predefinovat chovani pri nastavsi chybe.

Šaman:

or die ('hlaska') jsem nejprve pouzival (je to jednoduche), pak jsem ho pouzivat prestal (chtel jsem ukoncit html kod) a ted se k nemu zase vracim, protoze vetsinou includuju obsah okna do nejakyho nadrizenyho okna (index.php mi nacte menu, vlozi vlastni obsah, treba prida i nejakou paticku apod.) Pak se o ukonceni (x)html stara ten script, ktery to cele sestavuje dohromady..

ikona Jakub Vrána OpenID:

Ale die() přece ukončí celý skript, nejen vložený soubor. Rozmysli si, jestli jsou chyby skutečně tak fatální, že nutně musí skript ukončit.

HTML kód můžeš ukončit pomocí register_shutdown_function().

nick:

Taky jde:
mysql_query(...)or print("Chyba!");

a moje oblibene:
mysql_query(...)or error();

a funkce:
function error(){
echo "<h2>SQL chyba:</h2>";
echo mysql_error();
}

carA:

Ahoj, chápu o čem je článek, ale o5 mě zarazila jedna věc, kterou jsem viděl už několikrát, nicméně na to nemám stále odpověď, proč vlastně se to tak dělá.

Jde o
mail("info@example.com", "Test", $_POST["zprava"]);
mail("info@example.com", "Test", $zprava;

Obsahující text v $_POST["zprava"] je třeba přece ošetřit např htmlspecialchars() a pod a pak ji uložit.

V tomto směru mi něco stále uniká, jelikož pořád čtu, že v zájmu kontroly dat a nebezpečí útoku je vhodné s formy takto postupovat. Napisšte mi prosím někdo vysvtlění. Děkuji

ikona Jakub Vrána OpenID:

V textovém e-mailu žádné speciální znaky ošetřované funkcí htmlspecialchars() nejsou. Tato funkce se používá pro ošetření textu při výpisu do HTML kódu.

Kit:

Zrovna jsem to řešil, protože jsem nic vhodného nenašel, tak to zkuste zhodnotit. Víc už se mi to zkrátit nepovedlo.

<?php
class DbException extends Exception {
  public function __construct($text=''){
    $err=error_get_last();
    parent::__construct($text.$err['message'],$err['type']);
  }
}

if(!
$result = mysql_query("SELECT * FROM tabulka")) throw new DbException();
?>

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.