V současné době jsou problémy s připojením k databázi. Umím v PHP reagovat na chybu připojení ukončením pomocí "break". Tím se další zpracování zastaví. Lze nějak pokračovat výpisem stránky s vynecháním částí pracujících s databází? Tedy aby stránka vypadala kompletní, např. jen s poznámkou "tady kus chybí kvůli nedostupnosti db". :-) Zkoušel jsem i hledat na netu, ale buďto hledám špatně, nebo jsem to přehlédl. Jsem jen "programátor amatér"... Díky.
http://php.net/manual/en/language.exceptions.php
Break sám o sobě zastavuje skript či cyklus. Takže je jasné, že po jeho zavolání skript končí.
Zmíněný kód se používá pouze tam, kde se provádí sekvenční zpracování. To znamená, že pokud nastane chyba, tak končí.
Pro dynamické zpracování je potřeba použít jiný způsob.
Klasicky se používají podmínky. Skoro každá funkce vrací návratovou hodnotu. Dle ni se dá zjistit, zda funkce proběhla v pořádku nebo ne.
V případě databáze se při neúspěchu vrací false. Takže pak stačí podmínkami ošetřit danou situaci.
if ( mysql_connect() ) {
// pripojeni k databazi probehlo v poradku, muzeme pokracovat.
} else {
// nastala chyba pri pripojeni, zobrazime chybovou hlasku nebo alternativni text.
// chybu muzeme doplnit o mysql_error(), která zobrazi duvod chyby.
}
Nově lze u PHP5 použít i exception. Viz Kit. Tady záleží, zda funkce či kód je schopen vyvolat exception.
Zde se pak využívá toho, že tuto výjimku zachytíš a provedeš vlastní obsluhu dané chyby.
// Drobná ukázka, jak by mohl být napsán kousek aplikace
// s parametrizovaným dotazem SQLite přes PDO.
// Trochu jsem to osekával, tak jsem snad nenadělal moc chyb.
// Skript se neukončí, pokračuje normálně dál.
try {
if(!@$db=new PDO('sqlite:'.$data_file))
throw new Exception();
if(!$select=$db->prepare('SELECT text FROM pokus WHERE id=?'))
throw new Exception();
if(!$data=$select->execute(array($key)))
throw new Exception();
$pole=$data->fetch();
print_r($pole);
} catch (Exception $e) {
echo "Chybí data";
}
// Samozřejmě se vloudila chybička. Posílám znovu fungující kód.
// Ošetření je možné udělat všelijak, je to jen ukázka.
<?php
define('NL',"\n");
$data_file='pokus.sqlite';
$key=20;
try {
if(!@$db=new PDO('sqlite:'.$data_file))
throw new Exception('Nelze otevřít databázi');
if(!$select=$db->prepare('SELECT text FROM pokus WHERE id=?'))
throw new Exception('Chyba v SELECTu');
if(!$select->execute(array($key)))
throw new Exception('SELECT neproveden');
if(!$pole=$select->fetch())
throw new Exception('Záznam nenalezen');
print_r($pole);
} catch (Exception $e) {
echo $e->getMessage().NL;
}
?>
Díky za rady. Jak jsem psal - jsem jen programátor amatér, takže nejsrozumitelnější mi je podmínka if / else. Exception budu muset teprve nastudovat - to vidím prvně... Jestli jsem to správně pochopil - připojení k db vrací chybu jako kterýkoliv jiný příkaz a stejně tak to lze různými způsoby ošetřit. Není tedy nutné chybu připojení ukončovat příkazem break...
Pochopil jsi to správně.
Ten @ před mysql_connect() dej až ve chvíli, kdy to budeš mít odladěno a ošetřeny chyby. Zavináč zamezí výstupu chybových hlášek. Když se pak něco pokazí (nejede DB, uděláš chybu v SQL apod), špatně se to hledá.
S výjimkami jsem začal pracovat teprve nedávno a hned jsem si je oblíbil, protože zjednodušují aplikaci. Bohužel jedou až od PHP verze 5, takže zrovna na své doméně je použít nemohu.
Znám víc lidí (včetně mě), kteří se dali do používání výjimek v PHP (přecejenom zkušenosti z jiných jazyků, které výjimky podporují měl každý dobré). Ve výsledku ale výjimky už nepoužívá zase zpátky nikdo z nich (a já jenom na opravdu malých projektech) -- nejsou totiž zřejmě úplně doladěné (nebo je s nimi třeba pracovat jinak než ostatních jazycích a nepřišel jsem na to jak?) -- z nějakého důvodu se totiž v jistých situacích perou s include/require. Na druhou stranu, defaultní výpis stacku je pro ladění super :).
Freeze: Díky za upozornění, zkusím pár testů. Také se mi zdálo podivné chování ErrorException, protože jsem nedokázal zachytit hlášení, které běžně vypisuje. I s PDOException jsem měl nějaké potíže. Zatím to přičítám spíš nedostatku zkušeností. Napsal jsem si vlastní DbException a FileException a fungují mi podle mých představ.
Jako u všeho, i výjimky je nutné používat s mírou. Je to taková lepší náhražka goto. Když se udrží na uzdě, mohou udělat dobrou službu. Třeba to bude v PHP6 vyladěné lépe.
Použití výjimek je potřeba si osvojit. Kdo je v PHP nepochopí, tak bude zklamán a bude se i divit, proč to nejde.
V prvé řadě je třeba si uvědomit, zda je možné vyvolat výjimku. Nahrazení if/else za try/catch někde nemusí stačit, pač funkce výjimku nemusí vracet a může tak dojít k neočekáváním situacím.
V druhé řadě Exception je třída a je možné z ní dědit. Použití catch (Exception $e) je podstatě univerzální zápis a mělo by fungovat i na zděděné třídy. Naopak to však nelze. Catch (ErrorException $e) bude fungovat pouze na vyvolání výjimky z třídy ErrorException. Ostatní budou bez povšimnuty.
Je možné použít vícenásobnou detekci try {} catch (DBException $e) { //chyba z DB } catch (CacheException $e) { //chyba pri pouziti cache } catch (Exception $e) { //vseobecna chyba }
Je třeba si uvědomit, kde všude se zachytávač používá. Pokud funkce má vlastní zachytávač, tak funguje pouze v něm. Centrální zachytávač tuto výjimku už nedetekuje. Lze to obejít tak, že uvnitř funkce znovu zavoláme tutéž výjimku catch (Exception $e) { throw new Exception($e->getMessages()); }
Kdo chce být PHP5 cool, tak může vše vyexceptionovat a to včetně obyčejných PHP chyb. Tady se pak používá set_error_handler(), kde mu zavoláme throw ;) Příklad si můžete vzít z Nette Debugger, který i zapomenutý středník považuje za RSOD (červená obrazovka z laděnky) ;)
Až po předposlední odstavec vše zvládám, byla to otázka jednoho víkendu a jedné asi 700 řádkové aplikace, na které jsem se vyřádil. Příklad, který jsem uvedl, jsem záměrně ořezal, aby to bylo alespoň trochu pochopitelné.
Některé výjimky ošetřit nejdou a je mi divné, že se dá zachytit i chybějící středník. To mi už připadá zbytečné. Je ale fakt, že na ladění detailů se třeba ErrorException hodí docela dobře.
Zarazilo mě však, že výjimky blbnou v kombinaci s include/require. Dala by se ta chyba nějak blíže specifikovat?
Co konkrétně myslíš, že blbne s include? Níže uvedený kód normálně funguje. Vypíše pouze "Chyba".
index.php:
<?php
try {
include "test.php";
} catch ( Exception $e ) {
echo $e->getMessage();
}
?>
test.php:
<?php
throw new Exception("Chyba");
echo "Test";
?>
Freeze napsal: "... z nějakého důvodu se totiž v jistých situacích perou s include/require ...", tak jsem chtěl vědět, ve kterých situacích. Je jasné, že v podobně jednoduchých případech to fungovat bude.
Tím podivným chováním výjimek jsem myslel nesmyslný stack a jejich vyskakování v místech, kde nevznikly.
Obávám se ale, že nějaký deterministický příklad, který by ukázal, kde přesně se výjimky v php chovají podivně nemám (zkusím se kdyžtak začátkem týdne podívat po starších zdrojácích, kde se to původně využívalo a následně zařízlo).
Výsledné chování totiž zřejmě byla souhra několika okolností -- jednak násobné include/require, zároveň error_handler (s E_ALL), který výjimky vyhazoval a autoload pro třídy. Když potom vznikla nějaká chyba (typicky "method on not object") v metodě jiné třídy, která se volala z nižší úrovně zanoření v include, výsledná výjimka vyskakovala na úrovni nejvyššího include (podobně obsahovala stack jenom po první include) a zřejmě se úplně ignoroval zbytek vnořeného kódu. (A mimo to vypsaly i klasické chybové hlášky, jako by error_handler neexistoval.) Verze php tuším 5.2.6 bez nějakých optimalizací.
Je samozřejmě možné, že některé aspekty vůbec neměly na výsledný problém vliv, případně byl způsoben něčím jiným, než samotnou implementací výjimek v php. Fakt, že problém nepodařilo najít a jako řešení postaločilo "vypnutí" výjimek v error_handleru ve mě ale moc důvěry nevyvolává (zrušení výjimek se následně samozřejmě neobešlo bez pár možná až přiliš defenzivních isset/empty ve vnitřních funkcích). Toť vše :)
Jo takto. Tak s tím souhlasím. Když se vyvolá výjimka a potřebuji stack, tak se občas vypíše něco jiného než bych očekával a pak je složité vysledovat průběh chyby. Buď je to nedoladěné v PHP nebo nedoladěné v kódu. Když se do kódu implementuje všechno možné, tak z toho může vzniknout i zmatek ;)
Říká se, že se nemají využívat všechny možné vlastnosti programovacího jazyka, že je důležité držet se jednoduchosti. Platí to zejména u tak komplexních jazyků, jako je třeba C++ nebo PHP. U takového Lispu se dá jít až do absurdna. Přežije to právě proto, že je jednoduchý.
Možná to blbnutí souvisí s
http://www.php.net/manual/en/function.require.php
require() is identical to include() except upon failure it will also produce a fatal E_COMPILE_ERROR level error. In other words, it will halt the script whereas include() only emits a warning (E_WARNING) which allows the script to continue.
To znamená, že include/require nějakým způsobem ovlivňují chování zásobníku s chybami, což může v některých situacích způsobit zmíněné potíže.