<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
	<title>PHP triky</title>
	<subtitle>Weblog o elegantním programování v PHP pro mírně pokročilé</subtitle>
	<link rel="self" href="https://php.vrana.cz/atom.php"/>
	<!-- <link rel="search" type="application/opensearchdescription+xml" href="https://php.vrana.cz/opensearch.php"/> -->
	<link href="https://php.vrana.cz/"/>
	<id>https://php.vrana.cz/</id>
	<updated>2026-09-25T12:07:41+02:00</updated>
	<author><name>Jakub Vrána</name><uri>https://www.vrana.cz/</uri></author>
	<icon>https://php.vrana.cz/favicon.ico</icon>
	<logo>https://php.vrana.cz/logo.gif</logo>
	<rights>(c) 2005 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í.</rights>
	<link rel="license" type="application/rdf+xml" href="https://creativecommons.org/licenses/by/2.5/rdf"/>
	
<entry>
	<title>Adminer 6.1.1 – zvýrazňování výsledků hledání</title>
	<link href='https://php.vrana.cz/adminer-6-1-1.php'/>
	<id>https://php.vrana.cz/:810</id>
	<updated>2026-09-25T12:07:41+02:00</updated>
	<published>2026-09-25T00:00:00+02:00</published>
	<category term='9' label="Adminer"/>
	<content type='xhtml'><div xmlns='http://www.w3.org/1999/xhtml'>

<p><a href="https://www.adminer.org/"><b>Adminer</b></a> 6.1.1 přináší skoro <a href="https://github.com/vrana/adminer/releases/tag/v6.1.1">50 změn</a>, nejvýraznější je zvýrazňování nalezeného textu. Nechtěl jsem, aby při hledání <code>group = 6</code> byla šestka vysvícená ve všech řádcích, proto se zvýrazňuje jen při hledání podřetězce nebo ve všech sloupcích.</p>
<p><img src="data/adminer-highlight.png" width="1283" height="111" alt="screenshot"></p>
<p>Další zajímavé změny:</p>
<ol>
<li>Při editaci <abbr title="JavaScript Object Notation" lang="en">JSON</abbr> se zaokrouhlovala velká čísla. Funkce <code class="language-php">json_decode</code> má sice volbu <a href="https://www.php.net/manual/en/json.constants.php#constant.json-bigint-as-string"><kbd>JSON_BIGINT_AS_STRING</kbd></a>, ta je ale zase převede na řetězce. Při řešení jsem se dozvěděl o <a href="https://www.regular-expressions.info/mark.html"><code>(*SKIP)</code> a <code>(*FAIL)</code></a> v regulárech: <code class="language-php">preg_replace('~"(?:[^"\\\\]|\\\\.)*+"(*SKIP)(*FAIL)|-?\d[-+.\deE]*+~', '"\\\\u0001$0"', $json)</code> při značkování čísel přeskočí řetězce a je to rychlejší než <code class="language-php">preg_replace_callback</code>.</li>
<li>Při scrollování široké tabulky zůstane vlevo viset její editační odkaz. Obdobně název tabulky v přehledu databáze.</li>
<li>Odkazy do dokumentace byly rozeseté na náhodných místech v podobě otazníků, které se ale používají třeba i pro chybějící data. Změnil jsem je na ikonu 📖, kterou jsem přesunul do hlavičky.</li>
<li>V seznamu databází a schémat strašila systémová schémata. Přesunul jsem je do samostatné skupiny.</li>
</ol>
<h3>Ovladače</h3>
<ul>
<li>Triggery v <b>PostgreSQL</b> můžou jen volat funkci. Přidal jsem odkaz pro její editaci.</li>
<li><b>SQLite</b> pomocí <code class="language-sqlite">ATTACH</code> umožňuje vytvářet soubory. Zakázal jsem to metodou <code class="language-php">SQLite3::setAuthorizer</code>.</li>
<li><b>MS SQL</b> jsem rozchodil s extenzí <a href="https://www.php.net/manual/en/class.pdo-dblib.php">Dblib</a> a opravil v ní několik chyb.</li>
<li><b>ClickHouse</b> jsem vyzkoušel na jejich <a href="https://play.clickhouse.com/" target="_blank">pískovišti</a> a opravil několik chyb.</li>
</ul>
<h3>Pluginy</h3>
<ul>
<li><a href="https://www.adminer.org/editor/">Editor</a> odjakživa zobrazuje hodnotu cizího klíče jako první řetězcový sloupec odkazované tabulky. Plugin <b>select-foreign</b> to umožňuje i v Admineru.</li>
<li>Přidal jsem metodu <code>namePattern()</code>, kterou se dá ovlivnit výchozí způsob pojmenování vytvářených objektů. Plugin <b>name-patterns</b> to dovoluje změnit. Výchozí pojmenování <em>triggerů</em> je nově podle všech událostí (např. <code>iu</code> pro <code>INSERT OR UPDATE</code>).</li>
</ul>

</div></content>
</entry>
<entry>
	<title>Adminer 6.1.0 – webová aplikace</title>
	<link href='https://php.vrana.cz/adminer-6-1-0.php'/>
	<id>https://php.vrana.cz/:809</id>
	<updated>2026-09-19T06:21:34+02:00</updated>
	<published>2026-09-14T00:00:00+02:00</published>
	<category term='9' label="Adminer"/>
	<content type='xhtml'><div xmlns='http://www.w3.org/1999/xhtml'>

<p><img src="data/adminer-webapp.png" width="276" height="143" alt="Adminer webapp" class="right" style="border: 1px solid gray;"> <a href="https://www.adminer.org/"><b>Adminer</b></a> 6.1.0 lze nainstalovat jako webovou aplikaci. Název aplikace obsahuje doménu webového serveru, takže jde rozlišit vývoj a produkci.</p>
<h3>Další změny</h3>
<ul>
<li><a href="https://www.adminer.org/cs/tips/"><em lang="en">Power feature</em></a> Admineru je <kbd>Ctrl+click</kbd> pro přímou editaci hodnot v tabulce. Teď jsem to přidal i do SQL příkazu, kde je to mnohem složitější – může být spojeno víc tabulek, tabulky a sloupce můžou mít aliasy, hodnoty se můžou opakovat na více řádcích, … Funguje to jen s extenzí <a href="https://www.php.net/mysqli" target="_blank">MySQLi</a>, která jako jediná dodává všechny potřebné informace.</li>
<li>Při ukládání AJAXem nemusela být vidět hláška o výsledku, ta proto teď zůstane chvíli viset na obrazovce.</li>
<li>Neexistující objekty (např. cizí klíče nebo schémata) teď vrací <code class="language-http">404</code> a chybovou hlášku.</li>
<li>Přepracoval jsem práci s transakcemi, takže ty teď fungují v Oracle, MS SQL i SQL příkazu.</li>
<li>Překladatele jsem požádal o kontrolu AI překladů a udělal jim <a href="https://github.com/vrana/adminer/blob/main/adminer/lang/README.md#leaderboard" target="_blank">žebříček</a>.</li>
<li>Výpis dat dovoluje používat SQL funkce, s čímž pak Adminer dále pracuje. Jenže sloupec pojmenovaný doslova třeba <code>CHAR_LENGTH(name)</code> to rozbil. To sice není úplně reálné, ale stejně mě to štvalo, tak jsem to opravil.</li>
<li>V exportu přibyla možnost exportovat schéma (MS SQL) a tím i vybrat, jestli jím budou objekty plně kvalifikované (PostgreSQL).</li>
<li><b>Pluginy</b> můžou použít metodu <code>explain()</code>, což dovolí integraci nástrojů jako <a href="https://pev2.pages.dev/" target="_blank">PEV2</a>.</li>
<li>Vytvořil jsem plugin <a href="https://gist.github.com/vrana/7ed5b7176bfe6a9212324c70ddb6beef"><b>login-cross-origin</b></a>, který dovolí přihlášení bez kontroly <a href="cross-site-request-forgery.php" class="inner">CSRF</a>, zakázané od <a href="adminer-6-0-1.php" class="inner">6.0.1</a>.</li>
</ul>
<h3>Ovladače</h3>
<p>Ovladač pro <b>Oracle</b> se po 16 letech vývoje (ehm) osvobodil od přívlastku <em>beta</em>. Vyřešil jsem všechny otevřené chyby, vyrobil sadu testů, zrychlil dotazy a pro „databázi“ jsem použil schémata místo dřívějších <em lang="en">tablespaces</em>. Neobešlo se to bez <a href="https://github.com/php/pecl-database-pdo_oci/pull/42" target="_blank">změny</a> samotné PHP extenze, protože její pouhé nahrání mi shazovalo PHP.</p>
<ul>
<li><b>MySQL</b> zobrazuje jako přihlášeného uživatele <code class="language-sql">CURRENT_USER()</code> místo <code class="language-sql">USER()</code>.</li>
<li><b>PostgreSQL</b> dostalo přehled o nainstalovaných <a href="https://www.postgresql.org/docs/current/contrib.html" target="_blank">extenzích</a> a podporuje geometrii PostGIS.</li>
<li><b>MS SQL</b> se dočkalo podpory uložených procedur, <code class="language-mssql">varchar(max)</code> a Unicode ve všech řetězcích.</li>
<li><b>CockroachDB</b> odkazuje do vlastní dokumentace.</li>
</ul>
<p>A ještě nějaké <a href="https://github.com/vrana/adminer/releases/tag/v6.1.0">drobnější změny</a>.</p>


</div></content>
</entry>
<entry>
	<title>Čtyřka – mariáš pro čtyři hráče</title>
	<link href='https://php.vrana.cz/ctyrka-marias-pro-ctyri-hrace.php'/>
	<id>https://php.vrana.cz/:808</id>
	<updated>2026-09-09T21:10:19+02:00</updated>
	<published>2026-09-09T00:00:00+02:00</published>
	<category term='8' label="Osobní"/>
	<content type='xhtml'><div xmlns='http://www.w3.org/1999/xhtml'>

<p>Jako bakalářský projekt jsem v minulém století napsal karetní hru <a href="https://ctyrka.vrana.cz/"><b>Čtyřka</b></a> – mariáš pro čtyři hráče. Ta se pak i celkem úspěšně prodávala. Teď jsem se k tomu vrátil a přepsal jsem ji pro prohlížeč.</p>
<p><img src="https://ctyrka.vrana.cz/ukazka.gif" width="624" height="476" alt="screenshot"></p>
<p>Po letech jsem byl opravdu rád, jak jsem to už tehdy napsal dobře. Zaprvé je herní algoritmus zcela oddělen od zbytku programu (je to samostatné <abbr title="Dynamic-Link Library" lang="en">DLL</abbr>) a dostává jen ty informace, které daný hráč může mít. Nemůže tedy podvádět – koukat se hráči do karet ani spolupracovat s ostatními algoritmy. Zadruhé to není žádné obyčejné procházení stromem, ale hraje to podle asi 80 heuristik, které jsem zadal podle toho, jak jsem tehdy sám hrál. Hraje to tedy spíš jako člověk než jako počítač. Třeba když jsem hrával šachy, tak mě to proti lidem bavilo, ale proti počítači ne. Tady je to do značné míry potlačeno a docela mě překvapilo, jak mě po letech můj starý algoritmus drtí – inu, moc jsem za ta léta netrénoval.</p>
<p>Ve starém programu jde zapnout logování veškerých událostí a pouhým držením Enteru se dá rychle odehrát desítky her. <a href="https://claude.ai/">Claude</a> se z toho úplně tetelil blahem – můj algoritmus označil za korunní klenot programu, nechal mě „odehrát“ 400 her, celkem snadno reimplementoval algoritmus do TypeScriptu a pak si pomocí logu porovnal, jestli tam nejsou nějaké nesrovnalosti. Dokonce dokázal nový algoritmus pustit proti starému DLL a porovnal, jestli náhodou jedna verze nevydělává podezřele hodně peněz. Pak jsme teprve starý algoritmus začali vylepšovat.</p>
<p>Nechal jsem napsat i nový algoritmus pomocí procházení stromem, ale můj starý algoritmus nepřekonal – počítal s tím, že všichni budou hrát dokonale, což jde s neúplnou informací dost těžko.</p>
<h3>Nová hra</h3>
<p><img src="data/ctyrka.webp" width="624" height="400" alt="screenshot"></p>
<p>Uživatelské rozhraní bylo hotové celkem rychle a snadno. Překvapilo mě ale, že samotná hra byla zhruba jen třetina práce.</p>
<p>Další třetinu spolkla monetizace – hra se dá podobně jako původní Čtyřka zdarma vyzkoušet, pak si ji můžete koupit. Takže zřízení účtu u Fio, generování platebního QR kódu, samostatný EUR účet, párování plateb. Hra s platbou nijak zvlášť neotravuje – jen každé rozdání zobrazí platební informace o desetinu sekundy déle. Po sto hrách už čekání většinu hráčů přestane bavit.</p>
<p>A poslední velká část byla kampaň původním zákazníkům – na některé mám e-mail, na jiné telefon a na některé jen poštovní adresu. Takže to obnášelo všechno od zpracování nedoručitelných e-mailů, posílání SMS z Androidu, generování čitelných slevových kódů až po účet na <a href="https://online.postservis.cz/?akc=dopisonline&amp;sek=krok0" target="_blank">Dopis Online</a>.</p>
<p>Celé jsem to <em>navibekódoval</em>, většinu kódu jsem ani nečetl. Ale samozřejmě to nebylo jen „naprogramuj mariáš a obešli původní zákazníky“. Obnášelo to řadu vymýšlení a rozhodnutí, která výslednou podobu zásadně ovlivnila.</p>
<p>Možná hra nebude tak úspěšná jako její původní verze – přece jen už tehdy hráli mariáš z velké části důchodci. Ale i tak jsem rád, že jsem se k tomu vrátil – fakt mě to bavilo a dost jsem se toho naučil.</p>
<p>Zkuste si novou hru <a href="https://ctyrka.com/"><b>Čtyřka – křížový mariáš pro čtyři hráče</b></a> zahrát. Součástí je i tutoriál, který základní pravidla vysvětluje.</p>

</div></content>
</entry>
<entry>
	<title>Adminer 6.0.2 – service worker</title>
	<link href='https://php.vrana.cz/adminer-6-0-2.php'/>
	<id>https://php.vrana.cz/:807</id>
	<updated>2026-09-19T06:21:34+02:00</updated>
	<published>2026-09-07T00:00:00+02:00</published>
	<category term='9' label="Adminer"/>
	<content type='xhtml'><div xmlns='http://www.w3.org/1999/xhtml'>

<p>Zkompilovaný <a href="https://www.adminer.org/"><b>Adminer</b></a> načítá statické soubory (CSS, JS, logo) pomocí <code>?file=default.css</code> s hlavičkami  <code class="language-http">Expires</code> a <code class="language-http">Cache-Control: immutable</code>. Některé hostingy ale všem PHP skriptům dávají své vlastní hlavičky, které kešování naopak natvrdo vypnou. Skript s tím bez změny konfigurace serveru nemůže nic udělat a Adminer je pak pomalý. Uchýlil jsem se proto k tomuto řešení: Adminer si registruje <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API"><em lang="en">service worker</em></a>, který statické soubory nakešuje a při dalším požadavku je servíruje on. Metodou <code>serviceWorker()</code> se to dá vypnout.</p>

<p><a href="https://github.com/vrana/adminer/releases/tag/v6.0.2">Všechny změny</a>, zde jen ty zajímavé:</p>
<ol>
<li>Pro databáze bez podpory <abbr title="Data Definition Language" lang="en">DDL</abbr> transakcí Adminer dělá změny tímto postupem: vytvoř zkušební, smaž zkušební, smaž původní, vytvoř nový. Tím se sníží šance, že by se smazal starý objekt a pak se nepodařilo vytvořit nový. Nová verze to dělá i u kontrol <code class="language-sql">CHECK</code>.</li>
<li>U víceřádkových zkrácených textů je <code>…</code> místo celého posledního řádku.</li>
<li>Čísla se formátují podle jazykových zvyklostí, např. v češtině se jako oddělovač tisíců používá <em lang="en">narrow non-breaking space</em>, v bengálštině má neposlední skupina jen dvě číslice.</li>
<li>Uložené procedury a funkce při změně dovolují editovat všechny volby, např. <code class="language-sql">DETERMINISTIC</code>.</li>
<li>Odkaz <em>Načíst další data</em> zůstane viditelný i po odscrollování doprava.</li>
<li>Zjednodušil jsem RTL rozložení pomocí CSS vlastností <code class="language-css_pro">margin-inline-start</code> a spol.</li>
<li>MySQL: Pokud je s tabulkou nějaký problém (např. chybějící datový soubor), tak to MySQL reportuje v komentáři tabulky. Přepíše tím ale původní komentář. Adminer proto nově získá komentář z jiného zdroje a chybu zobrazí jako chybu.</li>
<li>PostgreSQL: V PDO jsem opravil export <code class="language-pgsql">boolean</code> sloupců.</li>
<li>SQLite: Přidal jsem podporu <a href="https://www.sqlite.org/fts5.html" target="_blank">fulltextového vyhledávání</a> <ins datetime="2026-09-08T21:00:00+02:00">a ostatních <a href="https://www.sqlite.org/vtab.html" target="_blank">virtuálních tabulek</a></ins>.</li>
<li>MS SQL: Podporované typy se nyní načítají přímo z databáze.</li>
<li><a href="https://www.adminer.org/cs/plugins/#driver">Pluginy ovladačů</a> se od <a href="adminer-6-0-1.php" class="inner">předchozí verze</a> kompilují podobně jako Adminer, takže jsem jim mohl přidat typové anotace.</li>
<li><ins datetime="2026-09-09T19:00:00+02:00">Přidal jsem vzhled <a href="https://github.com/vrana/adminer/tree/main/designs/cpanel">cPanel</a>, který se mi líbí i sám o sobě.</ins></li>
</ol>


</div></content>
</entry>

</feed>
