Nette Framework: Odvšivujeme

Říká se, že v každém programu je alespoň jedna chyba. Používáte nástroje, které vám pomohou chyby zavčasu odhalit? V druhém dílu obsáhlého seriálu o Nette Frameworku vám takové nástroje a techniky představíme. Možná se budete divit, proč je už dávno nepoužíváte ve svých projektech.

Seriál: Začínáme s Nette Framework (17 dílů)

  1. Nette Framework: zvyšte svoji produktivitu 10.3.2009
  2. Nette Framework: Odvšivujeme 17.3.2009
  3. Nette Framework: MVC & MVP 24.3.2009
  4. Nette Framework: Refactoring 31.3.2009
  5. Nette Framework: Chytré šablony 7.4.2009
  6. Nette Framework: adresářová struktura aplikace 14.4.2009
  7. Nette Framework: AJAX 21.4.2009
  8. Nette Framework: AJAX (pokračování) 28.4.2009
  9. Nette Framework: AJAX (dokončení) 5.5.2009
  10. Nette Framework: Sessions 12.5.2009
  11. Nette Framework: Přihlašování uživatelů 19.5.2009
  12. Nette Framework: Ověřování oprávnění a role 26.5.2009
  13. Nette Framework: Neprůstřelné formuláře 2.6.2009
  14. Nette Framework: Neprůstřelné formuláře II 9.6.2009
  15. Nette Framework: Neprůstřelné formuláře III 16.6.2009
  16. Nette Framework: Cache 23.6.2009
  17. Nette Framework: Co se do seriálu nevešlo? 30.6.2009

Možná byste rádi, aby se tento díl jmenoval Vytvoření Facebooku za 15 minut s Nette a vy byste mohli po jeho přečtení a odeslání pochvalného komentáře rovnou vytvořit dokonalou webovou aplikaci. Samozřejmě, mohlo by tomu tak být. Ale jak vás znám, respektive jak znám sám sebe, tak bychom při programování té dokonalé webové aplikace nasekali mraky chyb. A hlavně těch nenápadných, které se nejhůře hledají.

Disclaimer: Autor článku je hlavním vývojářem frameworku Nette.

Přičemž PHP je jazyk na sekání chyb jako stvořený, neboť dává vývojáři značnou volnost. Jak tomu udělat přítrž a jak začít psát programy bez těžko odhalitelných chyb? Stačí si nastavit přísnější laťku a dodržovat určitá pravidla. Zkusím být konkrétní.

Podívejte se na tento jednoduchý prográmek s převodníkem délkových jednotek. Z důvodu ladění budeme chtít, aby při každém převodu byl vypsán backtrace. Protože obsahuje citlivé informace, může být zobrazen jen vývojáři, kterého detekujeme podle IP adresy

// příznak DEBUG nastavíme pouze pro vývojáře
if (getenv('REMOTE_ADDR') === '192.168.0.5') {
        define('DEBUG', TRUE);
}

class Converter
{
        public $from = 'mm';

        public $to = 'mm';

        private static $units = array(
                'km' => 1000000,
                'm' => 1000,
                'cm' => 10,
                'mm' => 1,
                'inch' => 25.4,
                'mile' => 1609344,
        );

        function convert($value)
        {
                if (DEBUG) {
                        debug_print_backtrace();
                }
                return $value * self::$units[$this->from] / self::$units[$this->to];
        }
}

$converter = new Converter;
$converter->form = 'inch';
$converter->to = 'cm';
echo $converter->convert(123); // přepočítá 123 palců na centimetry 

Projděte kód do všech koutů, najdete nějaký bug? Dvě malé chyby tam jsou, to mohu prozradit: převodník nebude přepočítávat palce, nýbrž milimetry a citlivé debugovací informace se vypíší všem, nezávisle na IP adrese.

Jak je to možné? Jednak jsem omylem místo $converter->from napsal $converter->form, což je zrakem téměř nepostižitelná chyba, neboť mozek je uvyklý vídat obojí. Záludnost chyby spočívá hlavně v tom, že na ni PHP nijak neupozorní (z hlediska jazyka to není chyba) a stojí hodně námahy ji vypátrat. Druhou chybou je vyhodnocení nedefinované konstanty, kterou PHP z historických důvodů považuje za řetězec, takže podmínka if (DEBUG) se vyhodnotí kladně vždy. Naštěstí v tomto případě PHP vygeneruje zprávu E_NOTICE.

Jaká laťka a jaká pravidla mi pomohou najít a eliminovat tyto chyby? To je nejspíš zřejmé:

  1. dopřejeme sluchu chybám, které PHP generuje
  2. přimějeme třídu upozorňovat na použití neinicializovaných proměnných

Chyby úrovně E_NOTICE

Na uvedeném příkladě vidíte, že je nutné bedlivě sledovat chyby úrovně E_NOTICE. Vnímám je jako upozornění na nejzávažnější chyby v kódu. Je proto obrovským omylem začátečníků se domnívat, že tyto chyby mohou zahazovat, ať už metodou error_reporting, nebo mnohem hůře pomocí zavináče. To si profík tvořící Facebook za 15 minut přece nemůže dovolit!

Abychom mohli chyby všech úrovní sledovat, musíme psát kód tak, aby je sám negeneroval. V uvedeném příkladu to znamená například přidat kontrolu, jestli převodní jednotky vůbec známe – např. isset(self::$units[$this->from]). Přičemž nesmíme nikdy používat @ pro zahození chyby (kromě velmi výjimečných situací). A nejde tady jen o náš kód, musíme si totiž vybírat a používat takové knihovny, které tyto pravidla také dodržují (například Nette Framework, Zend Framework, dibi, …).

Striktní třídy

Třídu Converter přimějeme upozorňovat na použití neinicializovaných proměnných pomocí overloadingu, konkrétně implementací magických metod __get() a __set(). Mohou například vyhodit výjimku a překlep $converter->form bude ihned odhalen.

Není to náhodou seriál o Nette Framework?

Jistě, máte pravdu, na framework zatím řeč nepřišla. Nicméně úvod byl nutný. Filosofie přísnější laťky a zmíněných pravidel se totiž táhne celým frameworkem, její otisk najdeme v jeho každičkém bitu. Je to dokonce vědecky prokázáno. A jak vám potvrdí každý vývojář, který weby s Nette Frameworkem tvoří, striktnost mu ušetřila hodně času, který nemusel zbytečně prosedět u počítače, a mohl jej využít mnohem lépe, třeba nějakým příjemnějším prosezením u počítače.

Minule jsem vám sliboval seznámení s Laděnkou aka třídou NetteDebug. Společně s ní se přijde představit ještě třída NetteObject, která má ambici stát se prapředkem všech vašich tříd. Rozšiřuje totiž objektový model PHP o pár vychytávek, jednou z nich je právě zmíněná striktnost v používání nedeklarovaných proměnných. Přidejme tedy na začátek programu:

require 'Nette/loader.php';

// pokud používáte verzi pro PHP 5.3, odkomentujte následující řádek:
// use NetteDebug, NetteObject;

Debug::enable(); // aktivuje Laděnku 

a udělejme ze třídy Converter potomka NetteObject:

class Converter extends Object
{
        ... 

Zatímco dosud kód tiše (ale chybně) proběhl, teď už je situace jiná:

Nette 201

Laděnka označila místo, kde je překlep.

Třída NetteObject učinila Converter striktnějším a na použití nedeklarované proměnné reagoval vyhozením výjimky, kterou NetteDebug zobrazil. Řádek s fatálním překlepem je odhalen a označen, textová zpráva situaci popisuje slovy: Cannot assign to an undeclared property Converter::$form (nelze zapsat do nedeklarované proměnné Converter::$form). Programátor může promptně zareagovat. Chybu, které by si dlouho nemusel ani všimnout a jen za velkého úsilí by ji odhaloval, dostal srozumitelně naservírovanou na červeném podnose.

Poznámka: předpokládám, že příklady zkoušíte na lokálním HTTP serveru. Pokud používáte ostrý server, místo červené stránky vidíte jen prázdné bílé nic. To je v pořádku, doporučuji se proto buď přesunout na lokální server, nebo řádek Debug::enable() změnit na  Debug::enable(Debug::DEVELOPMENT);.

Všechny třídy Nette Frameworku jsou potomky třídy NetteObject, takže vám automaticky nabízejí její vyšší komfort. Upozorňování na nedeklarované proměnné přitom není zdaleka jedinou schopností této třídy, ostatní si však nechám do dalších pokračování.

Po opravení form na from se dostáváme k chybě s použitím nedefinované konstanty. Laděnka aktivovala reportování všech chyb, proto ji v prohlížeči vidíme:

Chybová hláška

Protože nejde o fatální chybu, jakou byla předchozí nezachycená výjimka, nepoužívá se k vizualizaci červená stránka, ale jen textová noticka. Ve složitější grafice je možno takovou chybu snadno přehlédnout, dokonce nemusí být viditelná vůbec (leda pohledem do kódu stránky). Laděnka se potutelně usmívá, protože má šikovné řešení.

Jako prohlížeč si otevřete Firefox a stáhněte si do něj pluginy

Ve Firefoxu zapněte Firebug a povolte panel Síť (Net), čímž aktivujete i FirePHP. Otevřete si náš prográmek a klikněte na panel Konzole. Ha! Sem se přesunula chybová hláška.

Spolupráce NetteDebug a Firefoxu

Spolupráce NetteDebug a Firefoxu

Komunikace mezi Laděnkou a konzolí Firebugu probíhá v HTTP hlavičkách. Před předáním zprávy proto nesmí být odeslán do prohlížeče žádný výstup. To lze zajistit například zavoláním metody ob_start na začátku skriptu. Pokud už k odeslání výstupu došlo, chybové zprávy se budou vypisovat klasicky do stránky.

Opravíme tedy chybu při práci s konstantou a zároveň, když už si Laděnka tak dobře rozumí s Firefoxem, co kdybychom ladící informaci místo do stránky poslali rovnou do konzole Firebugu? K tomu nám poslouží metoda  Debug::fireLog().

function convert($value)
{
        if (defined('DEBUG') && DEBUG) {
                Debug::fireLog('Byl volán Converter::convert()');
        }
        return $value * self::$units[$this->from] / @self::$units[$this->to];
} 

Metoda Debug::fireLog() vypíše do konzole prostou textovou zprávu. Malým trikem tam však můžeme poslat celý backtrace a vytvořit tak plnohodnotnou Firefoxí náhradu za původní debug_print_bac­ktrace()

if (defined('DEBUG') && DEBUG) {
        Debug::fireLog(new Exception('Byl volán Converter::convert()'));
} 
Zalogování vzniklé výjimky

Ještě je třeba zmínit metodu Debug::dump() pro výpis (tzv. dumpování) proměnných

<style>
pre span { color: blue }
</style>

<?php
Debug::dump($units); 

generující formátovaný a ošetřený HTML výstup

Výpis pole s převodem jednotek

a nebo stopky ukryté v metodě Debug::timer()

Debug::timer(); // zapne stopky

... // časově náročná operace

echo Debug::timer(); // vypíše uplynulý čas v sekundách 

Produkční vs. vývojový režim

Jak jste si mohli všimnout, Laděnka umí být poměrně výřečná. Její „červená smrt“ zobrazuje úseky kódu, kde se mohou nacházet citlivé informace. To se nakonec týká všech ladících informacích, které posíláme ven pomocí Debug::dump() nebo Debug::fireLog(), a samozřejmě také všech chybových zpráv, které generuje PHP.

Co lze ocenit ve vývojovém prostředí, by způsobilo hotové neštěstí na produkčním serveru. Tam se totiž žádné ladící informace vypsat nesmí. Laděnka se proto umí přepínat mezi produkčním a vývojovým režimem. Vývojový režim jsme používali až dosud a poctivě zobrazoval všechny ladící zprávy. Produkční režim naopak zobrazování úplně všech zpráv vypne. Pokud jste tedy v kódu zapomněli nějaké Debug::dump($obj), nemusíte se obávat, na produkčním serveru se nic nevypíše. Nepošlou se ani žádné informace do konzole Firebugu, nezobrazí se ani chybové hlášky PHP. Ty se naopak začnou logovat do souboru.

Možná jste díky předchozí zmínce vytušili, že k přepínání režimů slouží parametr Debug::DEVELOPMENT. A máte recht! Nicméně její výchozí hodnota se určuje autodetekcí podle IP adresy serveru. 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íte na svém lokálním serveru nebo v ostrém provozu.

Logování chyb

V produkčním režimu Laděnka všechny chyby zaznamenává do textového logu. Pokud neurčíme jinak, půjde o soubor php_error.log. Logování chyb je nesmírně užitečné. Představte si, že všichni uživatelé vaší aplikace jsou vlastně najatí betatesteři, kteří zdarma odvádějí špičkovou práci v hledání chyb a vy byste byl za hlupáka, kdybyste jejich cenné reporty zahodil bez povšimnutí do odpadkového koše.

Pro skutečného profíka je error log klíčovým dokumentem 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. Dokonce referuje o nezachytitelných fatálních chybách. Aby však nezahltila vývojářovu e-mailovou schránku, pošle vždy pouze jednu zprávu a vytvoří soubor php_error.log.monitor. 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ů.

K nastavení jména souboru s error logem a e-mailu vývojáře slouží druhý a třetí parametr metody  enable():

Debug::enable(NULL, 'logs/php_error.log', 'franta@example.com'); 

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. Takovým případem je i Laděnka nebo třída NetteObject. Nic vám nebrání si zpříjemnit život tím, že své třídy podědíte od NetteObject a na začátku každého skriptu spustíte Laděnku. Přitom nemusíte nic dalšího z frameworku využít.

Facebook za 15 minut

Gratuluji, myslím, že jste připraveni na tvorbu dokonalé webové aplikace. Vlastně ještě bychom si měli probrat slavnou architekturu MVC a říct si, proč MVC není tím, za co bývá obvykle považováno. Skvělé téma na příště!


Autor článku je vývojář na volné noze, specializuje se na návrh a programování moderních webových aplikací. Pravidelně pořádá školení pro tvůrce webových aplikací, vyvíjí open-source knihovny Texy, dibi a Nette Framework.

Používáte Laděnku?

David Grudl školí, je autorem PHP knihoven Nette Framework, databázové vrstvy dibi a formátovače HTML kódu Texy!.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 47

Přehled komentářů

alblaho Dobré počtení
Martin Soukup pana Grudla je pro PHP škoda
Hoween Re: pana Grudla je pro PHP škoda
Architekt Re: pana Grudla je pro PHP škoda
Čelo Re: pana Grudla je pro PHP škoda
Hoween Re: pana Grudla je pro PHP škoda
dc Re: pana Grudla je pro PHP škoda
alblaho Re: pana Grudla je pro PHP škoda
Martin Malý Re: pana Grudla je pro PHP škoda
alblaho Re: pana Grudla je pro PHP škoda
dc Re: pana Grudla je pro PHP škoda
alblaho Re: pana Grudla je pro PHP škoda
Ladislav Thon Re: pana Grudla je pro PHP škoda
Axiss Re: pana Grudla je pro PHP škoda
skrat BDD
Borek Bernard Re: BDD
Anonym kodovani?
David Grudl Re: kodovani?
Anonym Re: kodovani?
Tomáš Fejfar Re: kodovani?
Anonym Re: kodovani?
David Grudl Re: kodovani?
asdf Nejsou tam jen dvě malé chyby, jsou tam 3
David Grudl Re: Nejsou tam jen dvě malé chyby, jsou tam 3
asdf Re: Nejsou tam jen dvě malé chyby, jsou tam 3
Pichi Zmínka o PHP
Hoween Re: Zmínka o PHP
Pichi Re: Zmínka o PHP
wisdom kill script kitties
will Re: kill script kitties
wisdom Re: kill script kitties
Anonym Re: kill script kitties
Anonym Re: kill script kitties
K. R. Lispnik Re: kill script kitties
Anonym Re: kill script kitties
Anonym Re: kill script kitties
Ladislav Thon Re: kill script kitties
jan.letko Vyborne ...
Anonym RE: Nette Framework: Odvšivujeme
David Grudl RE: Nette Framework: Odvšivujeme
jirka d. RE: Nette Framework: Odvšivujeme
Anonym RE: Nette Framework: Odvšivujeme
Srigi RE: Nette Framework: Odvšivujeme
YangombiUmpakati perfektni clanek
kesspess Řešení záležitosti s konstantou
snehuliak otazka
Ab
Zdroj: https://www.zdrojak.cz/?p=2958