Laděnka, jak se vám líbí?
Laděnka je užitečnou každodenní pomocnicí PHP programátora. Její oficiální jméno zní Nette::Debug.
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ě Debug::dump()
$arr = array(10, 20.2, TRUE, NULL, 'hello');
Debug::dump($arr);
// včetně jmenného prostoru Nette::Debug::dump($arr);
vygeneruje výstup:
array(5) { [0] => int(10) [1] => float(20.2) [2] => bool(true) [3] => NULL [4] => string(5) "hello" }
Dalším užitečným nástrojem ladiče jsou stopky s přesností na mikrosekundy:
Debug::timer();
// princi můj malinký spi, ptáčkové sladce již sní...
sleep(2);
$elapsed = Debug::timer();
// $elapsed ≈ 2
Tohle všechno jsou sice užitečné drobnosti, skutečným killerem je však jiná Laděnčina vlastnost. Zpráva o nezachycené výjimce nebo chybě poskytuje vývojáři důležité informace o tom, kde a proč k ní došlo. Standardní výstup v PHP vypadá asi takto:
Pusťme však ke slovu Laděnku. Po aktivaci příkazem
Debug::enable() nám předvede svou nejvíce sexy polohu:
Takto vypadá výjimka, takto vypadá vygenerovaná chyba. To je pak jiné ladění, co?
Snad nemusím dodávat, že vypisování chyb se nesmí nikdy dostat na produkční server. Je to výborná společnice na pracovišti, ale nikdy ji nesmíme brát sebou ven. Je totiž děsně ukecaná a vyzradí na vás úplně všechno (nicméně kdyby k tomu náhodou došlo, má integrovaný systém pro skrytí citlivých políček, např. hesel). Na produkčním serveru je však možné nechat výpisy ukládat do adresáře nebo odesílat administrátorovi emailem.
→ Download: Nette::Debug.zip, licence, překlad
(doplněno: mám na vás prosbu…)
Závěrem dibitýdne...
…tu mám ještě několik novinek. Především, nyní je možné do řetězce zapsat více modifikátorů najednou a teprve poté uvést jejich hodnoty:
dibi::query('
SELECT * FROM [table]
WHERE id = %i AND added > %d', $id, $time
);
Tímto se z modifikátorů stávají takové chytré „placeholders“. Už ani nemusí být umístěny zcela na konci řetězce.
Zároveň zavádím několik nových modifikátorů:
%expro expanzi pole do argumentů (Rubysté znají jako*[1,2,3])%ora%andpřed polem spojí jeho prvky s oddělovačem ‚AND‘ nebo ‚OR‘%lmta%ofspro přenositelné a možná snadnější nastaveníLIMITaOFFSET
Příklady použití:
$where[] = '[age] > 20';
$where[] = '[email] IS NOT NULL';
dibi::query('SELECT * FROM [table] WHERE %and', $where);
// SELECT * FROM [table] WHERE [age] > 20 AND [email] IS NOT NULL
nebo také
$where['age'] = 20;
$where['email'] = 'franta@example.com';
dibi::query('SELECT * FROM [table] WHERE %and', $where);
// SELECT * FROM [table] WHERE [age]=20 AND [email]='franta@example.com'
Limity a ofsety:
// with limit = 30, offset = 90
dibi::query('SELECT * FROM [products] %lmt %ofs', 30, 90);
// SELECT * FROM [products] LIMIT 30 OFFSET 90
// with offset = 100
dibi::query('SELECT * FROM [products] %ofs', 100);
// pro SQLite:
// SELECT * FROM [products] LIMIT -1 OFFSET 100
// pro MySQL:
// SELECT * FROM [products] LIMIT 18446744073709551615 OFFSET 100
// pro PostgreSQL:
// SELECT * FROM [products] OFFSET 100
Funkčnost modifikátorů %and & %or považujte
za experimentální. Očekávám vaše připomínky a podle nich ji případně
upravím. Jinak změny si vyžádaly větší zásah do zdrojového kódu
dibi-překladače a ačkoliv jsem všechno velmi důkladně otestoval, buďte při nasazování
pozorní.
Experiment DibiTable
Zkusil jsem do dibi přidat takovou hračku:
/*
CREATE TABLE [albums] (
[id] INTEGER NOT NULL PRIMARY KEY,
[artist] VARCHAR(100) NOT NULL,
[title] VARCHAR(100) NOT NULL)
*/
class Albums extends DibiTable
{
}
Třída Albums reprezentuje databázovou tabulku (pokud jméno
tabulky neurčíme, bude se detekovat z názvu třídy). Nad ní můžeme
vykonávat třeba tyto operace:
$albums = new Albums;
// přečtení jednoho záznamu podle primárního klíče:
$key = 5;
$data = $albums->fetch($key);
// smazání záznamu č. 7
$albums->delete(7);
// smazání více záznamů
$count = $albums->delete(array(1, 2, 4));
// úprava záznamu č. 4
$data->title = 'new title';
$albums->update(4, $data);
// obdobně jako u delete lze měnit více záznamů
// vložení záznamu
$id = $albums->insert($data);
Dále lze provádět jednoduché výběry:
// řádky s klíčem 2, 3 a 5
foreach ($albums->find(2, 3, 5) as $row) {
...
}
// vypiš celou tabulku v HTML
$albums->findAll()->dump();
// výběr s řazením podle artist, title
$albums->findAll('artist', 'title');
Jde o velmi jednoduchého pomocníka (tzv. helper) pro rutinní operace nad tabulkou. Tedy žádné složité ORM nebo ActiveRecords. Tuším se tomu říká Table Data Gateway. Funkce je čerstvá a ve stádiu experimentování.
Ještě drobnost. Dibi je už ze své podstaty zcela imunní vůči SQL injection, takže se jich nemusíte obávat:
$key = '3 OR 1=1'; // podvrh
$albums->delete($key);
// --> DELETE FROM [albums] WHERE [product_id] = 3
Extrémě rychlý "load SQL file"
Tak hele, v šest ráno po mně nemůžete chtít kultivovaný titulek, spokojte se i s tímto.
K zálohování nebo přenášení databází mezi více servery se
používá tzv. SQL dump. Jde o textový soubor obsahující popis struktury
i obsahu tabulek ve formě série SQL příkazů. K jeho generování
z příkazové řádky je určen nástroj mysqldump, na hostingu
se obvykle používá interaktivní phpMyAdmin.
Aplikaci phpMyAdmin lze použít i k obnovení ze zálohy, tj. načtení SQL dumpu. Bohužel se s tím neskutečně párá a proces trvá moc dlouho. Databáze obsahující jen pár tisíc záznamů nelze takto vůbec importovat – to dřív vyprší časový limit běhu PHP skriptu.
Pokusil jsem se kdysi napsat rychlejší importér a povedlo se. Co phpMyAdmin louská dlouhé minuty, zvládne tento za zlomek sekundy. Nástroj jsem nyní začlenil do dibi a používá se takto:
dibi::connect();
dibi::loadFile('dump.sql');
Lze načítat i komprimovaný soubor:
dibi::loadFile('compress.zlib://dump.sql.gz');
Soubor se čte postupně, takže nevadí, když je větší než dostupná paměť.
Metoda vrací počet vykonaných příkazů. V případě chyby vyhodí výjimku. Ještě zdůrazňuji, že je určen pro SQL dump ve formátu, který generují zmíněné nástroje.
Tip: SQL dump generujte se zaškrtnutou volbou „Rozšířené inserty“ (Extended inserts), má to zásadní vliv na rychlost načítání, ať už používáte jakýkoliv importér.
Nette::Object - taťka všech objektů
Je známo, že objekty se do jazyka PHP dostaly spíš jako nezvaní hosté.
Docela příznačným rozdílem mezi jazykem, který byl jako objektový již
navrhován, a jazykem, který se jím stal k velkému překvapení samotných
tvůrců, je v absenci resp. přítomnosti společného předka všech tříd.
Object Pascal deklaruje TObject, DOT.NET má
System.Object a v Javě nebo Ruby stojí v hierarchii nejvýše
Object (je delikátní, že nejzákladnější třída
bývá pojmenována objekt). V PHP nic takového neexistuje.
Základní třída obvykle deklaruje metody pro sebereflexi. Například
metodu vracející název třídy. Je to sice detail, ale vždycky mi připadalo
tuze ošklivé mixovat hezký objektový kód s voláním pro-objektové
funkce get_class:
$a = $obj->myMethod();
$class = $obj->getClass(); // hezké
$class = get_class($obj); // ošklivé


pokračování...