PHP triky
Weblog o elegantním programování v PHP pro mírně pokročilé
Adminer 5.5.0
Z nástupu AI jsem byl trochu rozčarovaný. Model udělal třeba 80 % požadované kvality práce, ale donutit ho dodělat ten zbytek nebo to dodělávat sám nakonec často zabralo víc času, než když bych si to udělal sám celé od začátku. Často mi to připadalo jako hádat se s blbcem. Když přišel model Fable 5 a David Grudl kvůli němu zrušil návštěvu festivalů, tak jsem se rozhodl tomu dát šanci znovu.
A jsem nadšen! Model kódu rozumí kolikrát líp než já. Zeptal jsem se ho třeba „Jak to, že se v Admineru pro SQLite zobrazuje struktura tabulky sqlite_schema?“ A on mi to správně vysvětlil – sám bych to musel v kódu hledat a vymýšlet to. Spoustu práce mi ušetřil hlavně dotaz „Podívej se na poslední verze MySQL/PostgreSQL/SQLite a navrhni novinky, které by šly do Admineru zahrnout.“ Pak už jen stačilo zaškrtnout, které z těch novinek chci, a bylo hotovo. Tohle procházení novinek se mi nikdy dělat nechce, protože většina z nich se Admineru nijak netýká. Při práci se model jen tak mimochodem zmínil, že v process listu došlo v PostgreSQL 9.2 k přejmenování jednoho sloupce, a opravil to taky.
Nebo jsem chtěl v SQLite podporovat tabulky STRICT bez toho, aniž bych tím zaplevelil uživatelské rozhraní. Navrhl použít mechanizmus engine, který se v MySQL používá pro výběr typu tabulky. Jde skutečně o elegantní řešení, ke kterému je UI už navíc hotové. Časem bych k němu nejspíš dospěl i sám.
Pomohl mi i se zvýrazňovačem syntaxe. Třeba při přidávání podpory pro VECTOR do Admineru nejprve do submodulu jush commitnul zvýraznění syntaxe a pak do Admineru přidal samotnou podporu. Ještě mi připomněl, ať submodul pushnu jako první.
S tímhle už mě baví pracovat, je to rovnocenný parťák. Část novinek v Admineru je tedy ve spolupráci s Fable.
- Zakázal jsem neplatné porty a sockety. (GHSA-58cq-mgw2-38m5)
- Ignoruje se neplatný
X-Forwarded-Prefix. (GHSA-8478-xrj3-h9c2) - Do process listu jsem přidal odkaz pro zkopírování do schránky. Dávno existuje možnost ho i naklonovat, ale pokud je moc dlouhý, tak se nevejde do URL.
- Odkazy pro zkopírování dotazů do schránky nefungují v nebezpečných kotextech (např. na HTTP), takže se nově ani nezobrazují.
- V editaci se zalamují zobrazené hodnoty. To se hodí hlavně u needitovatelných položek na více řádků.
- Zrychlil jsem zvýrazňování syntaxe, hlavně při editaci dlouhých textů a při velkém množství zvýrazňovaných položek (typicky JSON v datech tabulky).
- Dá se připojit k socketu bez serveru pomocí
:/tmp/mysql.sock. (bug #1199, regrese z 5.4.0) - MySQL: V parametrech funkcí se neexportuje příznak
IN. - MySQL: Sloupce typu
vectorse ve výpisu a editaci zobrazují jako text. - MySQL: Odstranil jsem podporu MySQL 5.0 (z roku 2005) a starší.
- MySQL, SQLite: Ve změně tabulky jde sloupce přesouvat pomocí drag and drop.
- MariaDB: Podpora typů
inet4ainet6. - MariaDB: Podpora password validation pluginů při správě uživatele – Adminer volal odděleně
PASSWORD(), aby se nezahešované heslo neukládalo do historie. To se těmto pluginům nelíbilo. (bug SF-814) - MariaDB 11.7+: Podpora typu a indexů
vector. - PostgreSQL 9.2+: Zvýraznění dotazu v process listu.
- SQLite: Podpora typu
ANY. - SQLite: Podpora možností
STRICTaWITHOUT ROWID. - SQLite: Samotné
integer PRIMARY KEYuž se nebere jakoAUTOINCREMENT. - SQLite: Zakázání editace
sqlite_schema. - MS SQL: Oprava dotazů s více výsledky. (bug #1180)
Přidal jsem taky jednoduchý ovladač pro Redis a plugin pro přihlášení za reverzní proxy. Samotný Adminer totiž nevěří hlavičce X-Forwarded-For, takže neplatné pokusy o přihlášení za reverzní proxy znemožní přihlášení z jakékoliv IP adresy.
Zastaralé $http_response_header
PHP 8.5 zastaralo proměnnou $http_response_header, která obsahuje hlavičky posledního HTTP požadavku. Místo ní je k dispozici funkce http_get_last_response_headers(). Co když potřebujeme zpětně kompatibilní kód? Tímhle si nepomůžeme, protože zastaralost $http_response_header se zjišťuje už při kompilaci:
<?php $headers = (function_exists('http_get_last_response_headers') ? http_get_last_response_headers() : $http_response_header); ?>
Tohle varování o zastaralosti vyřeší, ale kód ve starších verzích nefunguje, proměnná se nenaplní:
<?php $var = 'http_response_header'; $headers = (function_exists('http_get_last_response_headers') ? http_get_last_response_headers() : $$var); ?>
Jedině tohle je řešení, které ve starších verzích funguje a v nové nezpůsobí varování:
<?php if (function_exists('http_get_last_response_headers')) { $http_response_header = http_get_last_response_headers(); } ?>
Novinky PHP 8.5
Novinky v PHP 8.5:
- Operátor pro řetězení funkcí
|>(nepříliš praktický). - Anonymní funkce lze použít na místě konstant:
const FN = strlen(...); - Předdefinovaný atribut
#[\NoDiscard]pro vynucení použití návratové hodnoty funkce. S ním související přetypování(void)pro zahození návratové hodnoty. - Globálním konstantám lze přiřadit atribut:
#[Deprecated] const A = 1; - Atribut
#[\DelayedTargetValidation]pro odložení kontroly nepodporovaných atributů. - Atribut
#[\Override]lze použít i u vlastností. - Statické vlastnosti můžu mít také asymetrickou viditelnost.
- Fatální chyby (jako překročení maximálního času) mají backtrace.
- Property promotion lze použít s
final. - V hodnotách konstant lze použít přetypování:
const T1 = (int) 0.3; - Operátor
clonelze používat jako funkci s druhým parametrem, který určí novou hodnotu vlastností:$b = clone($a, ['x' => 2]); - Třídy ve jmenném prostoru Uri pro práci s URL podle RFC 3986 a WHATWG.
- Chybové handlery lze získat funkcemi get_error_handler a get_exception_handler.
- Metodou Closure::getCurrent lze získat vlastní closure.
- Funkcí curl_share_init_persistent lze vytvořit cURL handle, který nezanikne po skončení aktuálního požadavku.
- Funkce array_first a array_last jako obdoba
resetaendnepotřebující referenci. - Zaniklo nastavení
disable_classes. - Zastaralé obraty: operátor
`,__sleep()a__wakeup(),$http_response_header.
Adminer 5.4.3
Vyšel Adminer 5.4.3. Opravuje několik nepříliš vážných bezpečnostních chyb:
- Pro obranu proti CSRF se nově používá hlavička Sec-Fetch-Site. Náhodně generovaný token neměl dostatečnou entropii. (GHSA-33j4-hc95-pggg)
- Ošetřuje se REQUEST_URI, které bylo možné zneužít pro nastavení parametrů cookies. (GHSA-c533-9qwm-8w5h)
- Validuje se název databázového serveru. V Oracle se pomocí toho dal vytvořit soubor. (GHSA-r4x9-5m63-3vxw)
- Validuje se verze databázového serveru. Pokud modifikovaný server vrátil neplatný řetězec s verzí, mohlo dojít k XSS. (GHSA-h6jr-7pr6-grgj)
- Pomocí SQL příkazu s 350 000 komentáři se dala v SQLite obejít obrana proti ATTACH. (GHSA-q4f2-39gr-45jh)
- V SQLite chyběla ochrana proti VACUUM INTO. (GHSA-gmx3-g29w-77wf)
- V SQLite se před smazáním databáze nekontrolovalo, jestli je název povolený. (GHSA-6pg3-chwq-wgqc)
- Místo
unserialize()se používájson_decode(). (bug #1289)
Ostatní změny jsou starší, ale dřív jsem je nevydal.
- Tabulky v přehledu lze třídit. (bug #1231)
- Inline editace pomocí Ctrl+klik je deaktivovaná, pokud uživatel nemá právo UPDATE.
- Ve výpisu tabulky se v bublině názvu sloupce zobrazuje typ, teď jsem přidal i informaci o NULL.
- V exportu se pamatují nevybrané položky. (regrese z 5.0.6)
- U cizího klíče se v případě chyby zobrazí nová hodnota.
- Adminer Editor: Sloupec typu
tinyint(1)se zobrazí jako checkbox. (bug #1246, regrese z 5.4.2) - Přibyl chorvatský překlad.
Další změny se týkají PostgreSQL:
Rozčarování z AI
Budoucnost celkem jasně vidím v tom, že AI řeknu, co má udělat a jak si to má zkontrolovat, a ona se bude tak dlouho snažit, dokud to neudělá a kontrola neprojde. Možná mám moc velké nároky, ale schopnosti modelů zatím nejsou tak daleko, aby mi ušetřily práci, pokud chci dosáhnout požadované kvality. Často mi ji naopak přidělají.
PHP_CodeSniffer
Na PHP_CodeSniffer je vidět, že byl navržen v „minulém století“ a pravidla pro něj se píšou strašně nešikovně. Když jsem potřeboval napsat celkem jednoduché pravidlo (kolem vlastností tříd může být 0-1 prázdný řádek), tak jsem to milerád přenechal modelu Claude Opus. Na specifikaci chování i v okrajových případech jsem si dal záležet, zadání mělo asi půl stránky. Model přesto přišel s řešením, které zadání nesplňovalo. Navíc bylo dost pomalé – zkoumalo všechny proměnné v souboru a ne jen vlastnosti. Hodinu jsem se s modelem hádal, ukazoval mu příklady, kde kód nesplňuje zadání, a vysvětloval mu, že takhle napsaný kód je navíc pomalý. Všelijak to záplatoval, řešení bylo čím dál ošklivější a zanášelo nové chyby. Nakonec jsem to celé zahodil a za hodinu si napsal pravidlo po svém – správné, rychlé a elegantní.
Upgrade verzí
Jeden projekt mi běžel na prehistorické verzi Nette. Požádal jsem Claude o převod na moderní verzi Nette, čehož se zhostil velmi dobře – pár problémů tam bylo, ale ty jsme rychle vyřešili. Tady jsem byl spokojen.
Stejně tak u převodu jiného projektu na moderní verzi PHP – převedl odstraněné funkce, našel zastaralé obraty a převedl i je. Při plánování jsme se dohodli, že nemá dělat shim, ale funkce má nahradit přímo v kódu. Trochu mě ale překvapilo, jak tupě převod dělá – hledá regulární výrazy a nahrazuje je něčím jiným – kódu vůbec nerozumí. Vím, že existuje napojení na MCP třeba v Idea, nebo LSP, ale to je trochu složitější rozchodit. V tomto případě regulární výrazy stačily.
Třetí převod verze dopadl katastrofálně – některé soubory nebyly v UTF-8 a Claude rozbil kódování. Chvíli jsem se ho snažil donutit je opravit, on původní verze lovil z nějaké své historie, ale nakonec jsem to vzdal, obnovil původní verzi a nechal ho udělat všechno znovu. Tentokrát s explicitní instrukcí, že některé soubory nejsou v UTF-8 a ať si na to dá pozor. Co myslíte? Dopadlo to úplně stejně a soubory jsem si nakonec převedl sám.
Jazykový model
Od té doby, co si David Grudl (velký popularizátor AI) nechává pomocí AI psát články, tak se nedají číst. Dřív měl břitký styl a věřím, že napsání dobrého textu trvalo řádově déle, než nadiktování poznámek AI s instrukcí, ať z toho udělá článek. Ale obraty jako „Jedna uživatelka nahrála hlasové poznámky z procházek s kočárkem“ tento styl prostě nemají. Dřív jsem rád poslouchal i jeho podcast Na vlnách EKG. Nový podcast Uměligence předčítaný robotickým hlasem se poslouchat fakt nedá, přestože mi je zaměřením mnohem bližší.
Windows
Část mých problémů je způsobena asi i tím, že Claude Code pouštím na Windows. Tam se dá spustit buď v PowerShellu nebo ve WSL. Půlka věcí nefunguje tam, druhá půlka tam. Propojení služeb (které vnímám jako největší skok dopředu) je kvůli tomu o dost komplikovanější. Pokud mám např. Claude spuštěný ve WSL a vývojový server mi běží ve Windows, tak bych Clauda ve WSL chtěl naučit koukat se na výsledky na vývojovém serveru. Sám to protunelovat neumím a když jsem to zadal Claudu, tak si s tím taky neporadil.
Integrace
Kde mi AI docela pomáhá, jsou jednoduché úlohy – hledám nějaké nastavení v Idea a ChatGPT ho celkem spolehlivě najde. Proč ale nemůžu vlastními slovy říct přímo Idea, co chci nastavit? Místo toho je tam úplně stupidní AI generování commit zpráv, které se nedá vypnout.
Stejně tak v Google Sheets – nemůžu si vzpomenout, jak se jmenuje nějaká funkce, tak Gemini řeknu, co potřebuji udělat (nejlépe s konkrétním vstupem) a funkci dostanu. Opět – proč kvůli tomu musím z Google Sheets odcházet (nebo za to platit, když ve vedlejším okně to mám zdarma)?
Co mě baví
Nedávno jsem objevil Wikidata, ze které se dají data získávat jazykem Sparql. Prvních pár dotazů jsem si nechal napsat pomocí AI a učící křivka tedy byla velmi strmá – rychle jsem dostával výsledky, které jsem potřeboval. Ale jak se mé požadavky komplikovaly, tak se do toho AI začala zamotávat a já se s ní víc času musel hádat o tom, že má něco špatně nebo pomale (nebo oboje). Organizaci dat ve Wikidata a způsob jejich dotazování jsem pochopil a dotazy si už většinou snadněji napíšu sám. Nebo u těch vygenerovaných aspoň dokážu rychle identifikovat, pokud je na nich něco špatně a buď to opravit, nebo AI říct, co má špatně.
U toho jsem si uvědomil, co mě při práci vlastně baví – často to není jen samotný výsledek, ale i způsob, jak se k němu dostanu. Něco pochopit a dokázat to aplikovat na nové problémy – to mi přináší radost. Ne hádání se s tupou AI, co zase udělala špatně, nebo spokojení se s neefektivním kódem. Nebaví mě ani konfigurace – nastavit robota, aby si dokázal spustit webový server a v prohlížeči si ověřoval výsledky. U každého projektu se to dělá trochu jinak a vždycky je potřeba to nějak nastavit.
Budoucnost
Modely se rychle zlepšují a s čím měly včera problémy, už dnes obstojně zvládají. Pořád mi ale přijde užitečné, když problému sám rozumím a poznám, když AI něco dělá špatně. Nudné práce bude ubývat, ale pro mě je větší nuda vysvětlovat AI, co má udělat, než problém pochopit a udělat to sám.
S příchodem modelu Fable se situace dramaticky zlepšila.
Starší články naleznete v archivu.

