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

PHP triky

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

Google Gemini: API

Odborník na umělou inteligenci a vynikající PHP programátor David Grudl před časem zveřejnil sneak peek pluginu do Admineru, který dovoluje sestavovat SQL dotazy pomocí AI. Navázal tak na svůj SQL Wizard. Plugin ale nikdy nepublikoval, tak si možná část lidí myslela, že to je fake. Řekl jsem si, jak by asi bylo složité to doopravdy udělat, a ukázalo se, že úplně triviální! Použil jsem Google Gemini a z jednoduchosti jeho nasazení jsem byl velmi mile překvapen. Nemusíte chodit do žádné Google Cloud Console (která je neuvěřitelně nepřehledná), ale prostě přímo v AI Studiu vygenerujete klíč a hned vidíte URL, na kterém ho můžete použít. Implementace v PHP je pak už jednoduchá:

<?php
/** Položení dotazu Google Gemini
* @param string Dotaz v přirozeném jazyce
* @param string Klíč získáte na https://aistudio.google.com/apikey
* @param string Dostupné modely: https://ai.google.dev/gemini-api/docs/models#available-models
* @return string Odpověď umělé inteligence
* @copyright Jakub Vrána, https://php.vrana.cz/
*/
function gemini($prompt, $apiKey, $model = "gemini-2.0-flash") {
    $context = stream_context_create(array("http" => array(
        "method" => "POST",
        "header" => array("User-Agent: PHP", "Content-Type: application/json"),
        "content" => '{"contents": [{"parts":[{"text": ' . json_encode($prompt) . '}]}]}',
    )));
    $response = json_decode(file_get_contents("https://generativelanguage.googleapis.com/v1beta/models/$model:generateContent?key=$apiKey", false, $context));
    return $response->candidates[0]->content->parts[0]->text;
}
?>

V pluginu pak dotazu od uživatele předřadím schéma databáze a přidám nějakou omáčku, aby to vracelo skutečně jen SQL dotaz, který pak prostě zobrazím. Funguje to naprosto neskutečně:

Poradí si to dokonce i s češtinou, umí to vytvářet i INSERT, přidávat indexy a já nevím, co ještě:

Plugin vyžaduje momentální vývojovou verzi Admineru, kam jsem přidal hook pro zobrazení promptu na tom správném místě, jinak si to žádnou změnu nevyžádalo.

Jakub Vrána, Seznámení s oblastí, 23.3.2025, diskuse: 0 (nové: 0)

GitHub API: Získání času modifikace Gistu

Dobrovolník ke všem pluginům v jejich seznamu přidal popis a datum poslední aktualizace – díky! Já jsem všechny autory pluginů obeslal pull-requesty s úpravou na Adminer 5. A tím se většina datumů poslední aktualizace stala neplatná.

Aktualizovat to celé ručně by byl nesmysl, tak jsem využil toho, že většina pluginů je k dispozici na GitHubu, některé na Gistu. Tím pádem se dá využít GitHub API a datum aktualizace získat přes něj. Např. pro Gist se to dá udělat takhle:

<?php
// zdrojem je normální URL s Gistem
if (preg_match('~^https://gist.github.com.*/(.+)$~', $url, $match)) {
    $gist = github_api("gists/$match[1]");
    echo $gist->history[0]->committed_at; // $gist->updated_at includes comments
}

function github_api($path) {
    $context = stream_context_create(array('http' => array(
        'header' => array("User-Agent: PHP"),
    )));
    return json_decode(file_get_contents("https://api.github.com/$path", false, $context));
}
?>

Při kladení požadavků musíme nastavit hlavičku User-Agent (na libovolnou hodnotu), což je takový chyták. Pro lepší zpracování chyb je vhodné přidat do kontextu ještě ignore_errors, díky čemuž získáme i stránky s chybami místo pouhého false. Hlavičky zprávy potom získáme v proměnné $http_response_header. Z nich se dozvíme třeba to, že u veřejných API (jako je tohle) má GitHub docela přísné limity – nějakých 60 požadavků za hodinu. Pokud chceme víc, dá se to nejjednodušeji udělat posláním tokenu, který si vygenerujeme v nastavení. Pak jen do pole s hlavičkami přidáme Authorization: Bearer ghp_... (nezapomenout na Bearer).

Jakub Vrána, Seznámení s oblastí, 19.3.2025, diskuse: 0 (nové: 0)

Adminer 5.0.6

Vyšel Adminer 5.0.6, který přináší tyto změny:

  • Možná trochu kontroverzní bude změna v zarovnávání čísel. Adminer Editor je odjakživa zarovnává doprava, Adminer je dával doleva. Ale zarovnání doprava je čitelnější, tak jsem to změnil i v Admineru (bug #912).
  • Pokud na sloupec v editaci najedete myší, zobrazí se jeho typ. Teď jsem tam přidal i komentář, na což dřív byl potřeba plugin.
  • Při exportu z SQL příkazu se nezapamatovalo jeho nastavení, takže ho bylo příště potřeba vybírat znovu. Nyní jsem ho synchronizoval s nastavením exportu ve výpisu dat.
  • Formulář s SQL příkazem Adminer pochopitelně odesílá metodou POST, ale JavaScriptem se ukládá i do URL, aby byl snadno k dispozici v historii prohlížeče. Tam se ukládal, jen pokud byl kratší než 2000 znaků, to ale pro některé servery bylo moc. Proto jsem to zkrátil na 500 (bug #917).
  • V SQL příkazu lze pomocí Ctrl+klik na klíčová slova otevřít jejich dokumentaci.
  • Opravil jsem i jednu bezpečnostní chybu, ve většině případů ale asi ne moc dobře zneužitelnou. Adminer si vytváří nějaké soubory v dočasném adresáři. Pokud do něj mají přístup i další uživatelé serveru (běžné např. na školách), tak můžou pod názvem souboru používaným Adminerem vytvořit symlink vedoucí na jiný soubor uživatele, pod kterým Adminer běží. Adminer pak přepíše tento soubor svými daty. Jednak v dnešní době virtuálních serverů nebývá zvykem, aby se o jeden server dělilo více uživatelů. Jednak webový server obvykle běží pod vyhrazeným uživatelem, který žádná práva zápisu kromě dočasných souborů nemá. I tak jsem chybu ale opravil. Jedno použití dočasných souborů je při kontrole nové verze – tam se soubor nejprve prostě smaže. V ostatních případech se nově kontroluje, jestli to není symlink. Do dočasného souboru se ukládají i počty neúspěšných pokusů o přihlášení jako obrana proti brute-force útokům. Pokud by někdo vytvořil symlink adminer.invalid, tak by tuto kontrolu vypnul. Adminer proto v takovém případě zkusí použít soubor s náhodnou koncovkou (bug #855).
  • V přihlašovacím formuláři se nově zobrazuje „MySQL / MariaDB“, aby uživatelům MariaDB bylo jasné, že to můžou použít. Po přihlášení se to změní buď na MySQL nebo na MariaDB. Stejnou změnu jsem mohl udělat i pro CockroachDB, ale text „PostgreSQL / CockroachDB“ byl prostě příliš dlouhý. Vzhledem k nižší známosti této databáze jsem nechal jen PostgreSQL, i když Adminer s CockroachDB plnohodnotně funguje.
  • Přidal jsem tmavý styl pro zvýrazňování syntaxe. Ten se zapíná podle nastavení operačního systému nebo pro tmavé vzhledy.
  • V tmavém vzhledu jsou nově tmavá i formulářová políčka. Měl jsem za to, že by to operační systém měl dělat automaticky, ale je potřeba mu to říct pomocí color-scheme.
  • Jak už jsem popisoval, Adminer nyní hledá vzhled i v souboru adminer-dark.css.
  • screenshot Pro pluginy jsem přidal metodu syntaxHighlighting, kterou lze změnit zvýrazňování syntaxe z výchozího JUSH. Použil jsem ji v novém pluginu CodeMirror, který zvýrazňování syntaxe včetně editace SQL příkazu přepne na CodeMirror. Adminer je zdá se navržen celkem dobře, protože tato změna si vyžádala jen minimální zásahy do zdrojáku. Killer feature CodeMirroru je napovídání klíčových slov, tabulek a sloupců. Časem chci tuto funkci dodělat i do JUSH.

Všechny autory pluginů jsem také obeslal pull requesty s doplněním jmenného prostoru. Většina už tuto změnu přijala, takže by pluginy měly jít přímo používat i v Admineru 5.

Funkci phpSrink jsem vyčlenil do samostatného repozitáře. Udělal jsem k tomu i demo, které díky Php-Wasm běží v prohlížeči uživatele. O tom zase někdy příště.

Na web jsem přidal stránku se seznamem útoků, proti kterým se Adminer explicitně brání.

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

Tmavý vzhled v CSS

V operačním systému si uživatel může nastavit, že preferuje tmavý vzhled. Ne webu to můžeme zohlednit ve stylu:

body { background: white; color: black; /* plus všechny další styly */ }
@media (prefers-color-scheme: dark) { /* měníme jen barvy */
	body { background: #111; color: #eee; }
}

No tak to bylo jednoduché! Od roku 2024 jde použít i funkce light-dark:

body { background: light-dark(white, #111); color: light-dark(black, #eee); }

Když se k tomu přidají CSS proměnné, tak je to úplná pohádka:

:root {
	--bg-color: white;
	--text-color: black;
}

@media (prefers-color-scheme: dark) {
	:root {
		--bg-color: #111;
		--text-color: #ccc;
	}
}

/* teď máme styl pěkně pohromadě a stejnou barvu můžeme použít na víc místech */
body { background: var(--bg-color); color: var(--text-color); }

Ovšem poněkud se to komplikuje, pokud máte nějaké závislosti, které na tmavý vzhled připravené nejsou, nebo pokud někdo závisí na vás. Zvýrazňovač syntaxe je jako mezi mlýnskými kameny. Potřeboval by si nastavit barvu pozadí, což je v pohodě u bloků, ale vypadá to blbě u inline kódu – barvu pozadí je v takovém případě lepší zachovat. V takové situaci je lepší styl rozdělit do dvou souborů:

/* default.css */
body { background: white; color: black; /* plus všechny další styly */ }

/* dark.css - měníme jen barvy */
body { background: #111; color: #eee; }

A stránka si pak řekne, který styl chce použít, a zda natvrdo, nebo podmíněně:

<link rel="stylesheet" href="default.css">
<link rel="stylesheet" href="dark.css"><!-- natvrdo -->
<link rel="stylesheet" media="(prefers-color-scheme: dark)" href="dark.css"><!-- podmíněně -->

Pokud má uživatel nastavený světlý vzhled, tak se stahuje míň dat. Ale pokud má nastavený tmavý, tak se kladou dva požadavky.

Pomocí color-scheme bychom operačnímu systému měli říct, jaké vzhledy podporujeme, a on nám pak dodá odpovídající formulářová políčka: <meta name="color-scheme" content="light dark">.

Adminer má situaci zapeklitou. Chci, aby sám od sebe fungoval při světlém i tmavém nastavení včetně zvýrazňování syntaxe. Ale uživatelé si pomocí adminer.css můžou definovat vlastní styl. V takovém případě na výchozí tmavý vzhled přepínat nechci, protože uživatelské styly na to nejsou připravené (mění většinou jen některé barvy, ne všechny). No pak ale zase existují tmavé styly, pro které chci zapnout aspoň tmavé zvýrazňování syntaxe. Jak jsem to vyřešil? Kromě adminer.css se nově hledá i soubor adminer-dark.css, který by měl používat tmavé styly. Dále je rozhodování takovéto:

  1. Nalezl se adminer.css i adminer-dark.css? Použij automatické přepínání podle operačního systému se všemi styly.
  2. Nalezl se pouze adminer.css? Použij jako základ světlý styl Admineru a světlý styl zvýrazňování syntaxe.
  3. Nalezl se pouze adminer-dark.css? Použij jako základ tmavý styl Admineru a tmavý styl zvýrazňování syntaxe.
  4. Nenalezl se ani jeden? Použij automatické přepínání podle operačního systému.

Takový vedlejší efekt toho je, že pokud vytvoříte prázdný soubor adminer-dark.css, tak bude Adminer v tmavém vzhledu nehledě na nastavení OS. Manuální přepínání vzhledu jsem přidal jako plugin.

Jakub Vrána, Seznámení s oblastí, 15.3.2025, diskuse: 0 (nové: 0)

Adminer 5.0.5 - tmavý vzhled

tmavý vzhled Vyšel Adminer 5.0.5. Přináší tyto novinky:

  • Na první pohled patrné je přidání tmavého vzhledu. Ten se automaticky zapíná podle nastavení prostředí. Tmavý styl se aktivuje, pouze pokud není použit adminer.css. Při této příležitosti jsem také prošel všechny designy v distribuci a zkontroloval, že fungují.
  • Adminer už dávno dovoluje zadávat hodnotu sloupců typu binary pomocí hexadecimálního řetězce. Nyní se při editaci zobrazuje informace o tom, jak bude zadaná hodnota převedená do databáze. Totéž se děje i se sloupci bit a geometry.
  • V MySQL se u binárních sloupců zobrazuje jejich výchozí hodnota.
  • V MySQL jde zadat výchozí hodnota sloupců typu json (předchozí verze to povolila pro text).
  • V MariaDB se hodnota NULL nezobrazuje jako výchozí (rozbila to verze 5.0.0).
  • V PostgreSQL přes PDO se správně ošetřují binární hodnoty (bug #218).
  • Při připojení ke CockroachDB (přes ovladač pro PostgreSQL) se zobrazuje verze CockroachDB a funkce unique_rowid() se bere jako auto increment.
  • V MS SQL je opravena editace řádek s datumem v primárním klíči.
  • MongoDB jsem přesunul do pluginu.

Dále jsem zapnul mnoho dalších kontrol stylu kódu a Adminer pro ně trochu učesal. Odhalilo to i nějaké skutečné chybky. Také jsem našel kontrolor kompatibility s verzemi PHP a zajistil, aby celý Adminer fungoval na všech podporovaných verzích (5.3 - 8.4).

Hodně jsem zapracoval na testech:

  • Adminer už léta kontroluji pomocí end-to-end testů Katalon. Ty běžely zevrubně pro MySQL a zběžně pro SQLite. Nyní jsem udělal jednoduchou věc: Celou sadu testů jsem prostě čtyřikrát zkopíroval, MySQL změnil na MariaDB, PostgreSQL, CockroachDB a MS SQL, mírně upravil pro specifika dané databáze a nyní zevrubné testy běží i pro tyto databázové systémy. Např. u CockroachDB jsem byl překvapen, že prakticky celý Adminer v něm skutečně funguje. Celá sada testů běží skoro 10 minut, takže nedává smysl to spouštět po každém commitu, ale vždycky před vydáním nové verze se to dá vydržet.
  • Přidal jsem testy pro php_shrink, který Adminer rozbil po zavedení viditelnosti proměnných.
  • Znovuzprovoznil jsem pokrytí kódu a byl jsem celkem překvapen, jak moc kódu Admineru je end-to-end testy pokryto.

Dobrovolníci také dokončili překlad webu Admineru včetně popisu všech pluginů do němčiny, polštiny a rumunštiny. Chybí slovenština – pokud by se toho někdo chtěl ujmout, tak prosím napište.

Jakub Vrána, Adminer, 13.3.2025, diskuse: 5 (nové: 5)

Starší články naleznete v archivu.

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.