Adminer 5.1.1 - refaktoring

Adminer prodělal asi největší refaktoring od přidání podpory pro ne-MySQL databáze.

PHP

Začalo to celkem nevinným komentářem, který mě vybídl k lepšímu popisu tvarů polí v dokumentačních komentářích. To bylo většinou jakž-takž popsané v textu komentáře, ale ne zcela přesně. Např. informace o sloupci v databázi mají trochu jiný tvar než informace o parametru uložené procedury, některé funkce ale dokáží pracovat s oběma formáty. K popisu jsem použil syntaxi PHPStanu a pro časté typy jsem vytvořil aliasy. Vyzkoušel jsem i náhradu polí za value objekty, ale nepracuje se s tím tak pohodlně, tak jsem zůstal u polí.

Původní kód:
/** @return array ["Timing" => [], "Event" => [], "Type" => []] */
Nový kód:
/** @return array{Timing: list<string>, Event: list<string>, Type: list<string>} */

Když už jsem měl typy popsané, tak jsem si řekl, že by bylo škoda je nevyužít pro kontrolu Admineru PHPStanem. Jenže PHPStan vyžaduje opakování názvu parametru ve značce @param a Adminer uváděl jen typ podle pořadí parametru. Nesnáším opakování, tak jsem se rozhodl, že typ parametrů deklaruji přímo v kódu a značku @param nechám jen v případě, že parametr bude mít nějaký popis nebo typ specifičtější než PHP (např. literal-string nebo list<int>). Totéž jsem udělal i pro návratový typ a vlastnosti objektů. Použil jsem syntaxi PHP 7.4, kterou nyní spuštění Admineru ze zdrojových kódů vyžaduje. Při kompilaci do jednoho souboru se typy zase odstraní, čímž jednosouborová verze zůstala kompatibilní s PHP 5.3. Vedlejší efekt je i ten, že v některých zákoutích kódu může být typ špatně, takže nová verze tyto případy hned nerozbije. Byl jsem opravdu hodně vděčný za bohaté end-to-end testy, díky kterým jsem většinu typových chyb snad našel.

Původní kód:
/** Get INI boolean value
* @param string
* @return bool
*/
function ini_bool($ini)
Nový kód:
/** Get INI boolean value */
function ini_bool(string $ini): bool

PHPStan našel i řadu dalších, většinou drobných chyb. Kód Admineru teď s několika výjimkami prochází PHPStan levelem 6.

Adminer pro každou z 12 podporovaných PHP extenzi (+ další v pluginech) definuje třídu Db. Těm jsem nyní dal společného předka, což kód v některých případech i zjednodušilo. Zvažoval jsem použití rozhraní, protože Db pro MySQLi je potomek třídy mysqli, takže jiného předka mít nemůže. Ale kód by pak musel mít stejné typy jako mysqli, takže by nebyl kompatibilní zároveň se starými a novými verzemi PHP. Rozhraní navíc nedovoluje deklarovat vlastnosti. Lepší by bylo dědění z mysqli zahodit a předělat to na proxy.

Změnil jsem implementaci třídy Plugins pro nahrávání pluginů. Tu jsem tak nesnášel, že před verzí 5.1 jsem jejích zkompilovaných 7 kB odmítl dát přímo do Admineru a uživatele pluginů ji nutil nahrát zvlášť. Původně byla tato třída potomek třídy Adminer a po zavolání pluginů zavolala výchozí implementaci ve třídě Adminer. Teď jsem to změnil tak, že třída Plugins prostě zaregistruje Adminer jako nejspodnější plugin a volá ji stejně jako všechny ostatní pluginy. Kód třídy Adminer jsem musel změnit tak, aby metody místo na $this volala na adminer(), což je svým způsobem i přesnější. Je z toho totiž jasné, že se zavolají nejprve pluginy a ne nutně implementace ve třídě Adminer.

Původní kód:
function name() {
	$args = func_get_args();
	return $this->applyPlugin(__FUNCTION__, $args);
}

function credentials() {
	$args = func_get_args();
	return $this->applyPlugin(__FUNCTION__, $args);
}

// kilometr dalšího kódu na stejné brdo
Nový kód:
function __call(string $name, array $args) {
	foreach ($this->hooks[$name] as $plugin) {
		$value = call_user_func_array(array($plugin, $name), $args);
		if ($value !== null) {
			return $value;
		}
	}
}

Smazal jsem všechny globální proměnné. V minulosti jich měl Adminer maximálně 23.

Kód ve verzi 4.8.1:
global $adminer, $connection, $driver, $drivers, $edit_functions, $enum_length, $error, $functions, $grouping, $HTTPS, $inout, $jush, $LANG, $langs, $on_actions, $permanent, $structured_types, $has_token, $token, $translations, $types, $unsigned, $VERSION;
Nový kód:
// nic

Přestal jsem ignorovat chybu Trying to access array offset on null, kterou PHP vyvolá třeba s kódem $_GET["where"]["_id"]. Našlo to jeden okrajový problém.

JavaScript

Do JavaScriptu jsem přidal 'use strict', což si v kódu nevyžádalo žádnou změnu.

CSS

Adminer používá obrázky prakticky jen pro tlačítka ve změně struktury tabulky. Byly vložené pomocí <input type="image">, což komplikovalo jejich stylování. Autoři designů si s tím i tak poradili, ale v nové verzi vkládám obrázky přes CSS background-image, což stylování zjednodušuje. Stávající designy jsem na to přehodil. Při kompilaci jsem url(plus.gif) změnil na url(data:image/gif;base64,...), což dovolilo odstranit kód pro servírování obrázků Adminerem. Pak jsem to zjedodušil ještě víc, obrázky inlinoval ručně a z repozitáře je smazal. Stejně se prakticky nemění a v případě potřeby je jde z CSS zase vytáhnout, upravit a vložit zpátky. Při vývoji Admineru mám vypnuté kešování a tato změna odstranila kratičké probliknutí před tím, než se obrázky stáhly.

Do CSS jsem doplnil alespoň dvě proměnné pro výchozí barvu pozadí a popředí a ve stávajících barevných designech ji nadefinoval. Důvodem je, že někdy ve výchozím vzhledu potřebuji tyto barvy použít na dalších místech a pokud s tím designy nepočítají, tak se rozbijí. Do budoucna to také umožní jednodušší vytváření nových designů.

Původní kód:
/* default.css */
body { background: #fff; }
.footer > div { background: #fff; }
#tables a { background: #fff; }
.footer { border-top-color: rgba(255, 255, 255, .7); }

/* dark.css */
body { background: #002240; }
.footer > div { background: #002240; }
#tables a { background: #002240; }
.footer { border-top-color: rgba(0, 34, 64, .7); }
Nový kód:
/* default.css */
html { --bg: #fff; }
body { background: var(--bg); }
.footer > div { background: var(--bg); }
#tables a { background: var(--bg); }
.footer { border-top-color: rgb(from var(--bg) r g b / .7); }

/* dark.css */
html { --bg: #002240; }

Za všimnutí stojí hlavně použití funkce rgb(from).

Testy

Přidal jsem si skript na vytvoření stejného testu pro všechny databáze a přidal s ním několik dalších testů. Doplnil jsem také alespoň základní test pro Elasticsearch.

Rozčilovalo mě, že screenshoty na homepage nejsou pro aktuální verzi, ale samozřejmě se mi je nechce dělat s každou novou verzí znovu. Tak jsem si napsal test, který mi je udělá.

Funkční změny

Žádná z těchto změn by se neměla navenek nijak projevit, proto nedošlo k žádnému bombastickému zvýšení čísla verze. Viditelných změn je jen pár:

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

Diskuse

salko1:

MySQL, problem ohladom cudzich klucov.

Ke je uz nastavene ON DELETE CASCADE, tak 5.1.0 ukaze, je CASCADE, ale 5.1.1 ukazuje, ze je RESTRICT.

Pri pokuse ALTER TABLE uplne vynecha ON DELETE CASCADE (aj ON UPDATE ignoruje). 5.1.0 funguje spravne.

ikona Jakub Vrána OpenID:

Díky za upozornění, včera jsem to opravil a brzy vydám novou verzi.

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

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