Debugování a zpracování chyb
Knihovna Nette\Diagnostics\Debugger, která zdomácněla pod jménem Laděnka, je užitečnou každodenní pomocnicí PHP programátora. Pomůže vám
- rychle odhalit a opravit chyby
- logovat chyby
- jak vypisovat proměnné
- a měřit čas
PHP je jazyk na sekání těžko odhalitelných chyb jako stvořený, neboť dává vývojářům značnou volnost. O to cennější je ladící nástroj Nette\Diagnostics\Debugger. Mezi diagnostickými nástroji pro PHP představuje naprostou špičku. Pokud se dnes setkáváte s Laděnkou poprvé, tak věřte, že váš život se začne dělit na ten před Laděnkou a ten s ní. Vítejte v té lepší části!
Laděnku aktivujeme snadno, stačí do kódu, nejlépe hned za načtení frameworku, přidat:
use Nette\Diagnostics\Debugger;
Debugger::enable(); // aktivujeme Laděnku
První, čeho si můžete na stránce všimnout, Debugger Bar.
Debugger Bar
Debugger Bar je plovoucí panel, který se zobrazí v pravém dolním rohu stránky. Můžeme jej přesouvat myší a po znovunačtení stránky si bude pamatovat svou pozici.

Do Debugger Baru lze přidávat další užitečné panely. Řadu jich najdete v doplňcích, nebo si dokonce můžete napsat vlastní.
Vizualizace chyb a výjimek
Jistě dobře víte, jak PHP oznamuje chyby: do zdrojového kódu stránky vypíše něco takového:
Parse error: syntax error, unexpected T_STRING in DashboardPresenter.php on line 8
nebo při nezachycené výjimce:
Fatal error: Uncaught exception 'MemberAccessException' with message 'Call to undefined method Nette\Http\User::isLogedIn().' in D:\Web\Nette.web\libs\Nette\ObjectMixin.php:69 Stack trace: #0 D:\Web\Nette.web\libs\Nette\Object.php(80): ObjectMixin::call(Object(User), 'isLogedIn', Array) #1 [internal function]: Object->__call('isLogedIn', Array) #2 D:\Web\cd-collection\app\presenters\DashboardPresenter.php(12): User->isLogedIn() #3 D:\Web\Nette.web\libs\Nette\Application\Presenter.php(154): DashboardPresenter->startup() #4 D:\Web\Nette.web\libs\Nette\Application\Application.php(148): Presenter->run(Object(PresenterRequest)) #5 D:\Web\cd-collection\app\bootstrap.php(54): Application->run() #6 D:\Web\cd-collection\index.php(13): require('D:\Web\cd-colle...') #7 {main} thrown in D:\Web\Nette.web\libs\Nette\ObjectMixin.php on line 69
V takovém výpisu se zorientovat není zrovna snadné. Pokud zapneme Laděnku, chyba nebo výjimka se zobrazí dočista v jiné podobě:
Chybová zpráva doslova křičí, vidíme část zdrojového kódu se zvýrazněným řádkem, kde k chybě došlo, informace Call to undefined method Nette\Http\User::isLogedIn() srozumitelně vysvětluje, o jakou chybu se jedná. Celá stránka je navíc živá, můžeme se proklikávat k větším podrobnostem. Zkuste si to.
A víte co? Tímto způsobem zachytí a zobrazí i fatální chyby. Bez nutnosti instalovat jakékoliv rozšíření.
Chyby jako překlep v názvu proměnné nebo pokus o otevření neexistujícího souboru generují hlášení úrovně E_NOTICE nebo E_WARNING. Ty lze v grafice stránky snadno přehlédnout, dokonce nemusí být viditelná vůbec (leda pohledem do kódu stránky).
Debugger::$strictMode = TRUE;
Abychom odhalili i překlepy při přiřazení do proměnné objektu, podědíme své třídy od Nette\Object.
Dále můžeme změnit hloubku zanoření pomocí Debugger::$maxDepth a délku zobrazovaných popisek pomocí Debugger::$maxLen. Nižší hodnoty laděnku přirozeně zrychlí.
Debugger::$maxDepth = 2; // default: 3
Debugger::$maxLen = 50; // default: 150
Produkční režim a logování chyb
Jak vidíte, Laděnka je poměrně výřečná, což lze ocenit ve vývojovém prostředí, zatímco na produkčním serveru by to způsobilo hotové neštěstí. Tam se totiž žádné ladící informace vypsat nesmí. Laděnka proto disponuje autodetekcí prostředí a pokud příklad spustíme na ostrém serveru, chyba se místo zobrazení zaloguje a návštěvník uvidí jen uživatelsky srozumitelnou hlášku:

Produkční režim potlačí zobrazování všech ladících informacích, které posíláme ven pomocí Debugger::dump() nebo Debugger::fireLog(), a samozřejmě
také všech chybových zpráv, které generuje PHP. Pokud jste tedy v kódu zapomněli nějaké
Debugger::dump($obj)
, nemusíte se obávat, na produkčním serveru se nic nevypíše.
K přepínání režimů slouží první parametr metody Debugger::enable()
, kde lze uvést buď konstantu
Debugger::PRODUCTION
nebo Debugger::DEVELOPMENT
.
Pokud jej neuvedeme, má výchozí hodnotu Debugger::DETECT
a v takovém případě se detekuje režim podle IP
adresy serveru – je-li dostupný přes veřejnou IP adresu, běží v produkčním režimu, je-li na lokální, tak ve
vývojářském. V drtivé většině případů tak není potřeba režim nastavovat a správně se rozezná podle toho, jestli
aplikaci spouštíme na svém lokálním serveru nebo v ostrém provozu.
V produkčním režimu Laděnka automaticky všechny chyby a zachycené výjimky zaznamenává do textového logu. Pokud
neurčíme jinak, půjde o soubor log/error.log
. Logování chyb je přitom nesmírně užitečné. Představte si,
že všichni uživatelé vaší aplikace jsou vlastně betatesteři, kteří zdarma odvádějí špičkovou práci v hledání
chyb a vy byste udělal hloupost, kdybyste jejich cenné reporty zahodil bez povšimnutí do odpadkového koše.
Pokud potřebujeme zalogovat vlastní zprávu nebo vámi zachycenou výjimku, použijeme k tomu metodu log()
:
Debugger::log('Doslo k necekane chybe'); // textová zpráva
try {
kritickaOperace();
} catch (Exception $e) {
Debugger::log($e); // logovat lze i výjimku
// nebo Debugger::log($e, Debugger::ERROR) odešle i e-mailovou notifikaci
}
Jiný adresář pro logování chyb lze nastavit druhým parametrem metody enable():
Debugger::enable(Debugger::DETECT, __DIR__ . '/mylog');
Pro skutečného profíka je error log klíčovým zdrojem informací a chce být ihned informován o každé nové chybě. Laděnka mu v tom vychází vstříc, umí totiž o novém záznamu v logu informovat e-mailem. Kam odesílat emaily určíme proměnnou $email:
Debugger::$email = 'admin@example.com';
Alternativně je také možné toto nastavit v konfiguračním souboru:
common:
nette:
debugger:
email: admin@example.com
Aby vám však nezaplavila e-mailovou schránku, pošle vždy pouze jednu zprávu a vytvoří soubor
email-sent
. Vývojář po přijetí e-mailové notifikace zkontroluje log, opraví aplikaci a smaže monitorovací
soubor, čímž se opět aktivuje odesílání e-mailů.
Dump proměnných
Každý ladič je dobrým kamarádem s funkcí var_dump, která podrobně vypíše
obsah proměnné. Bohužel v prostředí HTML výpis pozbude formátování a slije se do jednoho řádku, o sanitizaci HTML
kódu ani nemluvě. V praxi je nezbytné var_dump
nahradit šikovnější funkcí. Tou je
právě Debugger::dump()
$arr = array(10, 20.2, TRUE, NULL, 'hello');
Debugger::dump($arr);
// včetně jmenného prostoru Nette\Diagnostics\Debugger::dump($arr);
vygeneruje výstup:
<pre><span style="color:gray">array</span>(5) {
[0] => <span style="color:gray">int</span>(10)
[1] => <span style="color:gray">float</span>(20.2)
[2] => <span style="color:gray">bool</span>(true)
[3] => <span style="color:gray">NULL</span>
[4] => <span style="color:gray">string</span>(5) "hello"
}
</pre>
Měření času
Dalším užitečným nástrojem ladiče jsou stopky s přesností na mikrosekundy:
Debugger::timer();
// princi můj malinký spi, ptáčkové sladce již sní...
sleep(2);
$elapsed = Debugger::timer();
// $elapsed ? 2
Volitelným parametrem je možno dosáhnout vícenásobných měření.
Debugger::timer('page-generating');
// nějaký kód
Debugger::timer('rss-generating');
// nějaký kód
$rssElapsed = Debugger::timer('rss-generating');
$pageElapsed = Debugger::timer('page-generating');
Debugger::timer(); // zapne stopky
... // časově náročná operace
echo Debugger::timer(); // vypíše uplynulý čas v sekundách
Firebug a FireLogger
Ne vždy lze ladící informace posílat do okna prohlížeče. Týká se to AJAXových požadavků, generování XML výstupu či obrázků. V takovým případě můžeme zasílat zprávy samostatným kanálem do Firebugu. Chyby úrovně Notice a Warning jsou do okna Firebugu dokonce zasílány automaticky. Taktéž je možné logovat výjimky, které sice aplikace zachytila, ale stojí za to na ně upozornit.
Jak na to?
- stáhněte a pusťte si prohlížeč Firefox
- stáhněte si rozšíření Firebug
- stáhněte si rozšíření FireLogger
- restartujte prohlížeč, zapněte Firebug (klávesou F12) a povolte panely Síť (Net) a Logger
… Otevřete si náš prográmek a klikněte na panel Konzole. Ha! Sem se přesunula chybová hláška.
Protože Nette\Diagnostics\Debugger komunikuje s Firebugem přes HTTP hlavičky, je nutné volat logovací funkce ještě před tím, než PHP skript cokoliv odešle na výstup. Také je možné zapnout output buffering a tím výstup oddálit.
use Nette\Diagnostics\Debugger;
Debugger::fireLog('Hello World'); // vypíšeme řetězec do konzoly Firebugu
Debugger::fireLog($_SERVER); // do konzoly lze vypsat i pole nebo objekty
Debugger::fireLog(new Exception('Test Exception')); // či dokonce výjimky
Výsledek vypadá asi takto:

Laděnka k vašim službám
Nette Framework je koncipován jako otevřený framework. To znamená, že i jeho jednotlivé části můžete využívat samostatně ve svých aplikacích. To se týká i Nette\Diagnostics\Debugger. Nic vám nebrání si zpříjemnit život tím, že na začátku každého skriptu spustíte Laděnku.