PHP triky

Weblog o elegantním programování v PHP pro mírně pokročilé

Adminer 4.7.6

Adminer 4.7.6 zrychluje formulář pro změnu struktury tabulky, který se zpomalil přidáním CSP ve verzi 4.4.0. Důvodem tehdy bylo nahrazení inline event handlerů za značky <script> v každém řádku formuláře, což obzvlášť Chrome značně zpomaluje. Vyřešil jsem to centralizací těchto značek. Tady si postesknu nad nemožností se striktním CSP používat inline event handlery. Ty sice vedou ke špagetovému kódu, ale na druhou stranu umožňují vytvářet komponenty, které mají HTML kód i navázání jeho událostí na jednom místě. Rozdělení těchto dvou věcí vede k větší složitosti – komponenta např. může vracet zvlášť HTML a zvlášť kód pro obsluhu událostí, což pak zase musí její uživatel nalepit na různá místa. Jiným řešením může být jakýsi vlastní DSL využívající data- atributy, což taky někde používám.

Další změny:

Uživatelé také opravili plugin login-ip a přidal jsem ukázku použití Adminer Editoru s SQLite (kombinuje plugin login-password-less se změnou použitého ovladače v přihlašovacím formuláři a nastavením cesty k databázi).

Jakub Vrána, Adminer, 30.1.2020, diskuse: 8 (nové: 8)

Abstrakce nad abstrakcí

Zapojil jsem se do vývoje jednoho webu postaveného na Nette. Nad některými změnami, které by mi obvykle zabraly minutu, jsem strávil třeba hodinu. Aplikace je podle mě udělaná dobře, logické celky jsou oddělené, kód se neopakuje, takže v tom to není. Problém je, že kvůli několika úrovním abstrakce musím ty abstrakce nejdřív pochopit a naučit se, jak se v nich dělá to, co potřebuji. Přesně vím, co chci udělat, i jak se to ve finále udělá, ale oříšek je to přes ty abstrakce protlačit.

Příkaz po připojení k databázi

Kupříkladu jsem chtěl po připojení k databázi provést nějaký SQL příkaz. Ve starých dobrých časech jsem si ze skriptů includoval connect.inc.php, ve kterém jsem zavolal mysql_connect, za který bych si přidal to mysql_query, které jsem potřeboval. V této aplikaci je do prezenteru injectován repozitář – o to se asi nějak stará samo Nette, fajn. Repozitáře mají rodičovskou třídu, do jejího konstruktoru jsem tedy zkusil doplnit volání toho příkazu, což ale samozřejmě vedlo k tomu, že jednotlivé repozitáře příkaz volaly nezávisle na sobě a ten se tak provedl opakovaně. Konstruktor repozitářů přijímá instanci Dibi. Kde ta se bere? Mohl bych příkaz doplnit tam. Ale v kódu se přímo nikde nevytváří, asi ji zase injectuje Nette. Přímo do existujícího kódu si tedy příkaz nikam nedoplním, musím nějak přesvědčit Nette nebo Dibi, aby to udělaly za mě. Říkám si, že asi nejsem první, kdo něco takového v Dibi potřebuje. Koukám se do dokumentace, ale tam o tom nic nenacházím. V API dokumentaci projíždím seznam metod a taky tam nic nevidím. Nakonec se koukám do zdrojáku a zjišťuji, že to Dibi skutečně podporuje. Ono to je nakonec i v té API dokumentaci, ale ne moc přehledně:

Ukázka dokumentace

Pak už na první dobrou hádám, že stačí tenhle parametr přidat do sekce dibi v config.neon (syntaxe NEON je další abstrakce, ale naštěstí jednoduchá) a kód skutečně funguje.

Co když by Dibi něco takového nepodporovalo? Jsem jeden z těch, kdo by poslal pull request, ale co když by ho autor nepřijal? Tuším, že i to by se dalo vyřešit. V Nette bych si jistě mohl udělat nějakou DibiFactory, která by ten příkaz zavolala tam. Ale je to další abstrakce, kterou bych si musel nejdřív nastudovat.

Doplňování jména

V jednom formuláři je políčko name, které značí název místa, a políčko email, do kterého se zadává e-mail člověka, který název zadává. Chrome (a asi i další prohlížeče) při vyplňování name nabízí jméno člověka, což samo o sobě moc nevadí. Co je horší, že při automatickém doplnění e-mailu se přepíše i to, co je zadané v názvu. Ten je navíc o stránku výš, takže si toho člověk ani nevšimne. Můj první nápad bylo políčko prostě přejmenovat. Ve starých dobrých časech by to bylo triviální. Políčko bych změnil na <input name="place" value="<?php echo htmlspecialchars($row["name"]); ?>"> a kód, který ho zpracovává, na $row["name"] = $_POST["place"]. Přejmenovávat sloupec v databázi by bylo velmi složité, protože se v aplikaci používá na mnoha různých místech. Jak tohle udělat v aplikaci postavené nad Nette? Nepřišel jsem na to. Aplikace (nebo Nette?) automaticky provazuje formulářová políčka s daty v databázi a formulářová data zase posílá podle názvu do databáze. Asi by někam šlo doplnit tohle přejmenování, ale minimálně by to působilo jako hack.

Napadla mě jednodušší věc – políčku přidat atribut <input autocomplete>, což by v běžném formuláři byla práce na 10 sekund. Ve fóru jsem našel, jak se to dělá pro celý formulář s tím, že „pro jednotlivé inputy to není problém“. Pro mě to problém je. V dokumentaci jsem nic o nastavování vlastních atributů elementům nenašel. V API dokumentaci jsem našel metodu getControl, která vrací HTML element pro políčko. To mi přišlo dost podobné jako getElementPrototype doporučované pro nastavení autocomplete celému formuláři, tak jsem ji zkusil použít. Bohužel to ale nezabralo kvůli tomu, že metoda generuje kód pokaždé znovu a změny v dříve vygenerovaném HTML přijdou vniveč. Naštěstí jsem našel metodu setAttribute (později přejmenovanou na setHtmlAttribute), která vedla ke kýženému cíli. Povedlo by se mi to i pomocí metody getControlPrototype, která ale bohužel není v dokumentaci TextBase, kde jsem našel getControl. Je to proto, že pochází z rodičovské třídy a TextBase ji na rozdíl od getControl nepřepisuje. Navíc rozdíl mezi těmito metodami není nijak zevrubně popsán – čekal bych odkaz z jedné na druhou s vysvětlením rozdílu.

Proměnná na devu

Na vývojovém serveru jsem si chtěl nastavit nějakou proměnnou a použít ji v šabloně. Ve starých dobrých časech bych do config_local.php přidal define("MEDIA_ROOT", "https://...") a kdekoliv v kódu ji potom prostě vypsal. Jak to udělat v Nette? V config.local.neon si můžu vlastní proměnnou přidat do sekce parameters, jak ji ale pak použít v šabloně? Nepřišel jsem na to, tak jsem zvolil velmi krkolomné řešení poslání parametru do App\Model\Configuration (který se injectuje do presenterů), tam jeho uložení a následné ruční předání z prezenteru do šablony. Tuším, že tohle je úplně špatné řešení a že existuje mnohem jednodušší, ale v dokumentaci se o sekci parameters vůbec nepíše, natož aby tam bylo vysvětlené, jak ty parametry použít v jiných částech aplikace.

Závěr

Tímhle článkem rozhodně nechci říct, že abstrakce jsou špatné nebo že použité části (aplikace samotná, Nette, Dibi) nejsou dobré. Chci říct to, že každá abstrakce může něco zjednodušit, ale nejdřív se ji člověk musí naučit. Je jistě velmi pohodlné nestarat se o vytváření všech objektů použitých v aplikaci, ale pokud to nějaká abstrakce dělá za mě, tak je najednou mnohem pracnější to přizpůsobit. Stejně tak je pohodlné nemuset každé formulářové políčko vytvářet ručně, ale když ho potřebuji změnit, tak je to s abstrakcí opět složitější. Nebo vlastně ani ne, ale nejdřív tu abstrakci musím podrobně znát.

Dokumentace je v takovém případě klíčová. V uživatelské dokumentaci Dibi a Nette jsem třikrát nenašel to, co jsem potřeboval (onConnect, setAttribute, parameters). V API dokumentaci dvakrát ano, ale jednou v nepřehledné formě (onConnect) a jednou jsem sešel na scestí (getControl místo getControlPrototype). Jednu věc jsem nenašel vůbec (správné použití parameters), ale možná jen nevím, kde hledat. Na to, abych dokumentaci sám vylepšil, se necítím dost erudovaný – např. BaseControl má ještě nedokumentovanou metodu getControlPart, jaký je její smysl?

Kolem Nette je velmi aktivní komunita, takže když bych se zeptal, asi bych se dobral často k lepšímu řešení, než jaké jsem našel já. Ale už jen dobře zformulovat dotaz je těžká práce a čekat na odpověď se mi nikdy nechce – mám nějaký problém a chci ho vyřešit hned. Radši budu hodinu hledat v dokumentaci (a u toho třeba dozvím i něco dalšího) než deset minut formulovat dotaz a pak hodinu nebo den čekat na odpověď.

Jakub Vrána, Řešení problému, 8.12.2019, diskuse: 16 (nové: 16)

Adminer 4.7.5

Adminer 4.7.4 opravil XSS, ke kterému došlo naštěstí pouze v případě, že Adminer byl dostupný na URL ve tvaru /data:.

Adminer 4.7.5 zlepšuje kompatibilitu s PostgreSQL 12, které odstranilo několik systémových sloupců (bug #719). Další změny jsou drobnější:

Jakub Vrána, Adminer, 12.11.2019, diskuse: 3 (nové: 3)

Adminer 4.7.3

Adminer 4.7.3 přináší tyto změny:

Jakub Vrána, Adminer, 27.8.2019, diskuse: 4 (nové: 4)

Adminer 4.7.2

Nová verze Admineru opravuje několik drobnějších chyb.

Také jsem přesunul hosting a demo na Váš hosting.

Jakub Vrána, Adminer, 18.7.2019, diskuse: 2 (nové: 2)

Starší články naleznete v archivu.

avatar © 2005-2020 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.