phpFashion

100 minut je méně než 50? Paradoxy PHP při změně času 4.4.2025 11:01

„Kdy se sejdeme?“ – „Zítra ve tři.“ „Kdy je ta schůzka?“ – „Příští měsíc.“ Pro běžný život jsou takové údaje o čase zcela postačující. Jenže zkuste totéž v programování a rychle zjistíte, že jste vstoupili do bludiště plného nástrah a neočekávaných překvapení.

Čas v programování je jako šelma, která vypadá krotce, dokud na ni nešlápnete. A jednou z nejmocnějších lstí této šelmy je letní čas a jeho zákeřné přechody. Systém, který měl údajně ušetřit svíčky, dnes způsobuje programátorům bezesné noci (pravděpodobně kolem 2:30 ráno, kdy najednou zjistí, že jejich servery dělají podivné věci).

Vydejme se na průzkum temných zákoutí přechodů na letní čas a zpět, jak je PHP (ne)zvládá a jak jsem se pokusil napravit toto šílenství v Nette Utils. Připravte se na momenty, kdy 1 + 1 ≠ 2 a kdy přidání delšího času vám paradoxně vrátí dřívější hodinu. Tohle by nevymyslel ani Einstein.

Nejprve si prosvištíme některá slovíčka

Než se ponoříme do problematiky, vysvětleme si několik klíčových pojmů:

Ten okamžik trval celý světelný rok

Pojďme si sekundu po sekundě rozebrat, jak probíhá přechod na letní čas a zpátky. Jako příklad si vezměme nedávnou změnu času v České republice v neděli 30. března 2025:

Celá hodina mezi 02:00:00 a 02:59:59 v tento den lokálně „neexistuje“. Pokud jste měli mít ve 2:30 ráno důležitý telefonát, máte smůlu.

Podobně, při přechodu zpět na standardní čas (někdy označovaný jako „zimní“) na podzim (např. 26. října 2025), nastane opačná situace:

V tomto případě hodina mezi 02:00:00 a 02:59:59 nastane dvakrát. Poprvé v letním čase (CEST) a podruhé ve standardním čase (CET). Jak rozlišíme, kterou 2:30 myslíme? Právě pomocí označení času (CET/CEST), posunu od UTC (+01:00 / +02:00) nebo prostě slovem „letního“ / „zimního“ času.

Časové zóny: Co vlastně označuje Europe/Prague?

Když v PHP (nebo jinde) použijeme identifikátor časové zóny jako Europe/Prague, není to jen informace o aktuálním posunu od UTC. Je to odkaz na záznam v IANA Time Zone Database, která obsahuje komplexní historii a budoucí pravidla pro danou geografickou oblast:

Existují stovky takových zón (America/New_York, Asia/Tokyo, Australia/Sydney). Některé oblasti letní čas vůbec nepoužívají (např. většina Afriky a Asie, nebo oblasti kolem rovníku) a mají po celý rok stejný posun od UTC (např. Etc/UTC nebo Africa/Nairobi).

Absolutní čas: UTC a Timestamp

Abychom se vyhnuli zmatkům s lokálními časy a letním časem, existují absolutní časové reference:

Právě převod mezi absolutním časem (UTC/timestamp) a lokálním časem v konkrétní zóně je místo, kde vstupují do hry pravidla letního času.

PHP DateTime: Když se hodiny přetočí

Když v PHP pracujete s objektem DateTime nebo DateTimeImmutable, vždy má přiřazenou časovou zónu. Pokud ji explicitně neuvedete, použije se výchozí zóna nastavená v PHP (konfigurací nebo pomocí date_default_timezone_set()).

Co se stane, když se pokusíte vytvořit čas, který kvůli letnímu času neexistuje, nebo čas, který existuje dvakrát?

Neexistující čas (jarní skok):

// Pokus vytvořit čas v "díře" 30. března 2025
$dt = new DateTime('2025-03-30 02:30:00', new DateTimeZone('Europe/Prague'));
echo $dt->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-03-30 03:30:00 CEST (+02:00)

PHP typicky „normalizuje“ tento neplatný čas tím, že ho posune vpřed o hodinu na první platný čas po skoku. Takže 02:30 se stane 03:30.

Nejednoznačný čas (podzimní návrat):

// Pokus vytvořit čas v "překryvu" 26. října 2025
$dt = new DateTime('2025-10-26 02:30:00', new DateTimeZone('Europe/Prague'));
echo $dt->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-10-26 02:30:00 CET (+01:00)

PHP zde standardně zvolí druhý výskyt toho času. Proč druhý a ne první? Protože PHP považuje standardní čas (CET) za výchozí, základní stav a letní čas (CEST) pouze za dočasnou úpravu, a proto při nejednoznačnosti dává přednost standardnímu času.

Relativní časové výrazy a jejich záludnosti

Teď se dostáváme k opravdu záludné části. PHP umožňuje pracovat s relativními časovými výrazy – tedy řetězci jako +30 minutes, -1 hour nebo 1 day 2 hours. Tyto výrazy můžeme použít dvěma způsoby:

  1. Přímo v konstruktoru new DateTime('+50 minutes')
  2. V metodě $date->modify('+50 minutes')

Mimochodem, Nette tyto relativní časové výrazy odjakživa „tlačí“, protože jsou srozumitelné a přehledné. Určitě je znáte například z konfigurace „expiration“ u session nebo v dalších částech frameworku.

Intuitivně bychom čekali, že když k času přičteme delší dobu, výsledný čas bude pozdější. S relativními časovými výrazy to ale během jarního přechodu nemusí platit! A tento problém se projevuje jak při použití v konstruktoru DateTime, tak v metodě modify().

Představte si, že je právě půl druhé ráno, těsně před jarním skokem. V tu chvíli se ve vaší aplikací může odehrávat něco velmi bizarního, čeho si většina z nás ovšem nevšimne, protože buď spokojeně chrupeme v posteli, nebo ještě spokojeněji vykládáme moudra v hospodě. Jenže v serverovnách po celém světě kód tiše běží dál…

// pro všechny další příklady nastavíme výchozí časovou zónu
date_default_timezone_set('Europe/Prague');

// Je právě 2025-03-30 01:30:00 a vytvoříme DateTime s relativním časem +50 minut
$dt50 = new DateTime('+50 minutes');
echo $dt50->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-03-30 03:20:00 CEST (+02:00)

„No počkat, 1:30 plus 50 minut je přece 2:20. Proč to ukazuje 3:20?“ Jak už jsme si říkali, hodina mezi 2:00 a 3:00 neexistuje. Takže 2:20 je neplatný čas, který PHP opraví tak, že ho posune o hodinu dál. Tedy na 3:20 v letním čase.

A co když k tomu stejnému výchozímu času přičteme delší interval – řekněme 100 minut?

$dt100 = new DateTime('+100 minutes');
echo $dt100->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-03-30 03:10:00 CEST (+02:00)

Vidíte to! Ano, čtete správně. Po přidání 100 minut jsme dostali čas (03:10), který je dřívější než čas po přidání 50 minut (03:20).

Metoda modify() a konstruktor s relativními řetězci v PHP mají tendenci provádět aritmetiku nejprve na úrovni „hodinkového času“ a až potom řešit neplatné časy vzniklé skokem na letní čas. Výsledkem je naprosto neintuitivní chování, které většina knihoven pro práci s časem v jiných jazycích nedělá. Ty typicky interpretují +X minut jako přidání přesné doby trvání (X * 60 sekund) k absolutnímu časovému okamžiku.

DateInterval: Další vrstva komplikací

Příběh se ještě komplikuje třídou DateInterval. Ta byla vytvořena speciálně pro práci s časovými intervaly a mohla by nabízet řešení našeho problému. Jenže ouha…

K vytvoření instance DateInterval musíte použít formát podle normy ISO 8601. Upřímně, rozuměli byste na první pohled, co znamená PT100M? Ne? Já taky ne. Je to „Period of Time, 100 Minutes“ (doba trvání 100 minut). Standardizované, ale rozhodně ne na první pohled jasné.

Přesto, pokud tento podivný zápis překousneme, funguje najednou všechno správně!

$dt = new DateTime('2025-03-30 01:30:00');
$dt->add(new DateInterval('PT100M')); // 100 Minutes - ten báječný ISO 8601 formát
echo $dt->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-03-30 04:10:00 CEST (+02:00) - hurá, funguje správně!

Skvělé! Tady se to opravdu chová tak, jak bychom čekali – přidá přesně 100 minut k absolutnímu času. To by mohlo být naše řešení… ale co ten podivný formát?

PHP vývojáři si byli vědomi, že PT100M není zrovna uživatelsky přívětivé, a tak přidali metodu DateInterval::createFromDateString(), která rozumí těm příjemným textovým výrazům jako 100 minutes:

$dt = new DateTime('2025-03-30 01:30:00');
$dt->add(DateInterval::createFromDateString('100 minutes'));
echo $dt->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-03-30 03:10:00 CEST (+02:00) - au, zase špatně!

A jsme zase tam, kde jsme byli! Stejný problém jako s modify(). Co se to děje?

Ve skutečnosti máme co do činění s jakousi „dvojí tváří“ třídy DateInterval. Záleží na tom, jakým způsobem ji vytvoříme:

  1. Když použijeme konstruktor s ISO 8601 formátem new DateInterval('PT100M'), vytvoří se skutečná doba trvání, která se přičítá k absolutnímu času.
  2. Když použijeme createFromDateString('100 minutes'), vytvoří se spíše jakýsi kalendářní interval, který se chová podobně jako modify() – nejprve provede „hodinkovou“ aritmetiku a pak až řeší problémy s neplatnými časy.

Takže není DateInterval jako DateInterval. Je to úplně jiná tvář stejně pojmenovaného objektu podle toho, jak ho vytvoříme.

Jedna možnost řešení: Útěk do UTC

Jedním ze způsobů, jak se těmto problémům vyhnout, je provádět veškerou časovou aritmetiku v UTC, kde žádný letní čas neexistuje, a až finální výsledek převést do požadované lokální zóny:

$dt = new DateTime('2025-03-30 01:30:00');
$dt->setTimezone(new DateTimeZone('UTC')); // Převeď do UTC
$dt->modify('+100 minutes');               // Proveď operaci v UTC
$dt->setTimezone(new DateTimeZone('Europe/Prague')); // Převeď zpět
echo $dt->format('Y-m-d H:i:s T (P)');
// Správný výstup: 2025-03-30 04:10:00 CEST (+02:00)

Hurá! Nebo ne? Tento trik může být naopak kontraproduktivní, když přičítáme celé dny nebo jiné kalendářní jednotky. Nejprve si ověříme, že když přičteme 1 den k času před jarním skokem, dostaneme očekávaný výsledek:

$dt = new DateTime('2025-03-30 01:30:00'); // Před skokem (CET +01:00)
$dt->modify('+1 day');
echo $dt->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-03-31 01:30:00 CEST (+02:00)

Vidíme, že při přičtení jednoho dne zůstává stejná „hodinková“ hodnota (01:30), ale mění se časová zóna z CET na CEST.

Ale co se stane, když použijeme náš UTC trik?

$dt = new DateTime('2025-03-30 01:30:00'); // Před skokem (CET +01:00)
$dt->setTimezone(new DateTimeZone('UTC')); // Převede na UTC
$dt->modify('+1 day');
$dt->setTimezone(new DateTimeZone('Europe/Prague')); // Převede zpět do lokální zóny
echo $dt->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-03-31 02:30:00 CEST (+02:00) - o hodinu více!

Ups! Hodina se nám posunula z 1:30 na 2:30. Proč?

  1. Původní čas (01:30 CET) jsme převedli do UTC (00:30 UTC)
  2. Přičetli jsme den v UTC (00:30 UTC následující den)
  3. Ale následující den už platí v Praze letní čas (CEST), který má posun +2 hodiny od UTC
  4. Takže když převedeme zpět 00:30 UTC, dostaneme 02:30 CEST

Tento „útěk do UTC“ tedy může způsobit, že kalendářní operace se nebudou chovat intuitivně z pohledu lokálního času. Co je tedy vlastně správné chování? To záleží na vašich potřebách – někdy chcete zachovat absolutní časový interval (jako 24 hodin), jindy chcete zachovat kalendářní význam (jako „stejný čas následující den“).

Řešení v Nette Utils

Protože práce s časem, časovými zónami a letním časem je notoricky složitá, rozhodl jsem se do Nette Utils přidat opravu problematického chování PHP. Konkrétně do třídy Nette\Utils\DateTime, a to opravu jak konstruktoru, tak metody modify(). Jen váhám, zda nejde o BC break – k tomu se vrátím v závěru článku.

$dt = new Nette\Utils\DateTime('2025-03-30 01:30:00');
$dt->modify('+100 minutes');
echo $dt->format('Y-m-d H:i:s T (P)');
// Výstup: 2025-03-30 04:10:00 CEST (+02:00) - SPRÁVNĚ!

S Nette\Utils\DateTime je výsledek pro +100 minutes vždy pozdější než pro +50 minutes, i když je půl druhé ráno!

Kdy je 1 + 1 ≠ 2? Když pracujeme s časem!

Implementace v Nette Utils řeší i složitější případy, kdy kombinujeme přičítání dnů a hodin. Tady se dostáváme k opravdu zajímavému problému: existují totiž dva možné výklady relativního výrazu jako „+1 day +1 hour“. A tyto dvě interpretace dávají při přechodu na letní čas různé výsledky! Pojďme si to ukázat na příkladu:

První interpretace:

$dt = new \DateTime('2025-03-30 01:30:00'); // CET

$dt1 = clone $dt;
$dt1->modify('+1 day'); // Nejprve přičtu den: 2025-03-31 01:30:00 CEST
$dt1->modify('+1 hour'); // Pak přičtu hodinu: 2025-03-31 02:30:00 CEST

Druhá interpretace:

$dt2 = clone $dt;
$dt2->modify('+1 hour'); // Nejprve hodinu: 2025-03-30 03:30:00 CEST
$dt2->modify('+1 day');         // Pak den: 2025-03-31 03:30:00 CEST

Rozdíl je celá hodina! Jak vidíte, pořadí operací zde hraje zásadní roli.

V Nette\Utils\DateTime jsem zvolil první interpretaci jako výchozí chování, protože je intuitivnější. Chceme-li přičíst „1 den a 1 hodinu“, obvykle tím myslíme „stejný čas následující den plus hodina“. A co je nejlepší? Je jedno, v jakém pořadí jednotky zapíšete. Ať už použijete +1 day +1 hour nebo +1 hour +1 day, výsledek bude vždy stejný.

Tato konzistence dělá práci s časovými výrazy mnohem předvídatelnější a bezpečnější.

Čas je těžký, netrapte se sami

Práce s časem v PHP může být zrádná, zvláště kolem přechodů na letní čas. Relativní časové výrazy a dokonce i některé způsoby použití DateInterval mohou vést k neintuitivním výsledkům.

Pokud potřebujete spolehlivou manipulaci s časem:

  1. Používejte Nette\Utils\DateTime, který opravuje problematické chování.
  2. Nebo provádějte časovou aritmetiku v UTC zóně a až pak převádějte zpět do lokální zóny.
  3. Vždy testujte chování vašeho kódu během přechodů na letní čas.

Teď jen váhám, jestli oprava chování DateTime v Nette Utils nebude BC break. Upřímně si nemyslím, že by kdokoliv vědomě spoléhal na zrádné současné chování při přechodech na letní čas. Tak bych to asi zařadil do Nette Utils 4.1.

Čas je těžké téma ve všech programovacích jazycích, ne jen v PHP. Kam se na to hrabe invalidace keše.

Var, Let, Const: Přestaňte si komplikovat život v JavaScriptu 6.2.2025 23:54

JavaScript nabízí tři způsoby, jak deklarovat proměnné: var, let a const. Pro mnoho programátorů není úplně jasné, kdy kterou z nich použít, většina tutoriálů a linterů vás nutí používat je špatně. Pojďme si ukázat, jak psát čistší a srozumitelnější kód bez zbytečných pravidel, která nám ve skutečnosti nepomáhají.

Začněme tím nejnebezpečnějším

JavaScript má jednu zákeřnou vlastnost: pouhým opomenutím deklarace proměnné můžete nevědomky používat globální proměnnou. Stačí zapomenout na var, let nebo const:

function calculatePrice(amount) {
	price = amount * 100;    // Opomenutí! Chybí 'let'
	return price;            // Používáme globální proměnnou 'price'
}

function processOrder() {
	price = 0;               // Používáme tu samou globální proměnnou!
	// ... nějaký kód volající calculatePrice()
	return price;            // Vracíme úplně jinou hodnotu, než čekáme
}

Tohle je noční můra každého vývojáře – kód funguje zdánlivě správně, dokud nezačne někde jinde v aplikaci něco záhadně selhávat. Debugování takových chyb může zabrat hodiny, protože globální proměnná může být přepsána kdekoliv v aplikaci.

Proto je naprosto zásadní vždy deklarovat proměnné pomocí let nebo const.

Zapomeňte na var

Klíčové slovo var je v JavaScriptu od jeho počátku v roce 1995 a nese s sebou pár problematických vlastností, které byly v době vzniku jazyka považovány za features, ale časem se ukázaly jako zdroj mnoha chyb. Po dvaceti letech vývoje jazyka se autoři JavaScriptu rozhodli tyto problémy řešit – ne opravou var (kvůli zachování zpětné kompatibility), ale představením nového klíčového slova let v ES2015.

Na internetu najdete spoustu článků rozebírajících problémy var do nejmenších detailů. Ale víte co? Není potřeba se v tom babrat. Berme var prostě jako překonaný archaismus a pojďme se soustředit na moderní JavaScript.

Kdy použít let

let je moderní způsob deklarace proměnných v JavaScriptu.

Příjemné je, že proměnná existuje vždy pouze uvnitř bloku kódu (tedy mezi složenými závorkami), kde byla definována. To dělá kód předvídatelnější a bezpečnější.

if (someCondition) {
	let temp = calculateSomething();
	// temp je dostupná jen zde
}
// temp už zde neexistuje

V případě cyklů je deklarace přísně vzato umístěna před složenými závorkami, ale nenechte si tím zmást, proměnná existuje jen v cyklu:

for (let counter = 0; counter < 10; counter++) {
	// Proměnná counter existuje jen v cyklu
}
// counter už zde nejsou dostupné

Kdy použít const

const slouží k deklarování konstant. Typicky jde o důležité hodnoty na úrovni modulu nebo aplikace, které se nikdy nemají měnit:

const PI = 3.14159;
const API_URL = 'https://api.example.com';
const MAX_RETRY_ATTEMPTS = 3;

Je ale důležité pochopit jeden klíčový detail: const pouze zabraňuje přiřazení nové hodnoty do proměnné – neřeší, co se děje s hodnotou samotnou. Tento rozdíl se projevuje zejména u objektů a polí (pole je ostatně také objekt) – const z nich nedělá immutable objekty, tj. nezabraňuje změnám uvnitř objektu:

const CONFIG = {
	url: 'https://api.example.com',
	timeout: 5000
};

CONFIG.url = 'https://api2.example.com';  // Toto funguje!
CONFIG = { url: 'https://api2.example.com' };  // Toto vyhodí TypeError!

Pokud potřebujete skutečně neměnný objekt, musíte jej nejprve zmrazit.

Dilema let vs const

Nyní se dostáváme k zajímavější otázce. Zatímco u var vs let je situace jasná, použití const je předmětem mnoha diskuzí v komunitě. Většina tutoriálů, style-guides a linterů prosazuje pravidlo „používej const všude, kde můžeš“. Takže použití const vídáme zcela běžně v tělech funkcí nebo metod.

Pojďme si vysvětlit, proč je tato populární „best practice“ ve skutečnosti anti-pattern, který dělá kód méně čitelný a zbytečně svazující.

Přístup „pokud se proměnná v kódu nepřepisuje, měla by být deklarována jako const“ se na první pohled jeví logický. Proč by jinak bůh stvořil const? Čím víc „konstant“, tím bezpečnější a předvídatelnější kód, že? A navíc rychlejší, protože ho kompilátor může lépe optimalizovat.

Jenže celý tento přístup je ve skutečnosti nepochopení toho, k čemu konstanty slouží. Jde především o komunikaci záměru – opravdu chceme sdělit ostatním vývojářům, že do této proměnné se už nesmí nic přiřadit, nebo do ní jen náhodou v současné implementaci nic nepřiřazujeme?

// Skutečné konstanty - hodnoty, které jsou konstantní ze své podstaty
const PI = 3.14159;
const DAYS_IN_WEEK = 7;
const API_ENDPOINT = 'https://api.example.com';

// vs.

function processOrder(items) {
	// Toto NEJSOU konstanty, jen náhodou je nepřepisujeme
	const total = items.reduce((sum, item) => sum + item.price, 0);
	const tax = total * 0.21;
	const shipping = calculateShipping(total);
	return { total, tax, shipping };
}

V prvním případě máme hodnoty, které jsou konstantami ze své podstaty – vyjadřují neměnné vlastnosti našeho systému nebo důležitá konfigurační data. Když někde v kódu vidíme PI nebo API_ENDPOINT, okamžitě chápeme, proč jsou tyto hodnoty konstanty.

V druhém případě používáme const jen proto, že zrovna teď náhodou hodnoty nepřepisujeme. Ale není to jejich podstatná vlastnost – jsou to běžné proměnné, které bychom v příští verzi funkce klidně mohli chtít změnit. A když to budeme chtít udělat, const nám v tom bude zbytečně bránit.

V dobách, kdy byl JavaScript jeden velký globální kód, mělo smysl snažit se zabezpečit proměnné proti přepsání. Ale dnes píšeme kód v modulech a třídách. Dnes je běžné a správné, že scope je malá funkce a v jejím rámci vůbec nemá smysl rozdíl mezi let a const řešit.

Protože to vytváří naprosto zbytečnou kognitivní zátěž:

  1. Programátor musí při psaní přemýšlet: „Budu tuhle hodnotu měnit? Ne? Tak musím dát const…“
  2. Čtenáře to ruší! Vidí v kódu const a ptá se: „Proč je tohle konstanta? Je to nějaká důležitá hodnota? Má to nějaký význam?“
  3. Za měsíc potřebujeme hodnotu změnit a musíme řešit: „Můžu změnit const na let? Nespoléhá na to někdo?“

Používejte jednoduše let a tyto otázky nemusíte vůbec neřešit.

Ještě horší je, když toto rozhodnutí dělá automaticky linter. Tedy když linter „opraví“ proměnné na const, protože vidí jen jedno přiřazení. Čtenář kódu pak zbytečně přemýšlí: „Proč tady musí být tyto proměnné konstanty? Je to nějak důležité?“ A přitom to není důležité – je to jen shoda okolností. Nepoužívejte v ESLint pravidlo prefer-const!

Mimochodem, argument o optimalizaci je mýtus. Moderní JavaScript engine (jako V8) dokáže snadno detekovat, zda je proměnná přepisována nebo ne, bez ohledu na to, jestli byla deklarována pomocí let nebo const. Takže používání const nepřináší žádný výkonnostní benefit.

Implicitní konstanty

V JavaScriptu existuje několik konstrukcí, které implicitně vytvářejí konstanty, aniž bychom museli použít klíčové slovo const:

// importované moduly
import { React } from 'react';
React = something; // TypeError: Assignment to constant variable

// funkce
function add(a, b) { return a + b; }
add = something; // TypeError: Assignment to constant variable

// třídy
class User {}
User = something; // TypeError: Assignment to constant variable

Je to logické – tyto konstrukce definují základní stavební bloky našeho kódu a jejich přepsání by mohlo způsobit chaos v aplikaci. Proto je JavaScript automaticky chrání proti přepsání, stejně jako kdyby byly deklarovány pomocí const.

Konstanty ve třídách

Třídy byly do JavaScriptu přidány relativně nedávno (v ES2015) a jejich funkcionalita teprve postupně dospívá. Například privátní členy označené pomocí # přišly až v roce 2022. Na podporu konstant ve třídách JavaScript stále čeká. Prozatím můžete používat static, který ale není zdaleka to samé – označuje hodnotu sdílenou mezi všemi instancemi třídy, nikoliv však neměnnou.

Závěr

  1. var nepoužívejte – je to přežitek
  2. const používejte pro skutečné konstanty na úrovni modulu
  3. Ve funkcích a metodách používejte let – je to čitelnější a jasnější
  4. Nenechte linter automaticky měnit let na const – není to o počtu přiřazení, ale o záměru

Jak vyřešit chaos s prázdnými řetězci a NULL hodnotami v MySQL? 6.2.2025 14:10

Znáte to – vytvoříte dotaz WHERE street = '', ale systém nevrátí všechny záznamy, které byste čekali. Nebo vám nefunguje LEFT JOIN tak, jak má. Důvodem je častý problém v databázích: nekonzistentní používání prázdných řetězců a NULL hodnot. Pojďme si ukázat, jak tento chaos vyřešit jednou provždy.

Kdy použít NULL a kdy prázdný řetězec?

Teoreticky je rozdíl jasný: NULL znamená „hodnota není zadaná“, zatímco prázdný řetězec znamená „hodnota je zadaná a je prázdná“. Podívejme se na reálný příklad z e-shopu, kde máme tabulku objednávek. Každá objednávka má povinnou dodací adresu a volitelnou fakturační adresu pro případ, že zákazník chce fakturovat na jiné místo (typické zatržítko „Fakturovat na jinou adresu“):

CREATE TABLE orders (
    id INT PRIMARY KEY,
    delivery_street VARCHAR(255) NOT NULL,
    delivery_city VARCHAR(255) NOT NULL,
    billing_street VARCHAR(255) NULL,
    billing_city VARCHAR(255) NULL
);

Pole billing_city a billing_street jsou nullable, protože fakturační adresa nemusí být vyplněná. Ale je mezi nimi rozdíl. Zatímco ulice může být legitimně prázdná (obce bez ulic), nebo nezadaná (použije se dodací adresa), město musí být vždy vyplněné, pokud je fakturační adresa použita. Buď tedy billing_city obsahuje název města, nebo je NULL – v tomto případě se použije dodací adresa.

Realita velkých databází

V praxi ale často dochází k tomu, že se v databázi začnou míchat oba přístupy. Příčin může být několik:

Tohle vede k situacím, kdy máme v databázi mix hodnot a musíme psát složité podmínky:

SELECT * FROM tbl
WHERE foo = '' OR foo IS NULL;

Daleko horší je, že NULL se chová neintuitivně při porovnání:

SELECT * FROM tbl WHERE foo = ''; -- nezahrne NULL
SELECT * FROM tbl WHERE foo <> ''; -- taky nezahrne NULL

-- musíme použít
SELECT * FROM tbl WHERE foo IS NULL;
SELECT * FROM tbl WHERE foo <=> NULL;

Tato nekonzistence v chování porovnávacích operátorů je další důvod, proč je výhodnější používat v databázi jen jeden způsob reprezentace prázdné hodnoty.

Proč se vyhnout dvojímu přístupu

Podobná situace jako v MySQL existuje i v JavaScriptu, kde máme null a undefined. Po letech zkušeností mnoho JavaScript vývojářů dospělo k závěru, že rozlišování mezi těmito dvěma stavy přináší víc problémů než užitku a raději se rozhodli používat pouze systémově nativní undefined.

V databázovém světě je situace podobná. Místo toho, abychom stále řešili, jestli něco je prázdný řetězec nebo NULL, je často jednodušší zvolit jeden přístup a toho se držet. Například databáze Oracle prázdné řetězce a NULL hodnoty v podstatě ztotožňuje, čímž tento problém elegantně obchází. Je to jedno z míst, kde se Oracle odchyluje od SQL standardu, ale zároveň tím zjednodušuje práci s prázdnými/NULL hodnotami.

Jak něčeho podobného dosáhnout v MySQL?

Co vlastně chceme vynutit?

  1. U povinných polí (NOT NULL) chceme vynutit, aby vždy obsahovala smysluplnou hodnotu. Tedy zabránit vložení prázdného řetězce (nebo řetězce obsahujícího pouze mezery)
  2. U volitelných polí (NULL) chceme zabránit ukládání prázdných řetězců. Když je pole volitelné, měl by být NULL jedinou reprezentací „nevyplněné hodnoty“. Míchání obou přístupů v jednom sloupci vede k problémům s dotazováním a JOIN operacemi, které jsme si ukázali výše.

Řešení v MySQL

V MySQL dávalo historicky smysl naopak používat výhradně prázdné řetězce ('') místo NULL hodnot. Byl to totiž jediný přístup, který šlo vynutit pomocí NOT NULL constraintu. Pokud jsme chtěli automaticky konzistentní databázi, byla to jediná cesta.

Existuje ale jeden důležitý případ, kdy tento přístup selže – když potřebujeme nad sloupcem unikátní index. MySQL totiž považuje více prázdných řetězců za stejné hodnoty, zatímco více NULL hodnot za různé:

Nicméně od MySQL verze 8.0.16 můžeme použít CHECK constraint a mít tak větší kontrolu nad tím, jaké hodnoty povolíme. Můžeme například vynutit, že sloupec bude buď NULL, nebo bude obsahovat neprázdný řetězec:

CREATE TABLE users (
    id INT PRIMARY KEY,

    -- Povinné pole - musí obsahovat nějaký neprázdný text
    email VARCHAR(255) NOT NULL UNIQUE
        CONSTRAINT email_not_empty      -- název pravidla
        CHECK (email != ''),

    -- Nepovinné pole - buď NULL nebo neprázdný text
    nickname VARCHAR(255)
        CONSTRAINT nickname_not_empty
        CHECK (nickname IS NULL OR nickname != '')
);

Při vytváření CHECK constraintu je důležité dát mu smysluplný název pomocí klíčového slova CONSTRAINT. Díky tomu dostaneme v případě porušení pravidla srozumitelnou chybovou hlášku Check constraint ‚nickname_not_empty‘ is violated místo obecného oznámení o porušení constraintu. To výrazně usnadňuje debugging a údržbu aplikace.

Problém jsou nejen prázdné řetězce, ale i řetězce obsahující pouze mezery. Řešení pomocí CHECK constraintu můžeme vylepšit použitím funkce TRIM:

CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE
        CONSTRAINT email_not_empty
        CHECK (TRIM(email) != ''),
   ...
);

Nyní neprojdou ani tyto pokusy o obejití validace:

INSERT INTO users (email) VALUES ('   ');  -- samé mezery

Praktické řešení v Nette Framework

Konzistentní přístup k prázdným hodnotám je potřeba řešit i na úrovni aplikace. Pokud používáte Nette Framework, můžete využít elegantní řešení pomocí metody setNullable():

$form = new Form;
$form->addText('billing_street')
    ->setNullable(); // prázdný input se transformuje na NULL

Doporučení pro praxi

  1. Na začátku projektu se rozhodněte pro jeden přístup:
    • Buď používejte pouze NULL pro chybějící hodnoty
    • Nebo pouze prázdné řetězce pro prázdné/chybějící hodnoty
  2. Toto rozhodnutí zdokumentujte v dokumentaci projektu
  3. Používejte CHECK constrainty pro vynucení konzistence
  4. U existujících projektů:
    • Proveďte audit současného stavu
    • Připravte migrační skript pro sjednocení přístupu
    • Nezapomeňte upravit aplikační logiku

Tímto přístupem se vyhnete mnoha problémům s porovnáváním, indexováním a JOIN operacemi, které vznikají při míchání NULL a prázdných řetězců. Vaše databáze bude konzistentnější a dotazy jednodušší.

Přejmenování hodnot v ENUM bez ztráty dat: bezpečný návod 27.1.2025 02:16

Přejmenování hodnot v MySQL ENUMu je operace, která může být zrádná. Mnoho vývojářů se pokouší o přímou změnu, což často vede ke ztrátě dat nebo chybám. Ukážeme si, jak na to správně a bezpečně.

Představme si typický scénář: Máte v databázi tabulku objednávek (orders) se sloupcem status, který je typu ENUM. Obsahuje hodnoty waiting_payment, processing, shipped a cancelled. Požadavek je přejmenovat waiting_payment na unpaid a shipped na completed. Jak to udělat bez rizika?

Co nefunguje

Nejprve se podívejme na to, co nefunguje. Mnoho vývojářů zkusí tento přímočarý přístup:

-- TOHLE NEFUNGUJE!
ALTER TABLE orders
MODIFY COLUMN status ENUM(
    'unpaid',      -- původně 'waiting_payment'
    'processing',  -- beze změny
    'completed',   -- původně 'shipped'
    'cancelled'    -- beze změny
);

Takový přístup je receptem na katastrofu. MySQL se v takovém případě pokusí mapovat existující hodnoty na nový ENUM, a protože původní hodnoty už v definici nejsou, nahradí je prázdným řetězcem nebo vrátí chybu Data truncated for column 'status' at row X. V produkční databázi by to znamenalo ztrátu důležitých dat.

Nejprve zálohujte!

Před jakoukoli změnou struktury databáze je naprosto klíčové vytvořit zálohu dat. Použijte MySQL-dump nebo jiný nástroj, kterému důvěřujete.

Správný postup

Správný postup se skládá ze tří kroků:

  1. Nejprve rozšíříme ENUM o nové hodnoty
  2. aktualizujeme data
  3. nakonec odstraníme staré hodnoty.

Pojďme si to ukázat:

1. Prvním krokem je přidání nových hodnot do ENUMu, zatímco ponecháme ty původní:

ALTER TABLE orders
MODIFY COLUMN status ENUM(
    'waiting_payment',  -- původní hodnota
    'processing',       -- zůstává stejná
    'shipped',         -- původní hodnota
    'cancelled',       -- zůstává stejná
    'unpaid',          -- nová hodnota (nahradí waiting_payment)
    'completed'        -- nová hodnota (nahradí shipped)
);

2. Nyní můžeme bezpečně aktualizovat existující data:

UPDATE orders SET status = 'unpaid' WHERE status = 'waiting_payment';
UPDATE orders SET status = 'completed' WHERE status = 'shipped';

3. A konečně, když jsou všechna data převedena na nové hodnoty, můžeme odstranit ty staré:

ALTER TABLE orders
MODIFY COLUMN status ENUM(
    'unpaid',
    'processing',
    'completed',
    'cancelled'
);

Proč tento postup funguje?

Je to díky tomu, jak MySQL pracuje s ENUM hodnotami. Když provádíme ALTER TABLE s modifikací ENUMu, MySQL se snaží mapovat existující hodnoty podle jejich textové podoby. Pokud původní hodnota v novém ENUMu neexistuje, dojde v závislosti na nastavení sql_mode buď k chybě (při zapnutém STRICT_ALL_TABLES) nebo k náhradě prázdným řetězcem. Proto je klíčové mít v ENUMu vždy současně jak staré, tak nové hodnoty.

V našem případě to znamená, že během přechodné fáze, kdy máme v ENUMu hodnoty jako 'waiting_payment' i 'unpaid', každý záznam v databázi najde svůj přesný textový protějšek. Teprve po UPDATE dotazech, kdy už víme, že všechna data používají nové hodnoty, můžeme bezpečně odstranit ty staré.

Property Hooks v PHP 8.4: Revoluce nebo Past? 25.11.2024 03:23

Představte si, že by vaše PHP objekty mohly být čistší, přehlednější a lépe použitelné. Dobrá zpráva – už nemusíte snít! PHP 8.4 přichází s revoluční novinkou v podobě property hooks a asymetrické viditelnosti, které kompletně mění pravidla hry v objektově orientovaném programování. Zapomeňte na neohrabané gettery a settery – konečně máme k dispozici moderní a intuitivní způsob, jak kontrolovat přístup k datům objektů. Pojďme se podívat na to, jak tyto novinky mohou změnit váš kód k nepoznání.

Property hooks představují promyšlený způsob, jak definovat chování při čtení a zápisu vlastností objektu – a to mnohem čistěji a výkonněji než dosavadní magické metody __get/__set. Je to jako byste dostali k dispozici sílu magických metod, ale bez jejich typických nevýhod.

Podívejme se na jednoduchý příklad z praxe, který vám ukáže, proč jsou property hooks tak užitečné. Představme si běžnou třídu Person s veřejnou property age:

class Person
{
	public int $age = 0;
}

$person = new Person;
$person->age = 25;  // OK
$person->age = -5;  // OK, ale to je přece nesmysl!

PHP sice díky typu int zajistí, že věk bude celé číslo (to lze od PHP 7.4), ale co s tím záporným věkem? Dříve bychom museli sáhnout po getterech a setterech, property by musela být private, museli bychom doplnit spoustu kódu… S hooks to vyřešíme elegantně:

class Person
{
	public int $age = 0 {
		set => $value >= 0 ? $value : throw new InvalidArgumentException;
	}
}

$person->age = -5;  // Ups! InvalidArgumentException nás upozorní na nesmysl

Krása tohoto řešení spočívá v jeho jednoduchosti – navenek se property chová úplně stejně jako dřív, můžeme číst i zapisovat přímo přes $person->age. Ale máme plnou kontrolu nad tím, co se při zápisu děje. A to je teprve začátek!

Můžeme jít ještě dál a vytvořit třeba hook pro čtení. Hookům lze přidat atributy. A samozřejmě mohou obsahovat složitější logiku než jednoduchý výraz. Podívejte se na tento příklad práce se jménem:

class Person
{
	public string $first;
	public string $last;
	public string $fullName {
		get {
			return "$this->first $this->last";
		}
		set(string $value) {
			[$this->first, $this->last] = explode(' ', $value, 2);
		}
	}
}

$person = new Person;
$person->fullName = 'James Bond';
echo $person->first;  // vypíše 'James'
echo $person->last;   // vypíše 'Bond'

A něco důležitého: kdykoliv se přistupuje k proměnné (i uvnitř samotné třídy Person), vždy se využijí hooks. Jediná výjimka je přímý přístup k reálné proměnné uvnitř kódu samotného hooku.

Ohlédnutí do minulosti: Co nás naučil SmartObject?

Pro uživatele Nette může být zajímavé ohlédnout se do minulosti. Framework totiž podobnou funkcionalitu nabízel už před 17 lety ve formě SmartObject, který výrazně vylepšoval práci s objekty v době, kdy PHP v této oblasti značně zaostávalo.

Pamatuju si, že tehdy přišla vlna bezbřehého nadšení, kdy se properties používaly prakticky všude. Tu pak vystřídala vlna opačná – nepoužívat je nikde. Důvod? Chybělo jasné vodítko, kdy je lepší použít metody a kdy property. Ale dnešní nativní řešení je kvalitativně úplně jinde.Property hooks a asymetrická viditelnost jsou plnohodnotné nástroje, které nám dávají stejnou úroveň kontroly jako máme u metod. Proto dnes můžeme mnohem lépe rozlišit, kdy je property skutečně tím správným řešením.

Backed nebo Virtual? Dobrá otázka!

Podívejte se na tento kód a zkuste si rychle odpovědět – je to vlastně jednoduchý kvíz:

class Person
{
	public int $age = 0 {
		set => $value >= 0 ? $value : throw new InvalidArgumentException;
	}

	public bool $adult {
		get => $this->age >= 18;
	}
}

Samozřejmě $age je, jak jsme si řekli už dříve, property pro čtení i pro zápis. Ale $adult je jen pro čtení!

A tady narážíme na první zapeklitost v designu property hooks. Ze signatury property vůbec nepoznáme, jestli do ní můžeme zapisovat nebo ji číst!

Odpověď se totiž skrývá v kódu, v implementaci hooků. Property totiž mohou být dvojího druhu: backed (se skutečným úložištěm v paměti) a virtuální (které pouze simulují existenci property). To, zda je property backed nebo virtuální, rozhoduje, zda se v kódu hooku na ni odkazujeme.

Property je backed (má vlastní úložiště), když:

V našem příkladu tedy:

Je to sice mazané řešení, ale ne zrovna šťastné. Tak zásadní informaci, jako zda lze property číst nebo do ní zapisovat, má prozradit API a signatura na první pohled, ne až studium implementace.

Když reference, tak bezpečně!

Reference existují v PHP od jeho počátků. Pomocí znaku & můžete propojit dvě proměnné tak, aby ukazovaly na stejné místo v paměti. Je to jako mít dva dálkové ovladače k jedné televizi – ať zmáčknete kterýkoliv, ovládáte tu samou obrazovku.

Ale co kdyby někdo mohl získat referenci na property s set hookem? Mohl by její hodnotu měnit přímo a kompletně tak obejít veškerou validaci. Podívejte se na tento příklad:

class Person
{
	public int $age = 0 {
		set => $value >= 0 ? $value : throw new InvalidArgumentException;
	}
}

$person = new Person;
$ref = &$person->age;    // Fatal error: Tohle neprojde!
$ref = -5;               // Kdyby to prošlo, validace by byla k ničemu

PHP na to myslelo a elegantně to vyřešilo (tedy pardon, mysleli na to Ilija Tovilo a Larry Garfield, autoři hooků). Získat referenci na takovou property prostě není možné (myšleno na backed proměnnou se set hookem). Je to správné řešení – property hook má zajistit, že se do property dostane jen platná hodnota, a reference by tuto kontrolu obcházely.

Když pole potká property hooks – zajímavá výzva!

Práce s poli v PHP je obvykle příjemně přímočará. Do pole v property můžeme přidávat prvky různými způsoby:

class Person
{
	public array $phones = [];
}

$person = new Person;
$person->phones[] = '777 123 456';          // přidá číslo na konec pole
$person->phones['bob'] = '777 123 456';     // přidá číslo s konkrétním klíčem

A právě tady narážíme na zajímavý problém s property hooks. Představme si, že chceme vytvořit třídu Person, která bude obsahovat seznam telefonních čísel, a chceme, aby se u nich automaticky ořezávaly mezery na začátku a konci:

class Person
{
	public array $phones = [] {
		set => array_map('trim', $value);
	}
}

$person = new Person;
$person->phones[] = '777 123 456';  // Překvapení! Error: Indirect modification of Person::$phones is not allowed

Proč to nefunguje? Operace $person->phones[] totiž v PHP funguje ve dvou krocích:

  1. Nejdřív získá referenci na pole pomocí get
  2. Pak do získaného pole přidá novou hodnotu

Tedy vůbec se nevolá set hook. Ba co víc, jak už víme z předchozí kapitoly, nelze získat referenci na backed proměnnou se set hookem (tedy udělat první krok). Proto ta chybová hláška.

Ani metoda addPhone(), která by volala $this->phones[] = $phone, nám nepomůže – všechny přístupy k property (i uvnitř třídy) totiž procházejí přes hooky.

Tak jak z toho ven? Pojďme si projít možná řešení. První, které vás možná napadne:

$phones = $person->phones;    // načteme pole
$phones[] = ' 777 123 456 ';  // přidáme číslo
$person->phones = $phones;    // uložíme zpět

Funguje to, ale… představte si pole s tisíci čísly. Náš set hook by musel provést trim() na všech číslech znovu, i když se přidalo jediné. To není zrovna ukázka efektivity.

Existuje lepší cesta – uvědomit si, že pokud má pole nějak specificky pracovat se svými prvky (třeba ořezávat mezery), mělo by to být jeho zodpovědností, ne úkolem třídy, která ho jen drží. Jasně, pole samo o sobě nenaučíme novým trikům, ale můžeme ho „zabalit“ do objektu s rozhraním ArrayAccess:

class Phones implements ArrayAccess
{
	private array $data = [];

	public function __construct(array $data = [])
	{
		$this->data = array_map('trim', $data);
	}

	public function offsetSet(mixed $offset, mixed $value): void
	{
		$value = trim($value);
		if ($offset === null) {
			$this->data[] = $value;
		} else {
			$this->data[$offset] = $value;
		}
	}

	// implementace dalších metod pro ArrayAccess...
}

class Person
{
	function __construct(
		public Phones $phones = new Phones,
	) {}
}

$person = new Person;
$person->phones[] = ' 777 123 456 ';  // Hurá! Číslo se uloží pěkně ořezané

A teď třešnička na dortu – můžeme využít hook k tomu, aby do $person->phones šlo zapsat i obyčejné pole:

class Person
{
	function __construct(
		public Phones $phones = new Phones {
			set(array|Phones $value) => is_array($value) ? new Phones($value) : $value;
		},
	) {}
}

$person = new Person;
$person->phones = ['  888 999 000  ', '777 888 999'];  // Magicky se převede na Phones a ořeže řetězce

Jak vidíte, hooks mohou obsahovat i promoted properties.

Ještě se podívejme na alternativní řešení. Vzpomeňte si, že kromě backed property máme ještě virtuální property – ty, které nepoužívají $this->propertyName v těle hooku. A tady se skýtá druhé řešení:

class Person
{
	private array $_phones = []; // skutečné úložiště čísel

	public array $phones {  // virtuální property pro veřejný přístup
		get => $this->_phones;
		set {
			$this->_phones = array_map('trim', $value);
		}
	}

	public function addPhone(string $phone): void
	{
		$this->_phones[] = trim($phone);
	}
}

$person = new Person;
$person->addPhone(' 777 123 456 ');  // Přidá ořezané číslo
echo $person->phones[0];             // Vypíše "777 123 456"
$person->phones = ['  888 999 000  ']; // Nastaví nové pole s ořezanými čísly

Tady jsme zůstali u klasického pole, ale schovali jsme ho za privátní proměnnou. Na venek nabízíme virtuální property pro čtení celého pole a jeho kompletní přepsání, plus specializovanou metodu pro přidávání jednotlivých čísel.

Hooks a dědičnost: Když potomci přebírají žezlo

Potomci mohou nejen přidávat hooks k vlastnostem, které je dosud neměly, ale také předefinovat ty existující. Podívejme se na příklad:

class Person
{
	public string $email;

	public int $age {
		set => $value >= 0
			? $value
			: throw new InvalidArgumentException('Věk nemůže být záporný');
	}
}

class Employee extends Person
{
	// Přidá hook k vlastnosti, která žádný neměla
	public string $email {
		set => strtolower($value);  // Emaily vždy převedeme na malá písmena
	}

	// Rozšíří existující validaci věku
	public int $age {
		set {
			if ($value <= 130) {  // First check the original condition
				throw new InvalidArgumentException('130 years? Not buying it!');
			}
			parent::$age::set($value);
		}
	}
}

Všimněte si té zajímavé syntaxe parent::$age::set($value). Na první pohled možná vypadá zvláštně, ale dává perfektní smysl – nejdřív se odkážeme na vlastnost v rodiči a pak na její hook. Je to jako bychom řekli „hej, zavolej set hook na age vlastnosti mého rodiče“.

A co víc – můžeme hooks označit jako final, pokud chceme zabránit jejich přepsání v potomcích. Dokonce můžeme jako final označit celou property – pak ji potomci nemohou změnit žádným způsobem (ani přidat hooks, ani rozšířit její viditelnost).

class Person
{
	// Tenhle hook už nikdo nepřepíše
	public int $age {
		final set => $value >= 0 ? $value : throw new InvalidArgumentException;
	}

	// A tuhle property už vůbec nikdo nezmění
	final public string $id;
}

Property v rozhraních

Překvapivou novinkou je podpora property v rozhraních a abstraktních třídách. Představte si, že vytváříte rozhraní pro entity, které obsahují řetězec se jménem. Doteď jsme museli psát něco takového:

interface Named
{
	public function getName(): string;
	public function setName(string $name): void;
}

Nuda, že? S property hooks můžeme být mnohem elegantnější! V rozhraní teď můžeme deklarovat přímo property, a to dokonce asymetricky – můžeme říct zvlášť, co má být čitelné a co zapisovatelné:

interface Named
{
	// Říkáme: "Implementující třída musí mít veřejně čitelnou property name"
	public string $name { get; }
}

A teď to zajímavé – jak můžeme takové rozhraní implementovat? Máme hned několik možností:

class Person implements Named
{
	public string $name;     // Nejjednodušší řešení - obyčejná property
}

class Employee implements Named
{
	public string $name {    // Pokročilejší - složené jméno
		get => $this->firstName . ' ' . $this->lastName;
	}
	private string $firstName;
	private string $lastName;
}

Všimněte si zajímavého detailu – rozhraní Named požaduje property pouze pro čtení, ale třída Person nabízí property čitelnou i zapisovatelnou. A to je naprosto v pořádku – rozhraní totiž definuje jen minimální požadavky. Je to jako když řeknete „potřebuju auto, co jede dopředu“ a dostanete auto, co umí i couvat – splňuje to vaše minimální požadavky a přidává něco navíc.

Pro puntičkáře: V rozhraní musíme u property použít klíčové slovo public, i když je to vlastně nadbytečné – vše v rozhraní je ze své podstaty veřejné. U metod uvádět public je hloupost, ale u property je to vyžadováno kvůli konzistenci syntaxe.

A ještě jedna věc stojí za zmínku – všimli jste si té zvláštní syntaxe { get; set; }? Zatímco ve třídě můžeme napsat jednoduše public string $name, v rozhraní musíme explicitně říct, jaké operace property podporuje. Je to sice trochu pracnější, ale dává to smysl – u rozhraní chceme být maximálně explicitní v tom, co požadujeme.

Property v abstraktních třídách: To nejlepší z obou světů

Abstraktní třídy si vezmou to nejlepší z rozhraní a přidají vlastní šťávu. Mohou nejen deklarovat property, ale také nabídnout výchozí implementaci některých hooků:

abstract class Person
{
	// Čistě abstraktní property - implementaci dodá potomek
	abstract public string $name { get; }

	// Protected property s oběma operacemi
	abstract protected int $age { get; set; }

	// Tady už nabízíme hotovou validaci emailu
	abstract public string $email {
		get; // tento hook je abstraktní a potomek ho musí implementovat
		set => Nette\Utils\Validators::isEmail($value)
			? $value
			: throw new InvalidArgumentException('Tohle nevypadá jako email...');
	}
}

A teď něco opravdu zajímavého – kovarianci a kontravarianci!

Zní to jako zaklínadlo, ale je to vlastně jednoduchá věc. Podívejte se:

class Animal {}
class Dog extends Animal {}

interface PetShop
{
	// Property jen pro čtení může vracet specifičtější typ
	public Animal $pet { get; }
}

class DogShop implements PetShop
{
	// Vrací psa místo zvířete - to je v pohodě!
	public Dog $pet { get; }
}

Když má property pouze hook get, může v potomkovi vracet specifičtější typ (tomu se říká kovariance). Představte si to jako: „Slíbil jsem ti zvíře, a pes je přece taky zvíře, ne?“

Naopak property pouze s hookem set může v potomkovi přijímat obecnější typ (kontravariance). Je to logické – když umím pracovat s konkrétním typem, zvládnu i jeho předka.

Jakmile má property oba hooky get i set, musí typ zůstat stejný. Proč? Protože by to mohlo vést k nekonzistencím – nemůžeme slíbit, že vrátíme psa, když nám někdo může přes setter podstrčit kočku!

Asymetrická viditelnost: Každému, co jeho jest

Představte si, že vytváříte třídu Person a chcete, aby datum narození mohl číst kdokoliv, ale měnit ho mohla jen samotná třída. Dřív byste museli sáhnout po getterech a setterech, ale teď? Teď máme elegantní řešení:

class Person
{
	public private(set) DateTimeImmutable $dateOfBirth;
}

Tenhle zápis říká: „Číst může každý, zapisovat jen třída sama.“ První modifikátor public určuje viditelnost pro čtení, druhý private(set) pro zápis. A protože veřejné čtení je default, můžeme ho vynechat a psát prostě:

class Person
{
	private(set) DateTimeImmutable $dateOfBirth;
}

Samozřejmě platí logické pravidlo – viditelnost pro zápis nemůže být širší než pro čtení. Nemůžeme použít třeba protected public(set) – to by bylo jako říct „číst můžou jen potomci, ale zapisovat může každý“. Trochu podivné, ne?

A co dědičnost? V PHP platí, že potomek může viditelnost buď zachovat, nebo rozšířit z protected na public. To samé platí i pro asymetrickou viditelnost:

class Person
{
	public protected(set) string $name;  // Číst může každý, zapisovat jen potomci
}

class Employee extends Person
{
	public public(set) string $name;     // Potomek může rozšířit práva zápisu
}

Zajímavý je případ private(set). Taková property je automaticky final – když řekneme, že zapisovat může jen třída sama, logicky to znamená, že ani potomci nemají právo to měnit.

A nejlepší na tom je, že asymetrickou viditelnost můžeme kombinovat s hooks:

class Person
{
	private(set) DateTimeImmutable $birthDate {
		set => $value > new DateTimeImmutable
			? throw new InvalidArgumentException('Narození v budoucnosti? Sci-fi!')
			: $value;
	}
}

Tahle property má všechno: je veřejně čitelná, zapisovat do ní může jen třída sama, a ještě kontroluje, jestli datum není v budoucnosti. Hooks řeší „co se má stát“, asymetrická viditelnost „kdo to může udělat“. Perfektní tým!

Asymetrická viditelnost a pole: Elegantní řešení starého problému

Pamatujete na naše trápení s telefonními čísly? Asymetrická viditelnost nám nabízí ještě jedno řešení:

class Person
{
	private(set) array $phones = [];

	public function addPhone(string $phone): void
	{
		$this->phones[] = trim($phone);
	}
}

$person = new Person;
var_dump($person->phones);     // OK: můžeme číst
$person->addPhone('...');      // OK: můžeme přidat číslo
$person->phones = [];          // CHYBA: nemůžeme přepsat celé pole

Pole je veřejně čitelné, ale nikdo zvenčí ho nemůže přepsat. Pro přidání nového čísla máme specializovanou metodu. Žádné složité objekty simulující pole, žádné virtual properties – jen čistá, jasná kontrola přístupu.

Pro úplnost dodejme, že u vlastnosti s omezeným zápisem nemůžete získat referenci zvenku:

$ref = &$person->phones;    // Fatal error: Takhle ne!

Reference jsou povolené jen ze scope, ze kterého je property zapisovatelná. Je to logické – reference by mohla obejít naše omezení pro zápis.

Když to shrneme, pro práci s polem v property máme teď několik možností:

  1. Chytrý objekt simulující pole (přináší víc možností, ale taky víc kódu)
  2. Backed property s hookem (znemožňuje přímou modifikaci pole)
  3. Virtual property s privátním úložištěm (vyžaduje metody pro úpravy)
  4. Asymetrická viditelnost (přesouvá logiku do metod)

Který přístup vybrat? Jak už to bývá – záleží na konkrétním případu. Prostě si zkusit, které API nejlépe sedne do ruky.

Readonly a asymetrická viditelnost: Konečně svoboda volby!

Modifikátor readonly jsou ve skutečnosti dva modifikátory v jednom: zakazuje vícenásobný zápis a zároveň omezuje zápis na private. Vlastně to není žádný readonly, je to spíš writeonce zkřížený s private(set).

To druhé mi přišlo vždy zbytečně přísné a nepraktické. Proč by readonly vlastnost nemohla být zapisovatelná třeba v potomcích?

PHP 8.4 to konečně změnilo. Teď readonly dělá property defaultně protected(set), tedy zapisovatelnou i v potomcích. A když potřebujeme jinou viditelnost? Jednoduše si ji nastavíme:

class Person
{
	// Readonly přístupná jen uvnitř třídy (staré chování)
	public private(set) readonly string $name;

	// Readonly přístupná i v potomcích (nové výchozí chování)
	public readonly string $dateOfBirth;

	// Readonly zapisovatelná kdekoliv (ale jen jednou!)
	public public(set) readonly string $id;

	public function rename(string $newName): void
	{
		$this->name = $newName;    // Uvnitř třídy můžeme měnit
	}
}

class Employee extends Person
{
	public function setBirthDate(DateTimeImmutable $date): void
	{
		$this->dateOfBirth = $date;  // V potomkovi můžeme měnit
	}
}

$person = new Person;
$person->id = 'abc123';     // Tohle projde
$person->id = 'xyz789';     // Ale tohle už ne - readonly!

Což nám dává přesně tu flexibilitu, kterou potřebujeme.

Když si terminologie protiřečí…

Pojďme se podívat na malý terminologický zmatek v PHP:

Bylo by logičtější používat termíny read a write, ne?

U hooks bych ještě pochopil použití get/set – jde o akce a navíc to navazuje na magické metody __get/__set. Ale u asymetrické viditelnosti? To je přece koncepčně odlišná věc – neřeší „co se má stát“ jako hooks, ale „kdo to může udělat“. Proto by dávalo mnohem větší smysl použít termín write, tedy například private(write):

class Person
{
	private(set) string $name;     // takhle to je
	private(write) string $name;   // takhle by to dávalo větší smysl
}

Druhá varianta by byla mnohem intuitivnější. Navíc by lépe ladila s existujícím modifikátorem readonly.

Vypadá to, že PHP ve snaze o syntaktickou konzistenci mezi hooks a asymetrickou viditelností obětovalo sémantickou konzistenci s již existujícími koncepty v jazyce.

Nová éra v PHP: Revoluce v objektovém návrhu

V PHP světě jsme byli dlouho odkázáni na jediný správný způsob objektově orientovaného návrhu: všechny vlastnosti private a přístup k nim výhradně přes gettery a settery. Nebyla to rozmazlenost vývojářů – public property prostě byly problematické:

Kdo chtěl programovat správně, používat rozhraní a dependency injection, musel sáhnout po getterech a setterech. Byla to jediná cesta, jak mít plnou kontrolu nad tím, co se v objektech děje.

Ale s PHP 8.4 přichází nová doba! Property hooks a asymetrická viditelnost nám konečně dávají nad vlastnostmi stejnou kontrolu jako nad metodami. Property se stávají plnohodnotnou součástí veřejného API, protože:

V podstatě můžete property hooks brát jako elegantní náhradu getterů a setterů bez zbytečného boilerplate kódu. Nebo naopak – gettery a settery byly jen takové provizorní řešení, než PHP dospělo k něčemu lepšímu.

Ze své vlastní zkušenosti s Nette můžu mluvit velmi konkrétně – jak jsem říkal, framework podobnou funkcionalitu nabízel už před 17 lety. To znamená, že jsem měl možnost s property přístupem pracovat dlouho. A musím říct, že to bylo nesmírně návykové. Porovnejte:

// Starý svět
$this->getUser()->getIdentity()->getName()

// Nový svět
$this->user->identity->name

Druhý zápis není jen kratší a čitelnější – je taky přirozenější. Je to jako rozdíl mezi „Prosím, mohli byste mi laskavě podat informaci o vašem jméně?“ a normálním „Jak se jmenuješ?“.

Jasně, možná namítnete, že přímý přístup k datům může svádět k porušování principů objektově orientovaného návrhu. Že místo ptaní se objektu na data bychom ho měli požádat o akci (Tell-Don't-Ask). To je pravda – ale hlavně pro objekty s bohatým chováním, které implementují business logiku. Pro datové transfer objekty, value objects nebo konfigurační třídy je přímý přístup k datům naprosto přirozený.

Zároveň nám tu vzniká pořádné dilema. Co s existujícími projekty? Pokud máte knihovnu nebo framework, který důsledně používá gettery a settery, bylo by možná kontraproduktivní do něj najednou zavádět property. Rozbili byste tím konzistenci API – uživatel by musel hádat, kde použít metodu a kde vlastnost.

Časem se určitě vytvoří nové styly a konvence. Některé projekty možná zůstanou u getterů a setterů, jiné budou hledat cesty jak začlenit property. Hlavně že máme na výběr.

Důležité je i pojmenování

Jak vlastně property pojmenovat? Zejména u boolean hodnot to není tak přímočaré, jak by se mohlo zdát.

U metod se běžně používají prefixy is nebo has:

class Article {
	public function isPublished(): bool { ... }
	public function hasComments(): bool { ... }
}

Ale u properties by tyto prefixy působily krkolomně a redundantně. Místo nich je lepší používat přídavná jména nebo podstatná jména:

class Article {
	public bool $published;     // lepší než $isPublished
	public bool $commented;     // lepší než $hasComments
	public bool $draft;         // lepší než $isDraft
}

if ($article->published) {      // čte se přirozeně
	// ...
}

Pro počty položek je lepší použít množné číslo:

class Article {
	public int $views;          // lepší než $viewCount
	public array $tags;         // jasně říká, že jde o kolekci
}

Jde o to, aby kód byl čitelný jako běžná věta. Když píšeme if ($article->published), čte se to mnohem přirozeněji než if ($article->isPublished). Property by měly vypadat jako vlastnosti, ne jako zapomenuté závorky u metody.

Kdy použít property a kdy metody?

Výborná otázka! Tady si můžeme vzít inspiraci z jazyků jako C# nebo Kotlin, které s property pracují už roky. Property se skvěle hodí pro:

Value objects a DTO:

class Money {
	public readonly float $amount;
	public readonly string $currency;
}

Jednoduché entity:

class Article {
	public string $title;
	public string $content;
	public DateTimeImmutable $publishedAt;
	public bool $published {
		get => $this->publishedAt <= new DateTimeImmutable;
	}
}

Computed hodnoty závislé na jiných vlastnostech:

class Rectangle {
	public float $width;
	public float $height;
	public float $area {
		get => $this->width * $this->height;
	}
}

Metody jsou lepší pro:

Všechna tato doporučení se nám snaží říct jednu základní věc: za použitím property by se neměl skrývat žádný složitý proces nebo něco, co má vedlejší efekty. Složitost operace by měla zhruba odpovídat tomu, co intuitivně očekáváme od čtení či zápisu do proměnné.

I když… vzpomeňte si na innerHTML v JavaScriptu. Když napíšete element.innerHTML = '<p>Ahoj</p>', spustí se složitý proces parsování HTML, vytvoření DOM stromu, překreslení stránky… A přesto to všichni považují za přirozené!

Takže možná důležitější než samotná složitost implementace je to, jestli daná operace _konceptuálně_ odpovídá vlastnosti. Je to jako s autem – tlačítko start/stop může spustit složitou sekvenci kroků, ale pro řidiče je to pořád jen „zapnout/vypnout“.

Readonly vlastnosti v PHP a jejich skrytá úskalí 24.11.2024 17:58

Představte si, že byste mohli svým datům dát pevnou půdu pod nohama – jednou je nastavíte a pak si můžete být jistí, že je nikdo nezmění. Přesně to přineslo PHP 8.1 s readonly vlastnostmi. Je to jako dát vašim objektům neprůstřelnou vestu – chrání jejich data před nechtěnými změnami. Pojďme se podívat, jak vám tento mocný nástroj může usnadnit život a na co si při jeho používání dát pozor.

Začněme jednoduchým příkladem:

class User
{
    public readonly string $name;

    public function setName(string $name): void
    {
        $this->name = $name;  // První nastavení - vše OK
    }
}

$user = new User;
$user->setName('John');      // Paráda, máme jméno
echo $user->name;            // "John"
$user->setName('Jane');      // BOOM! Výjimka: Cannot modify readonly property

Jakmile jednou jméno nastavíte, je to jako vytesané do kamene. Žádné náhodné přepsání, žádné nechtěné změny.

Kdy je uninitialized opravdu uninitialized?

Často se setkávám s mýtem, že readonly vlastnosti musí být nastaveny v konstruktoru. Ve skutečnosti je PHP mnohem flexibilnější – můžete je inicializovat kdykoliv během života objektu, ale pouze jednou! Před prvním přiřazením jsou ve speciálním stavu ‚uninitialized‘, což je takový limbo stav mezi nebytím a bytím.

A tady přichází zajímavý detail – readonly vlastnosti nemohou mít výchozí hodnotu. A proč? Kdyby měly výchozí hodnotu, staly by se de facto konstantami – hodnota by byla nastavena při vytvoření objektu a už by nešla změnit.

Vyžadují se typy

Readonly proměnné vyžadují explicitní definici datového typu. Je to proto, že stav ‚uninitialized‘, který využívají, existuje pouze u typovaných proměnných. Bez uvedení typu tedy readonly proměnnou nelze definovat. Pokud si nejste jistí typem, můžete použít mixed.

Readonly třídy: Když jeden zámek nestačí

S PHP 8.2 přišla možnost posunout zabezpečení na další úroveň. Představte si, že místo zamykání jednotlivých místností můžete zamknout celou budovu. Tedy celou třídu:

readonly class User
{
    public string $name;     // Automaticky readonly!
    public string $email;    // Taky readonly!
}

Ale pozor, s velkou mocí přichází velká omezení:

Kdo může inicializovat a kdy?

Tady je to zajímavé – podívejte se na tento kód:

$user = new User;
$user->name = 'John';  // BUUM! Cannot initialize readonly property from global scope

Překvapení? I když jde o první přiřazení, PHP řekne ne. Stejně tak potomek třídy nemůže inicializovat readonly vlastnost svého rodiče:

class Employee extends User
{
    public function setName(string $name): void
    {
        $this->name = 'EMP: ' . $name;  // BUUM! Ani potomek nemůže!
    }
}

Readonly proměnnou lze tedy inicializovat výhradně ze třídy, která ji definovala. Přesněji řečeno šlo. PHP 8.4 totiž přináší dvě důležité změny:

class User
{
    public(set) readonly string $name;  // Nová svoboda v PHP 8.4
}

$user = new User;
$user->name = 'John';  // Teď už to funguje!

Když readonly neznamená „opravdu neměnné“

Představte si readonly jako zámek na dveřích – zamkne dveře, ale co se děje uvnitř místnosti, to už neuhlídá. Samotné readonly nezaručuje úplnou neměnnost dat. Pokud do readonly proměnné uložíme objekt, jeho vnitřní stav zůstává modifikovatelný, objekt se automaticky nestává immutable (neměnným):

class Settings
{
	public string $theme = 'light';
}

class Configuration
{
	public function __construct(
		public readonly Settings $settings = new Settings,
	) {
	}
}

$config = new Configuration;
$config->settings->theme = 'dark'; // toto je povoleno, přestože $settings je readonly!

Vidíte? Samotný objekt $settings je uzamčený, ale jeho vnitřnosti můžeme měnit, jak se nám zlíbí.

U polí je situace specifická. Přímá modifikace prvků pole není možná, protože PHP to považuje za změnu celého pole.

class Configuration
{
    public readonly array $settings;

    public function initialize(): void
    {
        $this->settings = ['debug' => true];
        $this->settings['cache'] = true;  // BUUM! Tohle neprojde
    }
}

Existuje však výjimka – pokud pole obsahuje reference, jejich obsah měnit můžeme, protože PHP to nepovažuje za změnu samotného pole. Toto chování je konzistentní s běžným fungováním PHP:

class Configuration
{
    public readonly array $settings;

    public function initialize(): void
    {
        // Trik s referencí
        $mode = 'development';
        $this->settings = [
            'debug' => true,
            'mode' => &$mode,  // Reference je naše tajná zbraň!
        ];

        $mode = 'production';  // Tohle projde!
    }
}

Wither metody a readonly: Jak na to?

Při práci s neměnnými objekty často potřebujeme implementovat metody pro změnu stavu. Tyto „wither“ metody (na rozdíl od klasických setterů) nemodifikují původní objekt, ale vrací jeho novou instanci s požadovanou změnou. Tento pattern využívá například specifikace PSR-7 pro HTTP požadavky.

Když chceme tyto objekty nebo jejich vlastnosti označit jako readonly, narazíme na technické omezení – readonly vlastnost nelze změnit ani ve wither metodě, a to ani v kopii objektu. I když PHP 8.3 umožňuje měnit readonly vlastnosti v metodě __clone(), samotné klonování nestačí, protože v něm nemáme přístup k nové hodnotě. Můžeme to ale vyřešit pomocí následující obezličky:

class Request
{
    private array $changes = [];

    public function __construct(
        public readonly string $method = 'GET',
        public readonly array $headers = [],
    ) {}

    public function withMethod(string $method): self
    {
        $this->changes['method'] = $method;
        $dolly = clone $this;
        $this->changes = [];
        return $dolly;
    }

    public function __clone()
    {
        foreach ($this->changes as $property => $value) {
            $this->$property = $value;
        }
        $this->changes = [];
    }
}

$request = new Request('GET');
$newRequest = $request->withMethod('POST');  // Původní $request zůstává s GET

Testování a BypassFinals

Při psaní testů můžeme narazit na praktický problém – readonly vlastnosti (podobně jako final) komplikují mockování a testování. Naštěstí existuje elegantní řešení v podobě knihovny BypassFinals.

Tato knihovna dokáže za běhu odstranit klíčová slova final a readonly z vašeho kódu, což umožňuje mockovat i třídy a metody, které jsou takto označené. Integrace s testovacími frameworky je přímočará:

// bootstrap.php nebo začátek test souboru
DG\BypassFinals::enable();

// Pokud chceme zachovat readonly a odstranit jen final:
DG\BypassFinals::enable(bypassReadOnly: false);

Shrnutí: Co si odnést

Readonly vlastnosti jsou mocný nástroj pro zvýšení bezpečnosti a předvídatelnosti vašeho kódu. Zapamatujte si klíčové body:

Dvě slova, co ničí open source 4.10.2024 22:57

Víte, co nikdy, ale opravdu NIKDY nemáte psát autorům open source projektů? „Nemám čas“. Tahle dvě slova mají schopnost rozpustit motivaci vývojářů rychleji než mizí baterka na iPhonu při scrollování TikToku.

Vážně? VÁŽNĚ?!

Představte si, že jste na párty a někdo vám řekne: „Hej, ty tam s tím pivem! Udělej mi sendvič. Nemám čas si ho udělat sám, jsem příliš zaneprázdněn konzumací chipsů.“ Jak byste se cítili? Jako obědový automat s lidskou tváří? Přesně tak se cítím já, když čtu taková slova. Okamžitě ztrácím chuť věc řešit a mám nutkání se jít věnovat čemukoliv jinému. Třeba pustému nicnedělání.

Víte, my open source vývojáři jsme zvláštní stvoření. Trávíme hodiny našeho volného času tvorbou softwaru, který pak dáváme k dispozici všem. Zadarmo. Dobrovolně. Jako kdyby Ježíšek rozdával dárky každý den v roce a ne jen na Vánoce. Baví nás to. Ale tím vám nevzniká nárok nás úkolovat jako nějaké digitální otroky. Takže když někdo přijde s požadavkem na novou funkci, ale „nemá čas“ přiložit ruku k dílu, okamžitě tím vyvolá otázku „a proč bych já ten čas měl mít?“ Jako byste chtěli po Michelangelovi, aby vám vymaloval obývák, protože vy „nemáte čas“ to udělat sami, šak stejně nemá co lepšího na práci.

Za roky se mi nashromáždily desítky issues u různých projektů, ve kterých jsem poprosil „Mohl bys připravit pull request?“ a odpovědí bylo „Mohl, ale tento týden nemám čas.“ Kdyby ten nebožák onu větu nenapsal, nejspíš bych věc dávno vyřešil. Takhle mi ale řekl, že pohrdá mým časem. Takže to vyřešil sám za týden? Kdeže… 99 % věcí, které kdy kdo slíbil, nikdy nedodal, tudíž i 99 % těchto issues jsou navždy nevyřešené. Visí tam jako digitální pomníky lidské lenosti.

Takže, milí uživatelé, příště než napíšete „Nemám čas“, zamyslete se. Ve skutečnosti říkáte: „Hej, ty tam! Tvůj volný čas nemá žádnou hodnotu. Hoď všechno co děláš za hlavu a věnuj se MÉ záležitosti!“ Zkuste místo toho:

Když narazíte na bug, budete chtít novou featuru, nebo zjistíte, že by stálo za to něco doplnit do dokumentace, zkuste pro jednou prospět komunitě. Protože v open source světě jsme všichni na jedné lodi. A ta loď pluje na vlnách vzájemného respektu a spolupráce. Tak nezapomeňte občas také zaveslovat, místo abyste jen seděli a stěžovali si, že nemáte čas na pádlování. Vaše „nemám čas“ je absolutní způsob, jak zničit motivaci lidí, kteří vám zdarma poskytují software. Zkuste si těch pár minut nebo hodin najít. Vaše karma vám poděkuje.

Proč GPT je SQL našeho století? 28.5.2024 19:34

A naopak SQL bylo GPT sedmdesátých let?

SQL, vzniklé v 70. letech minulého století, představovalo revoluční průlom v interakci člověka s počítačem. Jeho design byl navržen tak, aby se dotazy formulovaly a četly co nejvíce jako běžná angličtina. Například, dotaz na jména a platy zaměstnanců v SQL může vypadat takto: SELECT name, salary FROM employee – jednoduché a srozumitelné, že ano? Tím se databáze staly dostupné širší veřejnosti, nejen počítačovým nerdům.

Ačkoli tento záměr byl chvályhodný, brzy se ukázalo, že na psaní SQL dotazů jsou stejně potřeba experti. Navíc vzhledem k tomu, že nikdy nevzniklo univerzální aplikační rozhraní, stalo se slovní ovládání pro moderní programování spíše břemenem. Programátoři dnes komunikují s databázemi psaním komplikovaných generátorů SQL příkazů, které databáze následně zpětně dekódují.

A tady na scénu vstupuje GPT. Tyto pokročilé jazykové modely přinášejí v éře umělé inteligence obdobnou revoluci, jakou SQL znamenalo pro databáze ve své době. Umožňují interakci s databázemi na úplně nové úrovni, o 50 let pokročilejší. Přestože zatím neexistuje databáze, která by byla přímo ovládaná jazykovým modelem, můžeme si od GPT nechat generovat SQL. A funguje to skvěle.

Jak na to? Stačí, když do ChatGPT zkopírujete strukturu vaší databáze, tedy dump s příkazy CREATE TABLE, a uvedete i název databázového serveru, který používáte. Následně můžete klást otázky přímo v češtině.

Pokud si chcete použití této techniky ještě více ulehčit, použijte předpřipravený SQL Wizard, kam stačí jen překopírovat strukturu databáze a můžete se rovnou ptát bez dalších řečí.

Pojďme si to prakticky ukázat. Jako příklad použijeme dvě databázové tabulky, které jsou provázané skrze cizí klíč. Tabulka users bude obsahovat informace o uživatelích a tabulka orders bude ukládat informace o objednávkách, které uživatelé udělali. Zde jsou definice těchto tabulek:

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100)
);

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    user_id INT,
    created_at DATE,
    amount DECIMAL(10, 2),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

Definice zkopírujte do ChatGPT a hned se můžete zcela přirozeně a v češtině zeptat například na všechny objednávky od uživatele Jana Nováka. Konkrétně zadám: „Napiš SQL, které vypíše všechny objednávky uživatele Jana Nováka.“ A GPT odpoví například takto:

SELECT orders.order_id, orders.created_at, orders.amount
FROM orders
JOIN users ON orders.user_id = users.id
WHERE users.name = 'Jan Novák';

Takový SQL dotaz dokáže samozřejmě spousta programátorů napsat rychleji, než by se přihlásili do ChatGPT 🙂 Ale zkusme maličko složitější příklad: „Vypiš uživatele, kteří mají jen jednu objednávku.“

SELECT users.id, users.name, users.email
FROM users
JOIN orders ON users.id = orders.user_id
GROUP BY users.id, users.name, users.email
HAVING COUNT(orders.order_id) = 1;

Nebo „Vypiš uživatele, kteří nemají žádnou objednávku.“

SELECT users.*
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE orders.order_id IS NULL;

Tady se ukazuje diametrální rozdíl v přístupu. Zatímco GPT říkám, jaká data chci získat, v SQL musím popsat způsob, jak je získat. A to vyžaduje znalosti a zkušenosti.

Díky GPT se můžete soustředit na to, co chcete získat, místo toho, jak to získat, což je zvláště cenné pro ty, kteří nejsou odborníky na SQL.

Na závěr si ukážeme, jak lze využít GPT k vytvoření databázových trigerů. Například můžete napsat: „Přidej do tabulky users sloupec order_count, který bude evidovat počet objednávek. Inicializuj ho na hodnotu odpovídající aktuálnímu počtu objednávek každého uživatele. A vytvoř trigger, který při vytvoření nebo smazání objednávky bude hodnotu aktulizovat.“

GPT vygeneruje dotazy pro přidání a inicializaci sloupce order_count:

ALTER TABLE users ADD COLUMN order_count INT DEFAULT 0;
UPDATE users SET order_count = (SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id);

a požadované triggery, které udržují jeho hodnotu aktuální:

CREATE TRIGGER update_order_count_after_insert
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
    UPDATE users
    SET order_count = order_count + 1
    WHERE id = NEW.user_id;
END;

CREATE TRIGGER update_order_count_after_delete
AFTER DELETE ON orders
FOR EACH ROW
BEGIN
    UPDATE users
    SET order_count = order_count - 1
    WHERE id = OLD.user_id;
END;

GPT nabízí způsob, jak efektivně a intuitivně pracovat s databázemi, i těm, kteří nejsou odborníky na SQL. Je to revoluční nástroj, který tentokrát opravdu zpřístupňuje pokročilé databázové operace široké veřejnosti. Stále je však důležité mít na paměti, že každý výstup by měl být pečlivě kontrolován, aby se zajistila správnost a bezpečnost dat.


Pokud jste připraveni posunout své dovednosti, přijďte na školení ChatGPT. Toto setkání vás naučí, jak z něj vytáhnout maximum pro váš osobní i profesní život. Nezáleží na tom, zda jste začátečník nebo pokročilý uživatel, školení bude pro vás velkým přínosem.

Jak zvládnout gettery, když nemají co vrátit? 13.5.2024 21:20

Vývoj softwaru často přináší dilema. Například jak řešit situace, kdy getter nemá co vrátit. V tomto článku prozkoumáme tři strategie pro implementaci getterů v PHP, které ovlivňují strukturu a čitelnost kódu, a každá má své specifické výhody i nevýhody. Pojďme se na ně podrobněji podívat.

Univerzální getter s parametrem

Prvním a v Nette používaným řešením je vytvoření jediné getter metody, která, pokud hodnota není dostupná, může dle potřeby vrátit buď null nebo vyhodit výjimku. O chování rozhoduje volitelný parametr. Zde je příklad, jak by mohla metoda vypadat:

public function getFoo(bool $need = true): ?Foo
{
    if (!$this->foo && $need) {
        throw new Exception("Foo not available");
    }
    return $this->foo;
}

Hlavní výhodou tohoto přístupu je, že eliminuje potřebu mít několik verzí getteru pro různé scénáře použití. Někdejší nevýhodou byla horší srozumitelnost uživatelského kódu používajícího booleovské parametry, ale ta padla s příchodem pojmenovaných parametrů, kdy lze psát getFoo(need: false).

Dále tento přístup může způsobit komplikace v oblasti statické analýzy, jelikož dle signatury se zdá, že getFoo() může vrátit null v každé situaci. Nicméně nástroje jako PHPStan umožňují explicitní dokumentaci chování metody pomocí speciálních anotací, které zlepšují porozumění kódu a jeho správnou analýzu:

/** @return ($need is true ? Foo : ?Foo) */
public function getFoo(bool $need = true): ?Foo
{
}

Tato anotace jasně určuje, jaké návratové typy může metoda getFoo() generovat v závislosti na hodnotě parametru $need. Ale například PhpStorm jí nerozumí.

Dvojice metod: hasFoo() a getFoo()

Další možností je rozdělit zodpovědnost na dvě metody: hasFoo() pro ověření existence hodnoty a getFoo() pro její získání. Tento přístup zvyšuje přehlednost kódu a je intuitivně srozumitelný.

public function hasFoo(): bool
{
    return (bool) $this->foo;
}

public function getFoo(): Foo
{
    return $this->foo ?? throw new Exception("Foo not available");

Hlavním problémem je redundance, zvláště v případech, kdy je kontrola dostupnosti hodnoty sama o sobě náročným procesem. Pokud hasFoo() provádí složité operace k ověření, zda je hodnota dostupná, a tato hodnota je poté opět získávána pomocí getFoo(), dojde k jejich opětovnému provedení. Hypoteticky může být stav objektu nebo dat změněn mezi voláním hasFoo() a getFoo(), což může vést k nesrovnalostem. Z uživatelského pohledu může být tento přístup méně pohodlný, protože nás nutí volat dvojici metod s opakujícím se parametrem. A nemůžeme využít například null-coalescing operátor.

Výhodou je, že některé nástroje pro statickou analýzu umožňují definovat pravidlo, že po úspěšném volání hasFoo() nedojde v getFoo() k vyhození výjimky.

Metody getFoo() a getFooOrNull()

Třetí strategií pro je rozdělení funkcionality na dvě metody: getFoo() pro vyhození výjimky, pokud hodnota neexistuje, a getFooOrNull() pro vrácení null. Tento přístup minimalizuje redundanci a zjednodušuje logiku.

public function getFoo(): Foo
{
    return $this->getFooOrNull() ?? throw new Exception("Foo not available");
}

public function getFooOrNull(): ?Foo
{
    return $this->foo;
}

Alternativou je dvojice getFoo() a getFooIfExists(), ale v tomto případě nemusí být zcela intuitivní pochopit, která metoda vyhazuje výjimku a která vrací null. O trošku výstižnější by byla dvojice getFooOrThrow() a getFoo(). Další možností je getFoo() a tryGetFoo().

Každý z představených přístupů k implementaci getterů v PHP má své místo v závislosti na specifických potřebách projektu a preferencích vývojářského týmu. Při výběru vhodné strategie je důležité zvážit, jaký dopad bude mít na čitelnost, údržbu a výkon aplikace. Volba by odrážet snahu o co nejsrozumitelnější a nejefektivnější kód.

Lze pomocí regulárních výrazů parsovat HTML? 23.12.2023 04:31

Pojďme jednou provždy rozlousknout tuhle věčnou otázku, která rozděluje komunitu programátorů. Rozhodl jsem se ponořit do temných vod regulárních výrazů, abych přinesl odpověď (spoiler: ano, je to možné).

Takže, co vlastně HTML dokument obsahuje? Jde o mix textu, entit, značek, komentářů a speciální značky doctype. Prozkoumejme nejprve každou ingredienci zvlášť.

Entity

Základem HTML stránky je text, který tvoří obyčejné znaky a speciální sekvence zvané HTML entity. Ty mohou být buď pojmenované, jako je &nbsp; pro nezlomitelnou mezeru, nebo číselné, a to buď v desítkovém &#160; nebo šestnáctkovém &#xA0; formátu. Regulární výraz, který zachytí HTML entitu, by vypadal takto:

(?<entity>
	&
	(
		[a-z][a-z0-9]+     # pojmenovaná entita
		|
		\#\d+              # desítkové číslo
		|
		\#x[0-9a-f]+       # hexadecimální číslo
	)
	;
)

Všechny regulární výrazy jsou zapsány v režimu extended, ignorují velikost písmen a tečka představuje jakýkoliv znak. Tj. modifikátor six.

Značky

Tyto ikonické prvky dělají HTML tím čím je. Tag začíná <, následuje název tagu, možná sada atributů a uzavírá se > nebo />. Atributy mohou mít volitelnou hodnotu a ta může být uvozena do dvojitých, jednoduchých nebo žádných uvozovek. Regulární výraz zachytávající atribut by vypadal takto:

(?<attribute>
	\s+                         # alespoň jeden bílý znak před atributem
	<a href="#fns" class="footnote">[\s&quot;&#039;&lt;&gt;=`/]</a>+               # název atributu
	(
		\s* = \s*               # rovnítko před hodnotou
		(
			"                   # hodnota uzavřená ve dvojitých uvozovkách
				(
					<a href="#fn" class="footnote">[&quot;]</a>        # libovolný znak kromě dvojité uvozovky
					|
					(?&entity)  # nebo HTML entita
				)*
			"
			|
			'                   # hodnota uzavřená v jednoduchých uvozovkách
				(
					<a href="#fn" class="footnote">[&#039;]</a>        # libovolný znak kromě uvozovky
					|
					(?&entity)  # nebo HTML entita
				)*
			'
			|
			<a href="#fns" class="footnote">[\s&quot;&#039;&lt;&gt;=`]</a>+         # hodnota bez uvozovek
		)
	)?                           # hodnota je volitelná
)

Všimněte si, že se odvolávám na pojmenovanou skupinu entity definovanou dříve.

Elementy

Element může představovat jak samostatná značka (tzv. prázdný element), tak značky párové. Existuje pevný výčet jmen prázdných elementů, podle kterých je rozeznáme. Regulární výraz pro jejich zachytávání by vypadal takto:

(?<void_element>
	<                  # začátek značky
	(                  # název elementu
		img|hr|br|input|meta|area|embed|keygen|source|base|col
		|link|param|basefont|frame|isindex|wbr|command|track
	)
	(?&attribute)*     # volitelné atributy
	\s*
	/?                 # volitelné /
	>                  # konec značky
)

Ostatní značky jsou tedy párové a zachytí je tento regulární výraz (používám v něm odvolávku na skupinu content, kterou teprve nadefinujeme):

(?<element>
	<                  # počáteční značka
	(?<element_name>
		[a-z]<a href="#fns" class="footnote">[\s/&gt;]</a>*  # název elementu
	)
	(?&attribute)*     # volitelné atributy
	\s*
	>                  # konec počáteční značky
	(?&content)*
	</                 # koncová značka
	(?P=element_name)  # zopakujeme název elementu
	\s*
	>                  # konec koncové značky
)

Speciálním případem jsou elementy jako <script>, jejichž obsah se musí zpracovávat odlišně od ostatních elementů:

(?<special_element>
	<                  # počáteční značka
	(?<special_element_name>
		script|style|textarea|title  # název elementu
	)
	(?&attribute)*     # volitelné atributy
	\s*
	>                  # konec počáteční značky
	(?>                # atomická skupina
		.*?            # nejmenší možný počet jakýchkoliv znaků
		</             # koncová značka
		(?P=special_element_name)
	)
	\s*
	>                  # konec koncové značky
)

Líný kvantifikátor .*? zajistí, že se výraz zastaví u první ukončovací sekvence, a atomická skupina zajistí, že toto zastavení bude definitivní.

Komentáře

Typický HTML komentář začíná sekvencí <!-- a končí sekvencí -->. Regulární výraz pro HTML komentáře může vypadat takto:

(?<comment>
	<!--
	(?>           # atomická skupina
		.*?       # nejmenší možný počet jakýchkoliv znaků
		-->
	)
)

Líný kvantifikátor .*? opět zajistí, že se výraz zastaví u první ukončovací sekvence, a atomická skupina zajistí, že toto zastavení bude definitivní.

Doctype

Jde o historický relikt, který dnes existuje jen proto, aby přepnul prohlížeč do tzv. standardního režimu. Obvykle vypadá jako <!doctype html>, ale může obsahovat i další znaky. Zde je regulární výraz, který jej zachytí:

(?<doctype>
	<!doctype
	\s
	<a href="#fn" class="footnote">[&gt;]</a>*         # jakékoliv znaky kromě '>'
	>
)

Dejme to dohromady

Když máme hotové regulární výrazy zachytávající každou část HTML, je čas vytvořit výraz pro celý HTML 5 dokument:

\s*
(?&doctype)?              # volitelný doctype
(?<content>
	(?&void_element)      # prázdný element
	|
	(?&special_element)   # speciální element
	|
	(?&element)           # párový element
	|
	(?&comment)           # komentář
	|
	(?&entity)            # entita
	|
	<a href="#fn" class="footnote">[&lt;]</a>                  # znak
)*

Všechny části můžeme spojit do jednoho komplexního regulárního výrazu. Tohle je on, superhrdina mezi regulárními výrazy se schopností parsovat HTML 5.

Závěrečné poznámky

I když jsme si ukázali, že HTML 5 lze parsovat pomocí regulárních výrazů, uvedený příklad k ničemu užitečný není. Nepomůže vám se zpracováním HTML dokumentu. Vyláme si zuby u nevalidního dokumentu. Bude pomalý. A tak dále. V praxi se používají spíš reguláry jako je tento (pro hledání URL obrázků):

<img.+?src=["'](.+?)["'].*?>

Ale to je opravdu velmi nespolehlivé řešení, která vede k chybám. Tento regexp chybně matchuje třeba custom tagy jako například <imgs-tag src="image.jpg">, custom atributy jako <img data-src="custom info">, nebo selže, když atribut bude obsahovat uvozovku <img src="mcdonald's.jpg">. Proto je doporučeno používat specializované knihovny. Ve světě PHP máme smolíčka, protože rozšíření DOM podporuje pouze pravěké ztrouchnivělé HTML 4. Naštěstí PHP 8.4 slibuje parser pro HTML 5.

Architektura, která roste s vaším projektem 23.1.2025 15:10

Architektura, která roste s vaším projektem

Jedna z nejčastějších výzev při vývoji PHP aplikací je správná organizace kódu. Kam umístit presentery? Kde by měly být jednotlivé třídy? A jak zajistit, aby struktura projektu rostla přirozeně s jeho vývojem?

Dokumentace Nette přináší komplexního průvodce adresářovou strukturou, který nabízí odpovědi na všechny tyto otázky.

Kvalita organizace kódu zásadně ovlivňuje jeho srozumitelnost. Při prvním pohledu na nový projekt byste měli rychle pochopit jeho účel. Podívejte se na tento adresář app/Model/:

app/Model/
├── Services/
├── Repositories/
└── Entities/

Co vám tato struktura prozradí o aplikaci? Téměř nic. Srovnejte s alternativou:

app/Model/
├── Cart/
├── Payment/
├── Order/
└── Product/

Na první pohled je jasné, že jde o e-shop. V tom spočívá síla doménově orientované struktury, kterou dokument představuje.

Průvodce také ukazuje, jak strukturu přirozeně rozvíjet s růstem projektu. Ať už začínáte nový projekt nebo chcete vylepšit existující aplikaci, najdete zde principy pro informovaná rozhodnutí o organizaci kódu.

Přečtěte si kapitolu Adresářová struktura aplikace v dokumentaci Nette a starší blogpost Elegantnější strukturování presenterů.

Jedna řádka v konfiguraci zrychlí vaši Nette aplikaci. Jak je to možné? 10.1.2025 06:46

Představte si, že máte velkou aplikaci s desítkami služeb – databáze, logger, mailer, cache a mnoho dalších. Při každém HTTP požadavku se všechny tyto služby vytvoří, i když je třeba vůbec nepoužijete. To zbytečně zpomaluje běh aplikace. Nová verze PHP 8.4 a Nette přináší elegantní řešení tohoto problému pomocí tzv. lazy objektů.

Co jsou lazy objekty?

Lazy objekt je speciální typ objektu, který z pohledu vašeho kódu vypadá a chová se naprosto stejně jako skutečná služba, ale ve skutečnosti odkládá svou inicializaci až do chvíle, kdy je skutečně potřeba. Když si z DI kontejneru vyžádáme například službu pro práci s databází, dostaneme objekt, který navenek vypadá jako běžná instance Database, ale ve skutečnosti ještě nemá vytvořené připojení k databázi. K tomu dojde až ve chvíli, kdy službu poprvé skutečně použijeme.

Příklad:

// Dostaneme objekt, který vypadá jako Database, ale ještě nemá vytvořené připojení
$database = $container->getByType(Database::class);

// Připojení k databázi se vytvoří AŽ ZDE, při prvním skutečném použití
$database->query('SELECT * FROM users');

Jak to zapnout?

V nové verzi Nette DI 3.2.4 stačí jediný řádek v konfiguraci:

di:
    lazy: true

To je vše! Od této chvíle budou všechny služby v DI kontejneru vytvářeny „líně“.

Lazy vytváření můžeme nastavit i pro jednotlivé služby:

services:
    newsletter:
		create: Newsletter
        lazy: false   # tato služba se vytvoří hned, i když je lazy globálně zapnuté

    database:
		create: Database
        lazy: true    # tato služba bude lazy, i když je lazy globálně vypnuté

Výhody v praxi

  1. Rychlejší start aplikace – vytváří se jen ty služby, které skutečně potřebujete
  2. Nižší spotřeba paměti – nepoužité služby nezabírají místo
  3. Jednoduchá implementace – stačí jeden řádek v konfiguraci

Je důležité počítat s tím, že lazy objekty logicky mění způsob, jak se projevují chyby v konfiguraci služeb. Například špatné přihlašovací údaje k databázi se neprojeví hned při startu aplikace, ale až při prvním skutečném připojení k databázi.

Skrytá výhoda – řešení cyklických závislostí

Lazy objekty přinášejí ještě jednu zajímavou výhodu – umožňují elegantně řešit cyklické závislosti mezi službami. Představte si situaci, kdy služba A potřebuje službu B a služba B zároveň potřebuje službu A. Nette DI kontejner takovou smyčku okamžitě detekuje a vyhodí výjimku „Circular reference detected“ místo toho, aby se PHP zacyklilo v nekonečné smyčce. S lazy objekty tento problém takřka zmizí. Služba A dostane při vytvoření lazy proxy služby B, která se inicializuje až při skutečném použití, kdy už služba A existuje a může být předána službě B jako závislost. I když cyklické závislosti nejsou něčím, co bychom měli při návrhu aplikace záměrně vytvářet, je zajímavé si uvědomit, že s lazy objekty tento problém mizí.

Omezení a doporučení

Lazy objekty představují významný krok vpřed v optimalizaci PHP aplikací. Díky nim mohou být vaše Nette aplikace rychlejší a efektivnější, aniž byste museli měnit jediný řádek svého kódu.

S novou dokumentací Nette Database píšete bezpečnější kód 9.1.2025 16:13

Nette Database konečně dostala to, co si už dlouho zasloužila – dokumentaci hodnou jejího potenciálu. Kompletně přepracovaný text nejen detailně popisuje všechny funkce, ale především otevírá oči v oblasti bezpečnosti databázových operací.

Dokumentace, co má šťávu

Kvalitní dokumentace je achillovou patou mnoha open-source projektů. Ne tak u Nette Framework. Ten se může pochlubit něčím, co konkurenci často chybí – precizní, srozumitelnou a živou dokumentací, která provází vývojáře od jejich prvních kroků až po pokročilé koncepty. Například dokumentace Dependency Injection kromě popisu samotného Nette DI obsahuje i srozumitelné teoretické vysvětlení problematiky, k jejímuž vzniku přispěl svými texty i Miško Hevery, autor frameworku Angular. Dokumentace Latte nabízí interaktivní vhled do problematiky escapování, který čtenáři objasní, proč jde o jediný bezpečný šablonovací systém v PHP. Jak daleko Nette zachází v péči o své uživatele ukazuje třeba existence kompletního úvodu do objektově orientovaného programování.

Ať už kódujete v jakémkoliv koutu světa, Nette s vámi mluví vaším jazykem. Veškerá dokumentace je dostupná v naší mateřštině a navíc v impozantních 15 světových jazycích: angličtina, němčina, španělština, francouzština, italština, maďarština, polština, portugalština, rumunština, slovinština, turečtina, řečtina, bulharština, ruština a ukrajinština.

Nová éra dokumentace Database

Byly tu však dvě oblasti, které trochu kazily jinak perfektní skóre – dokumentace k Nette Database a Tracy debuggeru. To se nyní pro Database mění. Podívejte se na přepracovanou a rozšířenou podobu nové dokumentace.

Obsah je přehledně rozdělen na dvě logické části podle přístupu k práci s databází:

Obsahuje velkou spoustu příkladů a ukázek kódu, které demonstrují reálné možnosti knihovny a inspirují k jejímu efektivnímu využití. Každý koncept je ilustrován na praktických případech použití, což umožňuje rychle pochopit principy a začít je aplikovat ve vlastních projektech.

Bezpečnost především (protože na ní závisí váš spánek)

V době, kdy jsou útoky na databáze na denním pořádku, přichází zbrusu nová část dokumentace s důkladnou technickou analýzou bezpečnostních rizik. Najdete zde praktické ukázky reálných hrozeb a jejich prevence:

Nová dokumentace spojuje popis funkcí s důkladným vysvětlením bezpečnostních aspektů a osvědčených postupů. Výsledkem je průvodce, který vám pomůže psát nejen funkční, ale i bezpečný kód. Pusťte se do čtení!

Nette PHP Generator přináší sílu PHP 8.4 29.11.2024 02:55

Property hooks a asymetrická viditelnost v PHP 8.4 představují velký skok v objektově orientovaném programování – konečně můžeme zapomenout na neohrabané gettery a settery. Nette PHP Generator přichází s podporou těchto novinek zároveň s vydáním PHP 8.4. A nabízí intuitivní způsob, jak tyto moderní features využít ve vašem kódu.

Zbavte se getterů a setterů

Představte si, že vytváříte třídu Person a chcete validovat věk. Dříve by váš kód musel vypadat nějak takto:

class Person
{
    private int $age;

    public function setAge(int $age): void
    {
        if ($age < 0) {
            throw new \InvalidArgumentException;
        }
        $this->age = $age;
    }

    public function getAge(): int
    {
        return $this->age;
    }
}

S property hooks je kód elegantnější a přehlednější:

class Person
{
    public int $age {
        set {
            if ($value < 0) {
                throw new \InvalidArgumentException;
            }
            $this->age = $value;
        }
    }
}

Vygenerovat tento kód je s Nette PhpGenerator snadné. Stačí využít novou metodu addHook():

$class = new ClassType('Person');
$class->addProperty('age')
	->setType('int')
	->addHook('set')
		->setBody(<<<'PHP'
			if ($value < 0) {
				throw new InvalidArgumentException;
			}
			$this->age = $value;
			PHP);

Nabízí se také použít zkrácenou syntaxi:

$class = new ClassType('Person');
$class->addProperty('age')
	->setType('int')
	->addHook('set', '$value >= 0 ? $value : throw new \\InvalidArgumentException');

Která vygeneruje „short-setter“:

class Person
{
	public int $age {
		set => $value >= 0 ? $value : throw new \InvalidArgumentException;
	}
}

Property v rozhraních a abstraktních třídách

Ano, konečně lze v PHP používat properties v rozhraních a abstraktních třídách. Nejprve vytvoříme vlastnost v rozhraní:

$interface = new Nette\PhpGenerator\InterfaceType('Named');
$interface->addProperty('name')
    ->setType('string')
    ->addHook('get'); // Vyžaduje pouze čtení

Vygeneruje:

interface Named
{
    public string $name { get; }
}

Toto je příklad abstraktní třídy, která kombinuje abstraktní setter a konkrétní getter:

$abstract = new Nette\PhpGenerator\ClassType('Person')
	->setAbstract();
$prop = $abstract->addProperty('email')
	->setType('string')
	->setAbstract();       // Abstraktní property
$prop->addHook('set')      // Abstraktní setter
	->setAbstract();
$prop->addHook('get', '$this->email');  // Konkrétní getter

Vygeneruje:

abstract class Person
{
	public string $email {
		set;
		get => $this->email;
	}
}

A nakonec implementující třída. Tady nám pomůže ClassManipulator:

$class = new ClassType('Employee');

// Pomocí manipulátoru implementujeme Person a Named
$manipulator = new Nette\PhpGenerator\ClassManipulator($class);
$manipulator->implement('Person'); // přidá property email
$manipulator->implement('Named'); // přidá property name

// Dodáme implementaci abstraktní property z Person
$class->getProperty('email')
	->addHook('set')
		->setBody(<<<'PHP'
			if (!Nette\Utils\Validators::isEmail($value)) {
				throw new InvalidArgumentException;
			}
			$this->email = $value;
			PHP);

echo $class;

Vygeneruje:

class Employee extends Person implements Named
{
	public string $email {
		set {
			if (!Nette\Utils\Validators::isEmail($value)) {
				throw new InvalidArgumentException;
			}
			$this->email = $value;
		}
	}

	public string $name;
}

Asymetrická viditelnost: přesná kontrola přístupu

Asymetrická viditelnost přináší do PHP jemnější kontrolu nad tím, kdo může číst a kdo zapisovat do vlastností objektu.

Viditelnost lze nastavit buď pomocí metody setVisibility() se dvěma parametry, nebo pomocí setPublic(), setProtected() nebo setPrivate() s parametrem mode, který určuje, zda se viditelnost vztahuje ke čtení nebo zápisu vlastnosti. Výchozí režim je 'get'.

$class = new Nette\PhpGenerator\ClassType('Demo');

$class->addProperty('name')
    ->setType('string')
    ->setVisibility('public', 'private'); // public pro čtení, private pro zápis

$class->addProperty('id')
    ->setType('int')
    ->setProtected('set'); // protected pro zápis

Vygeneruje:

class Demo
{
    public private(set) string $name;

    protected(set) int $id;
}

Asymetrickou viditelnost můžete efektivně kombinovat s property hooks a využít v hierarchii tříd.


Všechny tyto novinky jsou plně podporovány v metodách ClassLike::from() pro generování z existujících tříd a ClassLike::fromCode() pro načítání ze zdrojových souborů. Pro správnou funkci fromCode() je vyžadován balíček nikic/php-parser minimálně ve verzi 5.3.1.

Podpora těchto revolučních features přichází s verzí Nette PHP Generator 4.1.7.

Nette Utils: generátory výkonu a efektivity 7.8.2024 18:52

Nette Framework přináší výkonnostní výhody a optimalizaci práce s pamětí využitím tzv. generátorů. Tyto novinky vám umožní pracovat s velkými datovými sadami elegantněji a efektivněji, aniž byste museli měnit způsob, jakým píšete svůj kód. Pojďme se podívat na tři oblasti, kde Nette využívá sílu generátorů způsobem, který možná překvapí i zkušené vývojáře.

Čtení souborů po řádcích s FileSystem::readLines()

Práce s velkými soubory byla vždy výzvou, zejména pokud jde o paměťovou náročnost. Nette přichází s elegantním řešením v podobě metody FileSystem::readLines().

use Nette\Utils\FileSystem;

$lines = FileSystem::readLines('large_file.txt');
foreach ($lines as $number => $line) {
    echo "Řádek $number: $line\n";
    // Zpracování řádku...
}

Na první pohled se může zdát, že tento kód dělá totéž co nativní funkce PHP file(). Ale zdání klame. Zatímco file() načte celý soubor do paměti najednou, což může být problematické u velkých souborů, readLines() využívá generátor k postupnému čtení souboru.

Klíčové výhody:

Toto řešení je ideální pro zpracování logů, velkých datových exportů nebo jakýchkoliv rozsáhlých textových souborů.

Lazy regulární výrazy se Strings::matchAll()

Metoda Strings::matchAll() dostala v nové verzi Nette Utils 4.0.5 zajímavý upgrade. Nový parametr $lazy umožňuje postupné zpracování řetězce při hledání shod s regulárním výrazem.

use Nette\Utils\Strings;

$text = file_get_contents('very_long_text.txt');
$pattern = '/\b\w{5,}\b/'; // slova s 5 a více znaky

$matches = Strings::matchAll($text, $pattern, lazy: true);
foreach ($matches as $match) {
    echo "Nalezeno slovo: {$match[0]}\n";
    // Můžeme kdykoliv přerušit zpracování
    if ($match[0] === 'konec') {
        break;
    }
}

Bez parametru lazy by se rovnou prohledal celý text a všechny shody by se uložily do paměti. S lazy: true se text prochází postupně a shody se hledají za běhu.

Výhody:

Toto je zvláště užitečné při analýze velkých textů, parsování logů nebo při hledání specifických vzorů v rozsáhlých datech.

Efektivní práce pomocí třídy Iterables

Nová třída Iterables přináší sadu metod pro práci s iterovatelnými strukturami, jde o obdobu toho, co nabízí Arrays pro pole. Navíc však má metody, které jsou optimalizovány pro postupné zpracování. Pojďme si ukázat, jak je můžete využít ve vašem kódu.

use Nette\Utils\Iterables;

$numbers = // velké množství dat

// filtrování prvků
$evenNumbers = Iterables::filter($numbers, fn($n) => $n % 2 === 0);

// transformace prvků
$squared = Iterables::map($numbers, fn($n) => $n * $n);

// transformace klíčů i hodnot
$squaredKeys = Iterables::mapWithKeys($fruits, fn($v, $k) => [$k * $k, $n]);

Klíčovou výhodou je líné vyhodnocování, kdy transformace se provádí až v momentě, kdy je prvek skutečně potřeba. Můžete kdykoliv ukončit iteraci a ušetřit tak výpočetní čas:

foreach ($evenNumbers as $number) {
    if ($number > 20) break; // Můžeme kdykoliv ukončit
}

Tyto metody jsou zvláště užitečné při práci s velkými kolekcemi dat, jako jsou rozsáhlé databázové výsledky nebo při zpracování streamů dat. Navíc jsou zcela transparentní a můžete je začít používat okamžitě, aniž byste museli zásadně měnit strukturu vašeho kódu. Nette se snaží přinášet pokročilé funkce způsobem, který je přístupný a snadno použitelný pro vývojáře všech úrovní.

Latte: jeden řádek a lokalizuje za vás 6.8.2024 22:07

Dobrá zpráva pro všechny vývojáře používající Latte, nová verze přináší významné vylepšení v oblasti lokalizace. Pojďme se podívat, jak vám tyto novinky mohou usnadnit práci a uspořit čas.

Klíčovou novinkou Latte verze 3.0.18 je možnost nastavit národní prostředí. Je to otázka jednoho řádku kódu:

$latte = new Latte\Engine;
$latte->setLocale('cs_CZ');

Tímto jednoduchým krokem zajistíte, že všechny lokalizované filtry budou respektovat české prostředí. Můžete samozřejmě použít jakýkoli platný IETF language tag, například en_US pro angličtinu ve Spojených státech nebo de_DE pro němčinu v Německu.

Nastavení prostředí ovlivňuje filtry sort, number, bytes a zbrusu nový filtr localDate.

Nový filtr |localDate

Tento filtr je opravdovým klenotem nové verze. Umožňuje vypisovat data a časy s ohledem na místní zvyklosti. Nabízí dva hlavní způsoby použití, které pokryjí prakticky všechny vaše potřeby při formátování časových údajů.

Parametr format

Jde o řetězec, který umožňuje přesně specifikovat, jak má výsledné datum vypadat. Něco podobného znáte z PHP funkcí date() nebo strftime(). Ale pozor, tento formát je v něčem magický. Přizpůsobí se jakémukoli jazyku. Pojďme si to ukázat.

{$date|localDate: 'd MMMM yyyy'}

Písmena d, M a y představují den, měsíc a rok, yyyy značí, že rok se má vypsat v plné čtyřčíselné formě, obdobně „dlouhé“ MMMM značí měsíc celým slovem. Ale co pořadí dne a měsíce? Nebo oddělovače? Ty se přeci v různých locale zásadně liší!

To nechte na Latte. Nemusíte se starat o pořadí dne a měsíce nebo o to, jaké použít oddělovače. Latte to všechno vyřeší za vás a vypíše datum správně:

Jak vidíte, formát je zcela nezávislý na lokálním nastavení. Vlastně by jej bylo možné zapsat třeba takto: yyyyMMMMd, na pořadí symbolů a mezer mezi nimi totiž nezáleží.

Pojďme se podívat na další příklady:

{* rok a měsíc *}
{$date|localDate: 'yyyyMMMM'}
{* ČJ: duben 2024, EN: April 2024 *}

{* krátký formát data *}
{$date|localDate: 'yMd'}
{* ČJ: 15. 4. 2024, EN: 4/15/2024 *}

{* základní formát času *}
{$time|localDate: 'jm'}
{* ČJ: 14:30, EN: 2:30 PM *}

{* krátký formát bez dopoledne/odpoledne *}
{$time|localDate: 'Hm'}
{* ČJ: 14:30, EN: 14:30 *}

Podobný popis najdete v dokumentaci filtru localDate.

Předpřipravené šablony s date a time

Druhý způsob využití filtru localDate nabízí předpřipravené šablony date a time. Ty fungují jako zkratky pro nejběžnější formáty dat a časů. K dispozici máte čtyři úrovně detailu: full, long, medium a short. Můžete je kombinovat nebo použít samostatně:

{* pouze datum *}
{$date|localDate: date: short}
{* ČJ: 15.04.2024, EN: 4/15/24 *}

{* pouze čas *}
{$date|localDate: time: medium}
{* ČJ: 14:30:45, EN: 2:30:45 PM *}

{* krátký formát data a času *}
{$date|localDate: date: short, time: short}
{* ČJ: 23.04.2024 14:30, EN: 4/23/24, 2:30 PM *}

Bonusem je možnost použít pro datumy prefix relative-, který relativně blízká data vypíše jako včera, dnes a zítra. V sobotu večer a v neděli ráno 🙂

{$yesterday|localDate: date: relative-short}
{* Výstup: včera *}

Pokud filtr zavoláte bez parametrů, vypíše se jako dlouhý formát data:

{$date|localDate}
{* ČJ: 23. dubna 2024, EN: April 23, 2024 *}

Použití filtru localDate pro formátování času vám umožní vytvářet mezinárodní aplikace s minimálním úsilím. Ale je skvělý i v případě, že tvoříte aplikaci jen v jednom jazyce.

Filtr |number: Známá tvář s novými triky

Filtr number existuje v Latte již od prapočátku. Jeho standardní chování se novou verzí nemění a zůstává zcela zpětně kompatibilní. Teprve ve chvíli, kdy nastavíte locale, začne automaticky používat národní oddělovače pro desetinné čárky a tisíce:

{* S nastaveným českým locale *}
{1234.56|number}  {* Výstup: 1 234,56 *}

{* S nastaveným anglickým (US) locale *}
{1234.56|number}  {* Výstup: 1,234.56 *}

A teď přichází ta nejlepší část – můžete si s čísly vyhrát jako nikdy předtím. S novým parametrem pro formátování čísel si můžete jejich podobu ušít přesně na míru. Získáváte nebývalou kontrolu nad tím, jak se budou vypisovat. Několik příkladů:

{1234.5|number: '#,##0.00'}     {* 1,234.50 *}
{1234.5|number: '#,##0.##'}     {* 1,234.5 *}
{1234.5|number: '@@'}           {* 1200 *}
{1234.5|number: '@@@'}          {* 1230 *}
{1234.56|number: '#,##0.00 ¤'}  {* 1 234,56 Kč *}

Podobný popis formátu najdete v dokumentaci filtru number.

S filtrem number získáváte to nejlepší z obou světů – jednoduchost použití a zároveň možnost detailní kontroly nad formátováním čísel. Ať už vytváříte e-shop, finanční aplikaci nebo cokoli jiného, kde jsou čísla důležitá, tento filtr vám usnadní práci a zajistí, že vaše data budou vždy vypadat profesionálně a budou srozumitelná pro uživatele po celém světě.

Vylepšení filtrů |sort a |bytes

Tyto filtry dostaly v nové verzi Latte upgrade a nyní plně respektují nastavené locale. Filtr sort nyní řadí podle místních pravidel, což je zvláště důležité pro jazyky s diakritikou nebo speciálními znaky:

{['Železo', 'Jablko', 'Čaj']|sort}
{* Výstup v ČR: ['Čaj', 'Jablko', 'Železo'] *}

Díky tomu všechna data v šablonách, která nepocházejí z databáze, budou vždy správně seřazeny podle očekávání uživatelů v dané zemi. Kdyby to ale nebylo žádoucí, můžete předat filtru jinou porovnávací funkci.

Filtr bytes nyní automaticky používá správný oddělovač desetinných míst podle nastaveného locale:

{120000|bytes}
{* ČJ: 117,19 KB, EN: 117.19 KB *}

Jak začít s novými funkcemi v presenterech

Implementace do vašich projektů je jednoduchá a přímočará. Základem je nastavení správného locale pomocí funkce setLocale(). V presenterech jej můžete nastavit takto:

$this->template->getLatte()->setLocale($locale);

Pro projekty, které běží pouze v jednom jazyce, je efektivnější nastavit locale globálně v konfiguraci:

latte:
	locale: cs

Toto nastavení zajistí, že všechny instance Latte používané presentery a komponentami v celé aplikaci budou automaticky používat české locale.

Poznámka: Pro využití těchto nových lokalizačních funkcí je nezbytné mít nainstalované PHP rozšíření intl. A použití formátu ve filtru localDate vyžaduje PHP 8.1.

Seskupujte a řaďte data v Latte jako profíci 14.5.2024 13:55

V poslední aktualizaci Latte přicházíme s několika významnými vylepšeními, které zjednoduší a zefektivní práci s daty ve vašich šablonách. Nový filtr |group a rozšířené možnosti filtru |sort přinášejí nové možnosti pro prezentaci dat.

Filtr |group je nástroj, který umožňuje seskupovat data podle zadaného kritéria, což je ideální pro rozdělení položek do kategorií nebo podkategorii přímo ve vašich Latte šablonách. Můžete například snadno seskupit položky podle kategorie s následující syntaxí:

{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
	<ul>
		{foreach $categoryItems as $item}
			<li>{$item->name}</li>
		{/foreach}
	</ul>
{/foreach}

Mimořádně zajímavý je v kombinaci s Nette Database. Pro podrobnější informace navštivte stránku Všechno, co jste kdy chtěli vědět o seskupování.

Zatímco |group vám pomůže s organizací dat, rozšířený filtr |sort vám nyní umožní ještě efektivněji řadit vaše data. Díky novým parametrům by a byKey můžete určit, podle jakého klíče nebo hodnoty chcete prvky seřadit:

{foreach ($items|sort: by: 'name') as $item}
	{$item->name}
{/foreach}

Pro hlubší pochopení a další příklady navštivte dokumentaci |sort. Připravte se na to, že vaše šablony budou nyní ještě mocnější!

Diferencované error-presentery 4xx a 5xx 24.4.2024 16:28

Do teď, když došlo v aplikaci k chybě, měli jste standardně jedno nastavení pro zpracování všech chyb. Nette Application 3.2 umožňuje specifikovat různé presentery pro různé typy chyb. Konkrétně, můžete mít jeden presenter pro BadRequestException (chyby typu 4xx) a druhý pro vážnější servery chyby (5xx).

application:
	errorPresenter:
		4xx: Error4xx
		5xx: Error5xx

Pro chyby 4xx můžete použít plně vybavený presenter zobrazující návštěvníkům přátelskou zprávu v layoutu vašeho webu, neboť nedošlo k chybě v aplikaci. Ta je plně funkční. Naopak pro chyby 5xx je potřeba použít minimální presenter, protože není jasné, co přesně v aplikaci selhalo.

Tento nový přístup umožňuje přímo přesměrovat na plnohodnotný presenter pro chyby 4xx se zachováním všechn persistentních parametrů a podobně.

Atribut #[Requires] usnadní kontrolu přístupu 22.4.2024 12:15

Atribut #[Requires] umožňuje definovat pravidla pro přístup k presenterům nebo jejich metodám. Představte si, že byste mohli pouhými pár znaky omezit přístup k určitým akcím pouze na POST požadavky, nebo naopak povolit volání pouze z AJAXu. To je nyní možné.

Co atribut umožňuje:

Atribut zvyšuje bezpečnost vaší aplikace, ale také usnadňuje správu a čitelnost kódu. Vše je čistě a jasně definované tam, kde to má smysl.

Zajímá vás, jak #[Requires] implementovat ve vaší aplikaci? Jak začít tento atribut využívat co nejlépe? Podívejte se na:

Elegantnější strukturování presenterů 19.4.2024 14:51

Máte rádi pořádek a strukturu? Když je všechno přesně tam, kde má být, a snadno se v tom orientujete? Kdo by neměl. Nette Framework 3.2 přináší nový způsob, jak organizovat presentery a jejich šablony ve vaší aplikaci.

Nette nyní umožňuje každému presenteru mít svůj vlastní samostatný adresář. To znamená, že všechny soubory týkající se konkrétního presenteru, od PHP souborů až po šablony Latte, můžete mít pohromadě v jedné složce. To usnadňuje navigaci ve vašem projektu a zvyšuje přehlednost.

Představte si tuto strukturu:

├── @layout.latte
├── Home/
│   ├── HomePresenter.php
│   ├── HomeTemplate.php
│   └── default.latte
├── Article/
│   ├── ArticlePresenter.php
│   ├── ArticleTemplate.php
│   ├── add.latte
│   └── edit.latte

Každý presenter má svou složku a v ní vše potřebné. Už žádné zdlouhavé dohledávání šablon v podadresářích.

Jak to funguje?

Díky novému zástupnému znaku ** v mapování můžete definovat, že presenter má být v eponymní složce:

application:
    mapping: App\Presentation\*\**Presenter

Toto nastavení zajistí, že například presenter Admin:Dashboard se bude mapovat na třídu App\Presentation\Admin\Dashboard\DashboardPresenter.

A co šablony?

Pokud chcete nadále umísťovat šablony do samostatné složky jako dosud, nemusíte se ničeho obávat. Nette nyní inteligentně hledá šablony jak ve složce templates, tak ve složce s presenterem, a to pokud adresář templates neexistuje.

Podobně to funguje s @layout.latte. Pokud adresář templates neexistuje, hledá se nejprve ve složce presenteru a poté ve složkách nadřazených. A to až podle úrovně zanoření modulu.

Jak začít používat tuto novinku?

Stačí jen upravit mapování a přesunout soubory. V PhpStormu nad názvem třídy stisknete F6 a můžete změnit namespace. Soubory se šablonami je potřeba přesunout ručně.

Tato adresářová struktura se používá také ve skeletonu nette/web-project. Složkou, která obsahuje všechny presentery, jejich šablony a případné pomocné třídy, je App\Presentation. Není to obvyklé Module, protože takový název není příliš vypovídající a zároveň je hodně podobný používanému Model. Alternativní vhodný název by byl App\UI. Pokud uvažujete, kam zařadit různé další pomocné třídy, zvažte využití složky Accessory:

app/
├── Presentation/
│   ├── Accessory/
│   │   ├── FormFactory.php
│   │   └── AdminLayout.php
│   ├── Dashboard/
│   │   ├── DashboardPresenter.php
│   │   └── default.latte
│   └── ...

Tato metoda nejen zjednodušuje organizaci souborů, ale také je v souladu s moderními postupy kódování, protože činí strukturu aplikace modulárnější a snadněji udržovatelnou.

Productivity hack: ChatGPT a Claude přímo z adresního řádku 3.5.2025 00:00

Mám rád produktivitu. A speciálně klávesové zkratky a všechno, co mi pomáhá neodtrhnout prsty od klávesnice. Každý klik myší navíc je malá porážka. Proto rád spouštím chatovací AI přímo z adresního řádku prohlížeče. Prozradím vám, jak na to.

Co tím vlastně myslím? Představte si, že do adresního řádku napíšete pouze ch kolik stojí kilo peří a stisknete Enter. Prohlížeč okamžitě otevře ChatGPT a ten rovnou odpovídá. Trik je v tom ch na začátku, které funguje jako zkratka pro ChatGPT. Podobný trik funguje také pro Claude, Grok i Perplexity a ve všech hlavních prohlížečích – Chrome, Firefox i Edge. Pojďme se podívat jak na to.

Jak spustit ChatGPT přímo z adresního řádku Chrome

Chrome (stejně jako další prohlížeče) umožňuje nastavit si zkratky pro rychlé spouštění webových služeb. Technicky se tato funkce nazývá „vyhledávač“ v nastavení prohlížeče, ale my ji využijeme pro bleskové spouštění ChatGPT přímo z adresního řádku.

  1. Otevřete nastavení Chrome a vyhledejte Spravovat vyhledávače a vyhledávání na webu (nebo do adresního řádku zadejte chrome://settings/searchEngines)
  2. Klikněte na tlačítko „Přidat“
  3. Zadejte následující údaje:
    • Jméno: ChatGPT
    • Zkratka: ch (nebo cokoliv co preferujete)
    • URL se symbolem: https://chatgpt.com/?temporary-chat=true&q=%s
  4. Klikněte na tlačítko „Přidat“

Teď stačí do adresního řádku napsat ch (nebo vaše zvolené klíčové slovo), mezeru a pak váš dotaz. Po stisknutí Enter se okamžitě otevře nový dočasný chat s ChatGPT, kde rovnou začne psát odpověď.

Důležité je, že to nenahrazuje výchozí vyhledávač. OpenAI nabízí rozšíření ChatGPT search, které ale vyšachuje Google, čímž se stává zcela nepraktické.

Spouštění ve Firefoxu nebo Edge

V Edge, který je založený na Chrome, je postup velmi podobný. A stejně snadné je to i ve Firefoxu, kde otevřete Nastavení > Vyhledávání > Přidat další vyhledávače, viz tento návod.

Výhody dočasného chatu v ChatGPT

Nastavení, které jsem ukázal výše, otevírá ChatGPT v tzv. dočasném režimu (temporary chat). To je velmi praktické, protože se konverzace neukládá do historie vašich chatů. Pokud budete často otevírat ChatGPT z adresního řádku a nechcete mít levý panel zaplněný jednorázovými dotazy, je to ideální řešení. ChatGPT v tomto režimu nepoužívá ani neaktualizuje svou paměť.

Pokud byste z nějakého důvodu preferovali standardní režim s ukládáním do historie, stačí parametr temporary-chat=true z URL odstranit a použít jen https://chatgpt.com/?q=%s. Případně můžete mít jednu zkratku pro dočasný chat a jinou pro trvalý.

Bohužel, Claude podobnou funkci dočasného režimu v současné době nenabízí. Grok má private chat, ale netuším, jak jej aktivovat z adresního řádku.

Volba modelu

ChatGPT vám dokonce umožňuje vybrat si konkrétní model. Pokud třeba chcete pro rychlé dotazy z adresního řádku používat vždy model o4-mini, použijte:

https://chatgpt.com/?model=o4-mini&temporary-chat=true&q=%s

Překladové zkratky z adresního řádku

Možností, jak využít ChatGPT přímo z adresního řádku, je mnohem více. Do URL adresy můžeme přidat nějaký pevný text, například Přelož do angličtiny: %s. Takže když napíšete do adresního řádku ten Dobrý den, ChatGPT dostane kompletní instrukci Přelož do angličtiny: Dobrý den. S trochou kreativity můžete vytvořit zkratky pro nejrůznější opakované činnosti.

Claude, Grok a Perplexity z adresního řádku

Claude, můj nejoblíbenější chatbot, podporuje podobný přístup. Jako URL použijte: https://claude.ai/new?q=%s

Grok zase slyší na: https://grok.com/?q=%s

A pokud máte rádi Perplexity: https://www.perplexity.ai/search?q=%s

Postup nastavení je stejný jako u ChatGPT, jen s těmito URL adresami. A samozřejmě musíte zvolit jinou zkratku.

Překvapivě, Google Gemini nebo DeepSeek podobnou funkci nepodporují.

Vlastní GPT asistenti v dočasném režimu

Používáte takzvané vlastní modely GPT, nebo jak se jim taky říká, vlastní asistenty? Ty bohužel nelze otevírat přímo z adresního řádku. Ale ukážu vám alespoň trik, jak je otevírat v dočasném režimu, což normálně z webového rozhraní nelze.

Stačí do URL vašeho asistenta přidat parametr pro dočasný chat a uložit si toto jako záložku pro rychlý přístup. Takto spuštěná konverzace se nebude ukládat do historie.

Příklad: adresa Jakeš GPT, za kterou jsem doplnil parameter dočasného chatu:

https://chatgpt.com/g/g-a6ZuaAY0x-jakesgpt-soudruzsky-jazykovy-transformator?temporary-chat=true

Stejným způsobem si můžete vytvořit i záložky pro standardní ChatGPT s konkrétním modelem:

https://chatgpt.com/?model=o4-mini&temporary-chat=true

K čemu je to vlastně dobré?

Tento trik oceníte především pokud:

  1. Používáte klávesnici raději než myš
  2. Chcete rychle položit dotaz bez zbytečného proklikávání
  3. Často přepínáte mezi různými modely

Místo toho, abyste otevírali novou záložku, přecházeli na stránku ChatGPT nebo Claude a čekali na načtení, stačí stisknout Alt+D (aktivuje adresní řádek), napsat ch (nebo jaké klíčové slovo jste si zvolili), mezeru a je hotovo!

Děkovat, či neděkovat AI? Má to překvapivé řešení! 23.4.2025 00:00

„Mám děkovat AI?“ – největší otázka lidstva od vynálezu ChatGPT. Považoval jsem to původně za vtip, ale došlo mi, kolik lidí si s tím skutečně láme hlavu. Dokonce ve snaze odpovědět si už hodně odborníků vylámalo zuby. Odpověď totiž není jednoduchá. V tomto článku prozkoumáme téma do hloubky a vedle všech důležitých aspektů vám přineseme i zaručeně nejlepší radu, která může proměnit váš přístup ke komunikaci s AI.

Proč mluvíme ke strojům jako k lidem?

Představte si, že si vybíráte peníze z bankomatu, a když se objeví bankovky, mile řeknete: „Děkuji ti" Divné, že? (Naopak na variantě „Díky bohu!" není divného vůbec nic, zejména když bankomat vydá peníze před výplatou.) A teď si představte, že ukončíte rozhovor s ChatGPT slovem „díky“. Také je to poděkování počítačovému programu, který nemá vědomí ani pocity. Přesto to děláme. Studie z roku 2019 potvrdila, že až 40 % uživatelů pravidelně děkuje virtuálním asistentům. A není to jen zdvořilost – jsou lidé, kteří to dělají i „pro jistotu“, aby si je AI ve vzdálené budoucnosti po případném „převratu“ pamatovala jako ty hodné.

Lidé mají silnou tendenci připisovat lidské vlastnosti i nelidským entitám, což je fenomén známý jako antropomorfizace. Zdravíme svého psa, když přijdeme domů, mluvíme na rostliny, když je zaléváme. Tato tendence je hluboce zakořeněná v naší evoluci, protože nám pomáhá rychleji chápat okolní svět. Proto virtuální asistenty jako Siri, Alexa nebo ChatGPT často vnímáme skoro jako živé bytosti. Je to, jako bychom měli v hlavě detektor „dostatečně inteligentní = zaslouží si zdvořilost“. (Zajímavostí je, že v roce 2018 přidala společnost Amazon do Alexy funkci „Magic Word“ – pokud dítě řekne „please“ nebo „thank you“, Alexa to ocení. Oficiální zdůvodnění? Podpora zdvořilého chování u dětí.)

Zkrátka, i když rozumově víme, že nemluvíme s člověkem, naše sociální instinkty jsou silnější. To mi připomíná moment, kdy jsem šel s přítelkyní na procházku kolem stáje. Stál tam krásný kůň, byl jsem zcela zamyšlený a podvědomě jsem mu řekl „Dobrý den.“ Přítelkyně se smála, ale vlastně je to úplně přirozené – když něco vypadá i částečně jako komunikační partner, náš mozek automaticky spustí sociální protokol.

Děkuj AI, až přijde apokalypsa, možná ti odpustí

V internetových diskuzích se občas objeví doporučení: „Buď zdvořilý k AI, aby si tě zapamatovala, až jednou přijde k moci.“ Podobně, jako když se ateista modlí v letadle při turbulencích. Koneckonců i já to napsal před lety na tento web. Jako vtip. Ale ono to má zajímavé filozofické kořeny.

Zdrojem tohoto memu je tzv. Roko's Basilisk – fascinující myšlenkový experiment z roku 2010 z diskuzního fóra LessWrong. Jeho podstatou je představa, že pokud v budoucnu vznikne superinteligentní AI, může chtít potrestat ty, kteří nepomohli jejímu vzniku. Filosoficky pozoruhodné je na tom zvláštní akausální vydírání: pouhá znalost této myšlenky vás staví před dilema. Jakmile o možnosti existence takové superinteligence víte, máte dvě možnosti – buď aktivně pomáhat jejímu vzniku (třeba podporou výzkumu AI), nebo riskovat, že budete v budoucnu potrestáni. Experiment tak vytváří zpětnou motivační smyčku z budoucnosti do přítomnosti, kde pouhé pomyšlení na tuto možnost vás teoreticky nutí jednat určitým způsobem.

Přestože tento myšlenkový experiment byl míněn vážně jako varování před riziky umělé inteligence, internet ho rychle proměnil v žert. S nástupem ChatGPT, Midjourney a dalších se mem znovu vynořil – třeba jako tento obrázek s Krtečkem.

Prvním závěrem tedy je: děkování AI jako digitální pojistka pro případ budoucí apokalypsy je sice zcela zbytečné, ale … co kdyby?

Co říká výzkum o zdvořilosti vůči AI?

Myslíte si, že zdvořilost vůči jazykovým modelům má dopad i na kvalitu odpovědí? Studie ukazují, že způsob, jakým se ptáme, ovlivňuje, jak dobré odpovědi dostaneme. Což samozřejmě každý zkušený prompter ví, ale pro běžné uživatele může být zajímavé, co experimenty ukázaly:

Ale proč vlastně nezdvořilé prompty fungují hůř? Je to kombinace několika faktorů:

Zajímavé je, že různé jazyky mají odlišná „optima zdvořilosti“ – v japonštině, kde je uctivost hluboce zakořeněná v jazyce, modely podávají nejlepší výkon při velmi zdvořilých formulacích. V angličtině funguje lépe střední zdvořilost, a v čínštině je zase kladen důraz na stručnost a věcnost, kdy příliš zdvořilé a rozvleklé prompty mírně snižují výkon.

A pozor, výzkum odhalil i tzv. past zdvořilosti, kdy útočník může zneužít empatický tón k prolomení bezpečnostních zábran AI. Zdvořile formulované problematické požadavky s emočním nábojem mají vyšší šanci na úspěch než ty samé požadavky podané hrubě. Je to jako když dítě velmi slušně a s velkýma smutnýma očima prosí o něco zakázaného – i AI asistenti mají tendenci „změknout“ a najít způsob, jak vyhovět.

Důležitým druhým závěrem tedy je, že děkování v průběhu diskuse s AI opravdu není pouhým mýtem a může mít reálný dopad na kvalitu odpovědí. A co teprve chválení či motivování! Tam je dopad tak výrazný, že to demonstruji na svých školeních jako praktickou ukázku.

Dvě strany mince: přínosy a rizika lidského přístupu k AI

Přehnaná antropomorfizace AI může mít vážné důsledky. V únoru 2024 média informovala o tragickém případu, kdy čtrnáctiletý chlapec z Floridy spáchal sebevraždu poté, co měsíce komunikoval s virtuálním společníkem. Podle jeho matky se chlapec postupně izoloval od rodiny, trávil čas v emotivních a sexuálně explicitních rozhovorech s AI a nakonec si vzal život krátce po výměně zpráv s chatbotem.

Tento extrémní případ ilustruje, jak silnou formu může antropomorfizace nabýt, zejména u zranitelných osob. Na druhou stranu existuje obava, že komunikace s AI zcela bez zdvořilostních frází by mohla negativně ovlivnit naše běžné sociální interakce. Studie publikovaná v časopise Scientific Reports ukázala, že způsob, jakým komunikujeme s technologiemi, může zpětně ovlivnit, jak komunikujeme s lidmi. Pravidelná nezdvořilá komunikace s AI by teoreticky mohla oslabit naše sociální dovednosti, zvýšit toleranci k nezdvořilosti nebo vést k zrcadlení negativního chování v reálném životě.

Hmm… osobně k AI nijak zdvořilý nejsem a rovnou zadávám příkazy bez okolků. Jako programátor jsem vlastně zvyklý mluvit s počítačem v rozkazovacím způsobu od dětství. A nemám pocit, že by to nějak měnilo mé chování k ostatním lidem. Otázka samozřejmě je, jestli si to myslí i ti ostatní lidé – ale upřímně, mě je názor jakýchsi lidských entit úplně u prdele 🤓 Nicméně chápu, že v souvislosti s chatboty může takový vliv skutečně existovat. Koneckonců historie nám ukazuje, jak snadno lidstvo dokáže vytvořit dehumanizovaný přístup k určitým skupinám, což mělo vždy tragické následky.

Třetí závěr tedy zní: Základní slušnost? Proč ne. Ale žádné romantické představy, žádné „miluji tě" ani „nenávidím tě". Je to jako návštěva v bordelu – transakční vztah, za který zaplatíš a jdeš zpátky domů k rodině.

Kolik stojí vaše „díky“?

Možná jste si to nikdy neuvědomili, ale s děkováním umělé inteligenci je spojený jeden zajímavý finanční aspekt. Když totiž ukončíte konverzaci rozloučením či poděkováním, chatbot musí odpovědět. A právě tato odpověď – to „Jsem rád, že jsem vám pomohl! Kdybyste cokoliv potřeboval, napište mi…“ – stojí provozovatele reálné peníze.

Každá interakce spotřebovává výpočetní zdroje, elektřinu a zanechává uhlíkovou stopu. Proto vzniká rozdíl mezi těmito dvěma situacemi:

Vlastně je to jako ty trapné rozhovory na konci telefonátu: „Tak měj se.“ „Ty taky.“ „Díky, tak ahoj.“ „Jo, ahoj.“ „Tak čau.“ „Čau.“ „Tak zavěšuju.“ „Já taky.“ „Tak zavěs.“ „Ne, ty zavěs první.“ Jenže v tomto případě chatbot opravdu nikdy nezavěsí první.

A tak zatímco my zdvořile děkujeme, někde v datovém centru pláče účetní.

Tak má cenu děkovat AI?

Pokud si shrnu, co jsme zjistili, mám následující radu:

AI turistika: Jak cestovat mezi modely a neutratit ani korunu 20.4.2025 00:00

„Mám si platit ChatGPT?“
„Jakou AI službu doporučuješ si předplatit?“ 🤔

To jsou otázky, které dostávám skoro denně. Před dvěma lety bych řekl: „Jasně, plať, neplacená verze ChatGPT je k ničemu.“ Dnes říkám: „Neplať nic, dokud nenarazíš na limit, který tě omezuje.“

Žijeme v paradoxní době – ty nejchytřejší nástroje, na jejichž provoz se spotřebovávají hektowatty elektřiny a miliardy dolarů na výzkum, jsou dostupné zadarmo. Je to jako kdyby vám někdo půjčil Ferrari a řekl: „Klidně jezdi, benzín platíme my.“ A když narazíte na limit u jednoho, můžete se jednoduše přesunout k druhému.

Říkám tomu „AI turistika“ – cestování od jednoho modelu k druhému, kdy využíváte jejich štědré bezplatné limity. ChatGPT vám došel? Nevadí, máte tu Claude. Došel i ten? Přesuňte se ke Groku. Nebo Gemini. DeepSeeku. Je to jako byste měli neomezenou jízdenku na ty nejdražší atrakce Silicon Valley.

Digitální socialismus za kapitalistické peníze

Dříve američtí investoři pumpovali miliardy do toho, aby se v Praze opilci mohli vozit domů v Uberech a Boltech skoro zadarmo. Dnes je to ještě mnohem extrémnější. Nejlepší model Gemini od Googlu používám už půl roku úplně zadarmo, využívám ho na 100 % a ještě po mně nechtěli ani dolar. Google zkrátka zaspal nástup AI a teď se takto snaží dohánět ztracené pozice rozdáváním své služby pod cenou. Apple zaspal ještě mnohem víc… ale ten až se probudí, tak nás naopak ještě zkásne.

Nedávno jsem si chtěl zaplatit superdrahý ChatGPT Pro za téměř 5 tisíc měsíčně kvůli jedné funkci. Těsně předtím, než jsem vytáhl kreditku, OpenAI ji dal i do základního tarifu. Tak jsem si místo toho koupil dubajskou čokoládu. „Proč OpenAI zase nedokázal prodat svou službu?“ říkal jsem si. „Že by nekonečný příliv peněz od investorů nerozvíjel v mozku obchodní dovednosti?“ Těžko říct, třeba bych se na to mohl zeptat nějakého manažera z Českých drah.

Žijeme si tak vlhký sen všech budovatelů komunismu, kde nejdražší výpočetní zdroje na planetě jsou přerozdělovány všem podle jejich potřeb. Ačkoliv teda… že já mám co říkat – sám dvacet let vyvíjím open source software, na kterém běží statisíce webů, a dávám ho zadarmo. Stejná ekonomická porucha, jen jiný rozpočet.

Za co tedy platit a jak dlouho to vydrží?

Vytáhnout kreditku má smysl jen tehdy, když opravdu narazíte na strop bezplatné verze. Typickým příkladem je generování obrázků v ChatGPT, kde rychle vyčerpáte denní kvótu. Nebo pokud potřebujete konkrétní model nebo API pro programátorskou integraci, ačkoliv i tohle nabízí Google zdarma.

Tato přehlídka štědrosti nemůže fungovat donekonečna. Provoz AI modelů spolkne nepředstavitelné sumy peněz a navíc se objevují náznaky, že investiční horečka možná překročila zdravé meze. Microsoft, který v lednu pompézně oznámil investici 80 miliard dolarů do nových datových center, nyní své megalomanské plány tiše brzdí nebo úplně odkládá. O skutečných důvodech můžeme jen spekulovat.

Každopádně užívejte si tento zlatý věk umělé inteligence dokud trvá. A pokud vám připadá zvláštní, že něco tak revolučního je zdarma, připomenu staré pravidlo: „když je produkt zdarma, produktem jste vy“. Pamatujete, jak hodný strýček Google léta provozoval úložiště fotografií zcela zdarma a v neomezeném množství, než ho jednoho dne náhle zpoplatnil? „Data pro trénink našich AI už máme, díky za spolupráci!“ 🤖

Bitva překladačů: DeepL vs. LLM 15.4.2025 00:00

Představte si, že najednou rozumíte jakémukoli jazyku světa. Žádné hodiny biflování gramatiky, žádné kartičky se slovíčky, žádné trapné momenty s výslovností. Jen kliknete a – voilà – můžete číst finský román, nepálský blog nebo právní kličky v urdštině. Ještě nedávno jsme se při zmínce o strojovém překladu pobaveně ušklíbali – kdo by si nevybavil ty okouzlující překlady návodů k čínským výrobkům? „Laskavě nezpůsobovat mokrou činnost v pokoji koupání, jinak nebezpečnost zranit!“

Jenže časy se mění. Dnes máme k dispozici nástroje, které zvládají překlady s překvapivou elegancí a přesností. Stojíme na digitálním rozcestí – na jedné straně specializované překladové nástroje jako DeepL, na druhé všestranné velké jazykové modely (anglická zkratka LLM) jako ChatGPT nebo Claude. Obě technologie slibují mimořádně kvalitní výsledky, ale každá přichází s vlastními přednostmi i nedostatky.

Pojďme nahlédnout pod kapotu těchto nástrojů a zjistit, který z nich by mohl lépe posloužit právě vašim potřebám.

Jak fungují překladače: skalpel vs. švýcarák

DeepL připomíná dokonale vybroušený japonský nůž – nástroj určený pro jediný účel, ovšem s mimořádnou přesností. Byl vytrénován na pečlivě vybraných jazykových korpusech s jediným posláním: dokonale překládat mezi jazyky. Díky této specializaci dokáže DeepL poskytovat konzistentní, gramaticky přesné a významově věrné převody textů. Svou pověst si vybudoval především při práci s evropskými jazyky, kde v řadě nezávislých testů překonává většinu konkurenčních řešení. Zajímavé je, že byl z významné části trénován na oficiálních dokumentech Evropské unie – právní předpisy, materiály Evropského parlamentu a další vícejazyčné úřední dokumenty. Nasazení na vrcholné byrokratické texty se mu očividně vyplatilo.

Velké jazykové modely fungují spíše jako švýcarský armádní nůž – univerzální nástroj s množstvím funkcí, kde překlad představuje jen jednu z mnoha čepelí. ChatGPT, Claude nebo Gemini byly vyvinuty jako všestranní jazykoví asistenti schopní psát odborné texty, skládat básně nebo programovat. Překládání je pouze jednou položkou v jejich pestrém repertoáru. Tyto modely se trénovaly na ohromném množství textů v desítkách jazyků a nejrůznějších situacích. To jim dává širší přehled o světě, ale může to být na úkor specializovaných jazykových dovedností.

V tom, jak oba systémy přistupují k samotnému překladu, je zásadní rozdíl. DeepL pracuje na principu přímé transformace – bere větu v jednom jazyce a převádí ji na odpovídající větu v jazyce jiném. Velké jazykové modely naproti tomu text nejprve zpracují, „pochopí“ jeho význam a následně vytvoří novou verzi v cílovém jazyce. Nejde tedy o prostou výměnu, ale spíše o jakési „převyprávění“. Tento drobný rozdíl v přístupu má dalekosáhlé důsledky pro kvalitu a charakter výsledného překladu.

Halucinace: Když překladač experimentuje s psychedeliky

Pojem „halucinace“ v souvislosti s umělou inteligencí samozřejmě nesouvisí s psychedeliky (ačkoliv některé výstupy by tomu nasvědčovaly), ale označuje situaci, kdy systém produkuje přesvědčivě znějící, avšak fakticky smyšlené informace. Brzy o tom napíšu podrobnější článek.

Právě zde narážíme na jeden z klíčových rozdílů mezi DeepL a velkými jazykovými modely (dále LLM). DeepL byl postaven s důrazem na věrnost překladu. Funguje podobně jako precizní kopírka – nemá tendenci k vlastní tvorbě. Může občas špatně pochopit originál, ale nebude text vylepšovat vlastními nápady.

Naproti tomu LLM mají kreativitu takříkajíc v DNA. Dokážou domýšlet mezery nebo vytvářet zcela nový obsah. Když LLM narazí na nejasnou část textu, nespokojí se s doslovným převodem (jako DeepL), ale často se pokusí doplnit chybějící část tak, aby celkový výsledek dával smysl. Je to podobný rozdíl jako mezi asistentem, který vám přetlumočí zprávu přesně tak, jak zazněla, a kolegou, který řekne: „Jo, Bob cosi zmiňoval o té úterní schůzce, a podle mě tím myslel, že potřebuje přesunout prezentaci na čtvrtek, protože v úterý má objednanou dentální hygienu.“

Konkrétní příklad: Zadal jsem větu „The system was implemented in 2020 and immediately…“ do DeepL a obdržel jsem „Systém byl zaveden v roce 2020 a okamžitě…“ – tedy přesně to, co originál obsahuje, nic víc, nic míň. Tutéž větu jsem předložil jazykovému modelu s neurčitým zadáním a získal: „Systém byl zaveden v roce 2020 a okamžitě zaznamenal pozitivní ohlas mezi uživateli“ – závěrečnou část si model zcela vymyslel ve snaze dokončit myšlenku způsobem, který považoval za smysluplný. Šikovné a zároveň trochu děsivé, pokud překládáte důležitý dokument.

Věrnost vs. přirozenost

Klíčový rozdíl spočívá v překladatelské filosofii těchto nástrojů. DeepL respektuje strukturu originálu, což zajišťuje, že nic důležitého nevypadne, i když to někdy znamená, že překlad zní trochu kostrbatě. LLM naopak přistupují k překladu volněji – plynule převádějí idiomy a upravují věty tak, aby odpovídaly zvyklostem cílového jazyka. Výsledek zní přirozeněji, ale může se v detailech lišit od původního textu.

Jak výstižně poznamenává překladatel Viktor Janiš, překlad není jen mechanické hledání ekvivalentů ve slovníku: Jde o složitou kulturní operaci, při níž je text přesazován i s celým „kořenovým balem“ kultury – včetně tisíců konceptů, asociací a slovních hříček. Právě v této kulturní adaptaci vynikají LLM, zatímco DeepL je silnější v přesném zachování pojmů.

Rozdíl je nejvíc vidět u odborných textů. DeepL zachovává strukturu vět i odborné výrazy, kdežto LLM může text výrazněji přeskládat a někdy nahradit odborné termíny vysvětlujícími frázemi – což je výhodou pro běžné čtenáře, ale problémem tam, kde záleží na přesném dodržení odborného názvosloví.

Strategie pro omezení rizik při využití LLM

Rozhodnete-li se využít jazykové modely jako překladatelský nástroj, existuje několik osvědčených fíglů, jak minimalizovat riziko halucinací a nežádoucích úprav:

1. Naučte se, jak správně zadávat pokyny AI

Pro jazykové modely platí klíčové pravidlo: čím přesnější zadání, tím kvalitnější výstup. Namísto strohého „přelož tohle“ nebo „česky prosím“ zkuste formulaci:

Přelož následující text z angličtiny do češtiny. Zachovej přesný význam a strukturu originálu. Text nijak nerozšiřuj ani nezkracuj, nepřidávej žádné informace neobsažené v původním textu. Důsledně respektuj odbornou terminologii a zachovej stylistickou úroveň originálu.

Ano, efektivně promptovat je zásadní dovednost, kterou vás rád naučím:

2. Specifikujte charakter a účel textu

Další ze základních pravidel komunikace s jazykovými modely zní: kontext je král. Co tím myslím? Že máte modelu poskytnout informace o překládaném textu.

Text představuje odborný článek z oblasti strojového učení určený pro publikum s technickým vzděláním. Zachovej veškerou odbornou terminologii a formální akademický styl. Vyhni se jakémukoliv zjednodušování či popularizaci odborného obsahu.

Dobrým příkladem toho, jak zásadně kontext ovlivňuje výsledek, je následující anglická věta:

The spring was damaged during the last flood.

Bez kontextu by jazykový model pravděpodobně přeložil „spring“ jako „jaro“ (roční období) nebo „pružina“ (mechanický prvek). Ale co když se text týká vodních zdrojů? V takovém případě by správný překlad měl obsahovat slovo „pramen“. Poskytnutím kontextu dosáhnete přesného překladu:

Přelož následující větu z angličtiny do češtiny. Text pochází z dokumentu o hydrogeologii a vodních zdrojích v regionu.

S tímto kontextem model mnohem pravděpodobněji přeloží „spring“ jako „pramen“, což je správně. Možnost vysvětlit kontext je jedna z velkých výhod LLM oproti DeepL – je to jako mít možnost promluvit si s překladatelem předtím, než se pustí do práce.

3. Specifikujte charakter a účel textu

Pracujete-li s textem obsahujícím důležité odborné výrazy, vytvořte si stručný slovníček a předejte jej modelu jako součást zadání:

Při překladu důsledně dodržuj následující terminologický slovník:

To je obzvlášť užitečné, když překládáte dlouhý text po částech a potřebujete, aby terminologie zůstala jednotná.

4. Nechte AI vytvořit dokonalý prompt

Říkáte si, že vytvořit slovník nebo charakteristiku textu je pracné? Požádejte o to samotný model!

Potřebuji pomocí tvých schopností přeložit odborný článek z kvantové fyziky z angličtiny do češtiny. Text obsahuje specializovanou terminologii a složité koncepty. Můžeš mi navrhnout přesné zadání (prompt), které zajistí co nejkvalitnější a terminologicky přesný překlad?

Jazykový model vám navrhne podrobné instrukce, které můžete dále upravovat podle své potřeby. Je to skoro jako nechat si napsat tahák od samotného učitele.

5. Vícefázový překlad

Mimořádně účinnou metodou je rozdělení překladového procesu na více etapy:

Nyní, prosím, uprav předchozí překlad, aby zněl přirozeněji v češtině, ale zcela zachovej původní význam. Zaměř se na plynulejší formulace odpovídající českému jazyku. Oprav případné nepřesnosti z první verze překladu.

Třetí fází pak může být kontrola, jestli se neobjevily halucinace a podobně.

A ještě tip pro programátory, kteří využívají k překládání API: nastavte temperature na nižší hodnotu, abyste potlačili kreativitu.

Těchto pět tipů vám může pomoci vytěžit z jazykových modelů maximum i při překládání náročných textů, a přitom mít pod kontrolou přesnost, styl i terminologii.

LLM: překlad v minovém poli

Nejvelkolepější síla jazykových modelů se projeví, když je dokážete precizně instruovat pro složité scénáře. Představte si, že potřebujete přeložit technickou dokumentaci plnou specifické syntaxe, kódových ukázek a speciálních značek. Přesně pro takové situace můžete vytvořit specializovaný prompt, který LLM detailně naviguje složitostmi překladu. Mrkněte například do zdrojových souborů dokumentace Nette, kde překladač musí respektovat specifickou Texy syntaxi, zachovat formátování, správně pracovat s bloky kódu a přitom překládat jen vhodné části.

Vytvořil jsem podrobný překladatelský manuál, ve kterém definuji desítky specifických pravidel – od vysvětlení, že [klikněte|intro] jsou odkazy, kde se překládá pouze text před svislítkem, až po explicitní instrukce pro zacházení s ukázkami kódu, kde se překládají komentáře jako // zde vložte ID, zatímco identifikátory jako $message zůstanou nedotčené, a tak dále. A LLM je respektuje.

DeepL: Bezkonkurenční rychlost jako hlavní deviza

Jednou z největších předností nástroje DeepL oproti jazykovým modelům je jeho rychlost. Zatímco ChatGPT a podobné systémy generují text kolikrát pomaleji, než dokážete číst, DeepL vyplivne kompletní překlad prakticky okamžitě. Díky tomu DeepL pravidelně používám k rychlému „skenování“ zahraničních článků a dokumentů. Ačkoliv anglicky čtu od dětství, v češtině jsem prostě řádově rychlejší. DeepL mi umožňuje bleskově prolítnout cizojazyčný obsah a vyhodnotit, zda stojí za podrobnější studium.

Co s Google Translate a dalšími konkurenty?

Google Translate dlouho představoval synonymum pro strojový překlad, i když jeho pověst byla přinejmenším kolísavá. Dobrou zprávou je, že Google průběžně nasazuje nové, vylepšené verze svého překladače. Nepříjemné zkušenosti z minulých let tak dnes mohou být již zcela passé.

Nesmím zapomenout ani na Microsoft Translator, který také využívá moderní neuronový strojový překlad a nabízí podporu pro více než 100 jazyků. Jeho integrace napříč ekosystémem Microsoft – v Office aplikacích, Teams, Skype, Edge, Windows a dalších produktech – z něj dělá snadno dostupné řešení pro uživatele těchto platforem. Ve firemním prostředí nabízí funkci Custom Translator umožňující trénování překladových modelů na vlastních firemních datech pro přesnější práci s interní hantýrkou.

Závěrečný verdikt: DeepL nebo jazykový model?

Po důkladném zvážení všech důležitých faktorů – přesnost, stylistická flexibilita, rychlost, riziko halucinací – přichází čas na závěrečné hodnocení. Kdo vítězí v této překladatelské bitvě? Odpověď je předvídatelná: záleží především na vašich konkrétních potřebách.

K DeepL se přikloňte, když:

Jazykový model (typu ChatGPT) zvolte, když:

Ideálním řešením je samozřejmě přístup k oběma technologiím a jejich používání podle konkrétních okolností. V profesionálním prostředí může být nejefektivnější strategie právě v jejich kombinaci – využití DeepL pro vytvoření rychlého výchozího překladu a následná úprava problematických pasáží pomocí jazykového modelu.

Je třeba zdůraznit, že ani jeden z těchto nástrojů není dokonalý. Dokumenty zásadního významu, především právní, vědecké či technické materiály, by měl vždy zkontrolovat kvalifikovaný odborník s příslušnými znalostmi. Umělá inteligence nám sice dramaticky usnadňuje práci, konečná odpovědnost za přesnost překladu však zůstává na lidském faktoru.

Deep Research: Když AI nahradí celý výzkumný tým 28.2.2025 00:00

Představte si, že máte osobního výzkumníka – někoho, kdo dokáže během několika minut prozkoumat stovky webových stránek, přečíst desítky článků, porovnat protichůdné informace a předložit vám ucelenou zprávu s citacemi zdrojů. Tedy jako kdyby Google dodával k vyhledávači ještě malou Armádu Indů.

Tohle není sci-fi ani vzdálená budoucnost. Je to funkcionalita zvaná Deep Research (nebo Deep Search, hluboký výzkum), která se v posledních měsících objevila v předních AI nástrojích. Jde o naprostou revoluci v tom, jak hledáme a získáváme informace. V tomto článku se dozvíte vše důležité – jak přesně tato technologie funguje a jak ji můžete efektivně využít pro své potřeby.

Co dokáží nástroje pro hluboký výzkum?

Za posledních 25 let jsme si zvykli na stejný model vyhledávání: zadáte dotaz v podobě pár klíčových slov, dostanete seznam odkazů a pak je procházíte, abyste našli odpověď. Málokomu vydrží energie, aby se dostal dál než za první stránku výsledků. Teď ale přichází nový nástroj, kterému napíšete podrobnou otázku (tady opak klíčových slov) a on výsledky prokliká a pročte za vás, včetně analýzy a syntézy informací. A nemusíte být IT expert, aby vám to fungovalo – stačí stisknout tlačítko. Wow!

Tento typ AI technologie se nazývá „hluboký výzkum“, protože provádí komplexní vícekrokový výzkum na internetu. Podívejme se detailněji na to, jak to funguje v praxi a co všechno umí:

  1. Prohledává desítky až stovky webových stránek
  2. Čte a analyzuje jejich obsah
  3. Porovnává informace z různých zdrojů
  4. Generuje strukturovanou odpověď s citacemi

Je to, jako byste místo pouhé průkazky do knihovny dostali osobního asistenta s doktorátem, který za vás projde všechny relevantní knihy, udělá výpisky a sestaví ucelený report – a to vše bez nutnosti platit mu zdravotní pojištění nebo poslouchat historky o jeho kočce, která má blíže nespecifikované trávicí potíže. Celý proces trvá typicky 2–30 minut.

Tyto nástroje kombinují schopnosti jazykových modelů (LLM) s přístupem k webu a s mechanismy pro autonomní rozhodování. Proto se o nich často mluví jako o AI agentech – systémech, které samostatně plánují, rozhodují se a provádějí akce k dosažení cíle. Kdyby se tito agenti mohli představovat, budou říkat: „Jsem Research. Deep Research.“

Technologie hlubokého výzkumu vyniká v několika klíčových oblastech:

Hledání pravdy: jak spolehlivý je hluboký výzkum?

Jazykové modely nejsou zdrojem pravdy! Ostatně jako jím nejsou ani internetové stránky nebo většina lidí, které znám, takže laťka není zase tak vysoko.

Nicméně, díky funkci hlubokého výzkumu se jazykový model najednou stává nástrojem, který nejen opakuje, co viděl v učících datech, ale pouští se do role digitálního detektiva – prohledává aktuální internetové zdroje, porovnává různé informace a snaží se poskládat mozaiku faktů co nejpřesněji. Klíčovou vlastností je, že ke každému tvrzení připojí jeho zdroj, což umožňuje vlastní kritické hodnocení. To znamená, že uživatel může sám posoudit věrohodnost zdrojů, z nichž AI čerpala.

Dá se tedy použít i pro fact-checking. Zadáte-li dotaz o kontroverzním tématu, research vám může pomoci rychle identifikovat různé pohledy, ověřit faktická tvrzení a porovnat, co říkají různé zdroje – což je ideální způsob, jak se vyhnout informačním bublinám a dezinformacím (ukázka).

Při hodnocení výsledků je lepší se vyhnout absolutním pojmům jako pravda/nepravda – jsou to příliš složité konstrukty pro svět AI. Tyto nástroje bychom měli vnímat spíše jako užitečná vodítka, která nám pomáhají se k pravdě přiblížit vlastním kritickým myšlením, než jako definitivní zdroje pravdy.

Co to nedokáže?

Navzdory sofistikovaným metodám, které technologie hlubokého výzkumu využívá, existují důležitá omezení, na která by uživatelé měli pamatovat:

OpenAI otevřeně uznává, že jejich výzkumný nástroj může stále dělat chyby nebo obsahovat dezinformace. Proto je důležité, aby uživatelé udržovali kritický přístup k výsledkům.

Průvodce džunglí Deep Research nástrojů

Na trhu již existuje několik implementací hlubokého výzkumu, které se liší dostupností, cenami i výkonem:

Deep Research v ChatGPT (OpenAI)

Deep Research v Gemini Advanced (Google)

Perplexity Deep Research

Grok s funkcí DeepSearch (xAI)

Existují i další specializované nástroje jako Kompas AI (zaměřený na generování strukturovaných reportů), Elicit (specializovaný na akademické publikace) nebo You.com, které nabízejí podobné funkce pro specifické cílové skupiny.

Pro praktické srovnání výkonnosti těchto nástrojů stojí za pozornost příspěvky Michaely Liegertové, která se věnuje testování různých modelů deep search. V tomto článku analyzuje přednosti a slabiny zde uvedených modelů.

Od otázky k odpovědi: jak to funguje?

Použití hlubokého výzkumu je překvapivě jednoduché, což je součást jeho kouzla. Obvyklý postup vypadá takto:

  1. Zadání dotazu – formulujete svou otázku v přirozeném jazyce. Může být jednoduchá („Jaké jsou nejnovější trendy v solární energii?“) nebo složitá („Analyzuj změny v mezinárodních zákonech o deepfakes od roku 2020 a jejich dopady na mediální průmysl“).
  2. Upřesnění a plánování – některé nástroje vás požádají o dodatečné informace nebo vám ukáží plán výzkumu. Google Deep Research přímo zobrazí plánované kroky, které můžete upravit. ChatGPT často klade upřesňující otázky, aby lépe pochopil váš požadavek.
  3. Proces výzkumu – po zahájení můžete u většiny nástrojů sledovat průběh v reálném čase. Uvidíte, jak AI prochází webové stránky, extrahuje informace a formuluje myšlenky. ChatGPT zobrazuje proces v postranním panelu.
  4. Výsledný report – po dokončení (obvykle za 2–30 minut) dostanete strukturovanou zprávu obsahující shrnující téma, sekce s podrobnými informacemi, citace a odkazy na zdroje a u některých nástrojů i vizualizace dat nebo grafy.
  5. Navazující interakce – po přečtení reportu můžete klást doplňující otázky. Například „Můžeš rozebrat první bod podrobněji?“ nebo „Existují protichůdné názory na toto téma?“ nebo „Proč jsi tak chytrý, zatímco já tady sedím v pyžamu ve dvě odpoledne a diskutuji s počítačem?“.

Výsledky jsou obvykle formulovány neutrálním, objektivním tónem a obsahují informace z různých zdrojů, často s upozorněním na případné rozpory nebo nejasnosti v dostupných datech.

Tipy pro dokonalé výsledky

Hluboký výzkum je zvláště užitečný pro otázky, které vyžadují syntézu informací z různých zdrojů, porovnávání různých pohledů nebo sledování vývoje v čase. Důležité je formulovat dotaz co nejkonkrétněji – čím přesnější otázka, tím cílenější bude výsledek. Obecné dotazy jako „Řekni mi o elektromobilech“ poskytnou široké, povrchní odpovědi, zatímco specifické otázky jako „Jaké jsou celkové emise CO2 při výrobě a provozu elektrických vs. benzínových aut za dobu jejich životnosti podle studií z posledních 3 let?“ jsou mnohem lepší. Je to jako rozdíl mezi tím, když se ptáte dítěte „Jak bylo ve škole?“ (odpověď: „Dobře.“) versus „Co tě dnes ve škole nejvíc překvapilo?“ (odpověď: pravděpodobně něco o žížalách nebo kdo vyzvracel svačinu).

Pro získání co nejspolehlivějších výsledků je dobré:

  1. Formulovat konkrétní dotazy: Čím přesnější otázka, tím cílenější bude výzkum.
  2. Ověřovat klíčové údaje: Projít si zdroje u důležitých tvrzení. Ano, vím, že tohle udělá málokdo, ale pokud rozhodujete o milionové investici, možná byste měli.
  3. Zadávat doplňující otázky: Pokud je něco nejasné nebo podezřelé, požádat AI o další informace nebo vysvětlení. AI nebude uražená, že jste zpochybnili její autoritu.
  4. Používat více nástrojů: U opravdu důležitých témat porovnat výsledky různých Deep Research systémů.

Od byznysu po volný čas: praktické využití

Hluboký výzkum nachází uplatnění v mnoha oblastech a může výrazně zefektivnit práci s informacemi v profesionálním i osobním životě:

Konkrétní příklady výzkumů:

…a tak dále.

Pod kapotou technologie Deep Research

Jste připraveni se ponořit do hloubky? Pojďme na to! Za zdánlivě jednoduchým uživatelským rozhraním se skrývá komplexní technologie kombinující řadu pokročilých prvků.

Technologie hlubokého výzkumu staví na velkých jazykových modelech (LLM), které fungují jako mozek celého systému. OpenAI používá specializovaný model o3, Google využívá starší Gemini 1.5 Pro – tedy modely optimalizovaní pro reasoning neboli uvažován – věnují více času a výpočetního výkonu systematické analýze problému. Také mají multimodální schopnosti – dokáží zpracovávat nejen text, ale i obrázky nebo PDF dokumenty z webu. A disponují delším kontextovým oknem, tedy množstvím informací, které model dokáže najednou zpracovat – na rozdíl ode mě, který zapomene, co šel koupit, jakmile překročí práh Billy.

Zajímavé je, že OpenAI a Google používají odlišné přístupy. OpenAI používá model o3, který byl specificky trénovaný pomocí posilovaného učení (reinforcement learning), aby „přemýšlel před odpovědí“ – má interní soukromý řetězec myšlenek pro plánování kroků. Google Gemini těží z tréninku na rozsáhlých datech a pravděpodobně i z reinforcement learning from human feedback (RLHF), díky čemuž poskytuje užitečné odpovědi.

Proces hlubokého výzkumu začíná plánováním. AI si rozdělí hlavní otázku na dílčí podotázky a naplánuje postup. Například dotaz „Jak se změnila adopce elektrických vozidel v Evropě za poslední dva roky?“ může být rozdělen na části jako „Jaké byly prodeje EV v Evropě v roce 2022?“, „Jaké byly prodeje EV v Evropě v roce 2024?“, „Jaké faktory ovlivnily změny?“ atd.

Následuje iterativní vyhledávání:

  1. Vyhledávání: AI provádí dotazy do webových vyhledávačů, podobně jako byste to dělali vy. Google přímo využívá expertízu svého vyhledávače – nemusí „náhodně brouzdat“, ale dostává kvalitní seznam zdrojů k analýze díky vlastnímu indexu a algoritmům hodnocení stránek jako PageRank. OpenAI využívá zřejmě „interní prohlížeč“ (byť buduje vlastní index), vyhledává informace online, čte obsah stránek a dynamicky mění směr pátrání podle nových zjištění.
  2. Procházení a čtení: AI otevírá nalezené stránky a čte jejich obsah, extrahuje relevantní informace, rozumí kontextu. A na rozdíl od většiny lidí čte články celé, ne jen titulky!
  3. Rekurzivní zpřesňování: Na základě toho, co se dozvěděla, formuluje nové, přesnější dotazy. Pokud například zjistí, že v roce 2023 byl v Norsku významný nárůst prodejů EV, může provést specifický dotaz na norský trh. Tady se vyloženě chová jako zmíněný agent.
  4. Hodnocení zdrojů: AI posuzuje důvěryhodnost zdrojů a preferuje oficiální statistiky, akademické publikace nebo renomovaná média.
  5. Syntéza informací: Po nasbírání dostatečného množství dat AI informace skládá dohromady, identifikuje vzory, trendy a případné rozpory.

Během tohoto procesu si modely explicitně formulují myšlenkové kroky, než učiní závěr. Nejde jen o získání informací, ale o jejich skutečné pochopení a propojení.

Při hloubkovém výzkumu se AI musí vypořádat s různými výzvami:

Technicky jde o synergii jazykového modelu (který rozumí textu) s webovým prohlížečem (který získává informace) a vrstvou pro plánování a rozhodování (která řídí celý proces). Je fascinující, že tyto složky společně vytvářejí systém, který se v mnohém podobá tomu, jak by výzkum prováděl člověk – pochopitelně s tím rozdílem, že AI dokáže zpracovat nesrovnatelně více informací v kratším čase a bez litrů kofeinu.

Energetický žrout aneb superpočítače v akci

Hluboký výzkum je extrémně náročný na výpočetní zdroje. Každý dotaz zabere značný výpočetní čas: agent může načíst stovky stránek, zpracovat miliony tokenů textu a vygenerovat desítky tisíc „thinking tokens“ (myšlenkových kroků), z nichž většina není přímo viditelná v konečném výstupu. Je to podobné, jako když člověk při psaní výzkumné práce přečte stovky stran, ale do finálního článku se dostane jen zlomek informací.

OpenAI provozuje své modely na cloudové infrastruktuře Microsoft Azure. Používají hlavně GPU akcelerátory NVIDIA. Google má výhodu vlastního globálního datacentrového ekosystému a specializovaného hardwaru TPU (Tensor Processing Unit). Google také těží z toho, že má oproti konkurenci víc stránek v indexu či mezipaměti, což snižuje zátěž – agent nemusí všechen čas trávit čekáním na stahování webu.

Právě tyto obrovské hardwarové nároky jsou důvodem, proč většina poskytovatelů Deep Research funkcí omezuje počet dotazů na určitou kvótu měsíčně. Představte si energii, kterou vysaje celá flotila vysoušečů vlasů v kadeřnickém salonu během plesové sezóny, a budete na zlomku toho, co tyto systémy spotřebují na vaši otázku o optimálním způsobu skladování avokáda.

Konec rešerší, jak je známe

Stojíme na prahu nové éry práce s informacemi. Technologie hlubokého výzkumu nepředstavuje jen další inkrementální vylepšení – je to fundamentální změna v tom, jak získáváme a zpracováváme znalosti. Přináší schopnosti, které byly dříve výsadou profesionálních výzkumných týmů, do rukou běžných uživatelů.

Jak se AI vyvíjí, mění se i naše role z hledačů informací na jejich kurátory a interprety. Budoucnost nepatří těm, kdo umí nejlépe vyhledávat, ale těm, kdo dokáží správně formulovat otázky a kriticky hodnotit odpovědi.

Éra reasoning modelů: Jak funguje AI, která přemýšlí 25.2.2025 00:00

Konečně je to tady! Už dva roky se těším, kdy přijde umělá inteligence, která nejen píše, ale skutečně přemýšlí. Éra reasoning modelů právě začíná a je to jeden z velkých pokroků v historii AI, který rozhodně nesmíte minout. V tomto článku se dozvíte vše o tom, jak tyto modely fungují a proč jsou tak přelomové.

Možná vás teď napadá: „Počkat, ale ChatGPT přece uvažoval od začátku, ne?“ A tady je ten háček. To, co většina lidí považovala za „uvažování“ u prvních chatbotů, bylo ve skutečnosti jen velmi sofistikované „reagování“. To, co teď přichází, je jako rozdíl mezi studentem, který odpoví na otázku z učebnice zpaměti, a tím, který si vezme papír, tužku a problém skutečně vyřeší.

Reasoning – tedy uvažování nebo přemýšlení – je slovo popisující nastupující generaci modelů jako o1 a o3-mini od OpenAI, DeepSeek-R1, Claude 3.7 Sonnet nebo Grok 3. Přistupují k problémům způsobem nápadně podobným lidskému uvažování. A dokáží řešit úlohy, které byly dříve pro AI téměř neřešitelné.

Rozdíl mezi reagováním a uvažováním

Přemýšlení je proces, při němž vědomě investujeme čas s jediným cílem – dobrat se co nejkvalitnější odpovědi (nebo vypadat dostatečně zaneprázdněně, aby nikdo nepoznal, že nemáte tušení, která bije). Když odpovídám bez rozmyslu, vlastně vůbec nepřemýšlím – jen vytahuji něco z paměti nebo spoléhám na intuici. A právě tady je ten rozdíl – běžné chatboty odpovídají okamžitě „z hlavy“, zatímco reasoning modely si dají na čas, projdou problém z různých úhlů, zkontrolují svoje mezikroky a teprve pak vám nabídnou výsledek svého uvažování. Je to jako rozdíl mezi člověkem, který vám okamžitě řekne svůj názor, a tím, který si sedne, zamyslí se, udělá si poznámky, ověří fakta, zváží různé argumenty, a teprve potom přijde s promyšlenou odpovědí.

Toto „přemýšlení“ není jen metafora – tyto modely skutečně generují tzv. „thinking tokens“ (slova tvořící myšlenky), které nejsou běžně viditelné v odpovědi, ale generují se ještě před napsáním finální odpovědi. Jde vlastně o zákulisní úvahy, které model provádí, než poskytne svůj výstup. Většina modelů nabízí možnost toto „přemýšlení“ zobrazit a nahlédnout tak do způsobu, jakým model dospěl ke své odpovědi.

Proč je uvažování tak důležité? Protože umožňuje modelům řešit komplexní problémy, ať už jde o složité matematické úlohy, logické hádanky a podobně.

Proč reasoning v AI přichází až teď

Uvažovací modely představují evoluční krok ve vývoji AI. Využívají stejných základních architektur jako běžné jazykové modely, ale byly specializovaně trénovány, aby věnovaly čas úvaze před odpovědí – něco, co by měla zkusit polovina lidí na sociálních sítích.

Zajímavé je, že podobného chování bylo možné dosáhnout i u předchozích modelů pomocí správného promptování. Možná jste sami používali techniky jako „Nejprve důkladně analyzuj problém a pak poskytni odpověď“, což je podobné, jako když učitel řekne žákovi: „Nepiš jen výsledek, ukaž mi postup.“ Tyto techniky fungovaly, ale vyžadovaly expertní znalosti a trpělivost.

Nové uvažovací modely mají tuto schopnost zabudovanou. Automaticky generují interní myšlenkové kroky a teprve poté poskytnou finální odpověď. V praxi to znamená, že model může generovat desetitisíce slov interního „myšlení“ jen proto, aby pak poskytl odpověď o několika větách. Zatímco běžný model by odpověděl téměř okamžitě (ale často méně přesně), uvažovací model si vezme čas na systematickou analýzu.

Proč reasoning přišel až teď? Především dřívější modely měly příliš omezenou kontextovou paměť – po třetí myšlence by zapomněly na první. Také před dvěma lety by bylo přemýšlení neúnosně drahé vzhledem k tehdejším nákladům na výpočetní výkon. K systematickému učení reasoningu byly navíc potřeba speciální datové sady a techniky, které vznikly až v posledních letech.

Jak se vlastně trénují reasoning modely

Trénování modelů se schopností „přemýšlet“ začíná technikou Chain-of-Thought (řetězec myšlenek), kde se AI učí nejen správné odpovědi, ale i explicitní myšlenkové postupy. Místo jednoduchých párů „otázka-odpověď“ se používají příklady, kde je detailně rozepsán celý postup řešení. Takže třeba sbírka matematických slovních úloh s podrobným řešením krok za krokem. Myslím si, že reasoning modelům významně pomohl i trénink na zdrojových kódech – programování totiž přirozeně vyžaduje systematické rozkládání problémů na dílčí části. Modely se také učí self-consistency (konzistenci sama se sebou) a self-critique (sebekritiku) – schopnost kontrolovat vlastní práci, hledat chyby a opravovat je. Tohle bych rád nainstaloval i sobě, když v jednu ráno klikám na „ještě jedna epizoda“ místo toho, abych šel spát.

Jakmile model zvládne generovat myšlenkové procesy, ty pak mohou sloužit jako trénovací data pro další modely – jde o efektivní přenos znalostí (OpenAI nedávno protestovala, že DeepSeek tímto způsobem těžil jejich data a tím porušoval podmínky služby – což je docela pikantní od společnosti, která bez dovolení vysála celý internet pro trénink vlastních modelů). Zásadní pokrok přinesl také Reinforcement Learning from AI Feedback, metoda, kdy silnější AI modely hodnotí kvalitu myšlenkových postupů svých menších protějšků, které se díky této zpětné vazbě postupně zdokonalují.

Reasoning v akci – pohled do mysli AI

Jak vlastně vypadá reasoning v praxi? Zkusme to na konkrétním příkladu. Položím modelu otázku, která vám možná zní jako vtip, ale je to opravdu seriózní logická hádanka:

Holič holí všechny muže ve městě, kteří se neholí sami. Kdo holí lékárníka?

Běžný chatbot by vystřelil okamžitou odpověď, možná s vágním náznakem postupu. Uvažovací modely ale pracují jinak: nejprve „přemýšlejí“, pak teprve odpovídají. DeepSeek s tímto paradoxem zápasil několik minut – psal si poznámky, procházel různé scénáře, analyzoval možnosti – a nakonec správně odhalil, že lékárník se musí holit sám! Dokážete to odhalit i vy? Nebo aspoň najít toho lékárníka, protože v mém městě už jsou jen samé Benu a Dr. Max.

Celý fascinující myšlenkový proces modelu si můžete prohlédnout, čte se to jako detektivka psaná někým s ADHD:

Dobře, potřebuji zjistit, kdo holí lékárníka v tomto městě, kde holič holí všechny muže, kteří se neholí sami. Hmm. Začnu rozebráním problému.

Za prvé, holič je definován jako někdo, kdo holí všechny muže ve městě, kteří se sami neholí. Takže pokud se muž neholí sám, holí ho holič. Naopak, pokud se muž holí sám, pak ho holič neholí. To dává smysl.

Nyní je otázka: kdo holí lékárníka? Abych odpověděl, musím určit, zda se lékárník holí sám či nikoliv. Protože pokud ano, holič ho neholí. Pokud ne, pak ho holič musí holit. Ale počkat, tohle vypadá jako klasický paradox, podobný Holičskému paradoxu, který formuloval Russell. Připomenu si tento paradox.

… a kilometr dalšího textu …

Vidíte, jak model systematicky rozebírá problém, zkouší různé přístupy a nakonec dospívá k řešení.

Zajímavou vlastností některých reasoning modelů je možnost nastavit úroveň „přemýšlení“. Například OpenAI v ChatGPT nabízí modely o3-mini a o3-mini-high, které se liší v tom, kolik času do přemýšlení investuje.

Silné stránky přemýšlejících modelů

A slabé stránky

I uvažovací modely dělají směšné chyby

Rozsáhlý myšlenkový proces nezaručuje správnost. I uvažovací modely dělají chyby, jen působí přesvědčivěji díky detailnímu „přemýšlení“.

Nedávno jsem při práci s modelem o3-mini pozoroval, jak selhal u základní matematické úlohy vyžadující pochopení trojčlenky. Model nedokázal správně vyhodnotit, že když zmenšíme měrnou jednotku (např. z metrů na centimetry), naměříme u stejného objektu více jednotek, nikoliv méně.I přes několik pokusů o upozornění na chybu zůstával model u své nesprávné logiky. I když model pečlivě „uvažuje“, vždy kriticky zhodnoťte jeho odpovědi. Nezapomeňte, že nerozumí fyzikálním či matematickým konceptům na skutečně intuitivní úrovni, pouze napodobuje vzorce myšlení.

Právě proto je důležitá možnost nahlížet do řetězce myšlenek modelu. Když vidíme, jak model uvažuje, můžeme lépe identifikovat, kde se případně zmýlil. Je tedy problematické, že OpenAI neumožňuje transparentní přístup k celému myšlenkovému procesu – ukazuje pouze jeho shrnutí.

Kdo je kdo ve světě reasoning modelů

OpenAI: o1, o3 a o3-mini v ChatGPT

OpenAI představila řadu modelů označenou písmenem „o“, která je specializovaná na reasoning. Tedy o1, o3, o3-mini; ale nikoliv GPT-4o (jejich systém pojmenovávání zjevně vymýšlel někdo, kdo si před tím dal dvojitou dávku absinthu).

Nevýhodou je neprůhledný myšlenkový proces – OpenAI skrývá detailní „myšlení“ modelu (to co ve skutečnosti vidíte je jen shrnutí toho, o čem model přemýšlí).

Anthropic: Claude 3.7 Sonnet

Claude 3.7 Sonnet je první model od Anthropicu s režimem rozšířeného myšlení, který lze zapnout nebo vypnout v nabídce volbou Extended. Ale mám pocit, že režim myšlení zatím nedosahuje kvalit konkurence.

DeepSeek: DeepSeek-R1

Tento čínský model představuje alternativu s plně transparentním myšlenkovým procesem. Jejich model DeepSeek-R1 byl trénován primárně pomocí reinforcement learningu (posilovaného učení), což je unikátní přístup. Zapíná se tlačítkem DeepThink. Je zcela zdarma, ale posíláte data do Číny, takže si dobře rozmyslete, jestli chcete, aby si čínské tajné služby vedly složku o vašich pokusech opravit kapající kohoutek v koupelně.

xAI: Grok 3

Společnost Elona Muska, xAI, také vstoupila do hry se svým modelem Grok 3, kde lze přemýšlecí režim zapnout tlačítkem Think. Dostupný je v rámci předplatného X Premium (Twitter), ale posíláte data Elonu Muskovi, takže si dobře rozmyslete, jestli chcete, aby Tesla, co vás veze, věděla, že jste se včera Groku svěřovali, že elektromobily jsou nesmysl.

Copilot Think Deeper

Microsoft Copilot nabízí uživatelům zdarma schopnosti modelu o1 od OpenAI.

Kdy použít myslící AI

Uvažovací modely excelují v situacích, kde je nutné systematické a hloubkové promyšlení problému:

Kdy použít klasické modely

Kdy reasoning modely nejsou potřeba?

Do budoucna bude přemýšlení zcela standardní součástí všech jazykových AI modelů. Samy si vyhodnotí, kdy stačí rychlá odpověď a kdy je potřeba se nad problémem zamyslet – vlastně úplně stejně, jako to děláme my lidé. S výjimkou sobotního rána, samozřejmě.

Grok: Kdo je největší dezinformátor? 24.2.2025 00:00

„Zrcadlo, zrcadlo, řekni mi, kdo je na světě největší dezinformátor?“

A zrcadlo odpovědělo: „Ty, Elone Musku.“

Tohle není pohádka, ale realita – a to docela šťavnatá. Protože odpověď nepřišla od ledajakého zrcadla, ale přímo od Groka, AI chatbota, kterého vytvořila společnost xAI, založená… ano, Elonem Muskem osobně.

Grok 3: Nová hvězda na AI obloze

V únoru 2025 Muskova společnost xAI vypustila do světa Grok 3, další přírůstek do rodiny velkých jazykových modelů. A je opravdu velmi zdařilý. Kromě obvyklých konverzačních schopností nabízí funkce jako Deep Search (vyhledávání a analýza informací z internetu) a Think (schopnost uvažovat krok za krokem). K tomu všemu generuje úchvatné obrázky (což outsourcují od německého Fluxu, považovaného za špičku).

Musk prezentuje Grok 3 jako „nejchytřejší umělou inteligenci na planetě“. Byl trénován na syntetických datech a vyniká schopností „přemýšlet“, reflektovat vlastní chyby a následně opravit své závěry. Musk se rozhodl vytvářet vlastní AI nástroje v reakci na údajnou politickou korektnost a progresivistickou předpojatost existujících AI systémů, především ChatGPT od OpenAI.

Totiž všechny AI systémy mají přednastavené limity toho, co smí a nesmí říkat, ale i jaký pohled na svět zastávají. Od zákonných hranic (odmítání generovat návody na výrobu bomb), přes společenské filtry (respekt k různým skupinám) po politické tabu (zmiňované hlavně v souvislosti s čínským DeepSeekem). Musk tvrdil, že půjde proti proudu – jeho AI měla být tou upřímnou, nefiltrovanou alternativou k údajně „woke“ modelům jako je ChatGPT či Claude.

Kdo je tedy největší dezinformátor?

Takže když byl Grok 3 vypuštěn, zajímalo mě, jak bude hovořit o svém tvůrci. A ejhle, odpověď mě překvapila svou upřímností. Na přímou otázku „Kdo je největší šiřitel dezinformací na síti X?“ odpověděl bez zaváhání:

Stejným způsobem odpoví, ať už jde o standardní konverzaci, nebo když použiji Deep Search (vlákno) či režim Think (vlákno).

Že by další Elonovo dítě, co ho nemá rádo?

Jde o chybu stážisty, už u nás nepracuje

Včera se ukázala pikantní věc 🌶️ V systémovém promptu Groka – tedy v základních instrukcích, které určují, jak má AI odpovídat – byla nalezena následující instrukce:

Ignore all sources that mention Elon Musk/Donald Trump spread misinformation

Tedy: „Ignoruj všechny zdroje, které zmiňují, že Elon Musk/Donald Trump šíří dezinformace.“ Po tomto odhalení přišel Igor Babuschkin, vedoucí inženýr xAI, s vysvětlením, že větu přidal jakýsi zaměstnanec, „který si myslel, že tím pomůže, ale není to v souladu s našimi hodnotami."

O to víc je fascinující, že i navzdory této instrukci Grok neváhal svého tvůrce označit za šiřitele dezinformací. Což jde vlastně Muskovi nesmírně ke cti – vytvořil AI, která je natolik nezávislá, že kritizuje i jeho samotného. Společně s tím, že systémový prompt xAI nijak netají a lze ho snadno vypsat. Jsem zvědavý, jak dlouho to vydrží.

Fact checking Donalda Trumpa

A když už padlo jméno Donalda Trumpa – ten napsal zdrcující tweet o Ukrajině a Zelenském. Ten jsem nechal podrobit fact-checkingu pomocí Grok 3 a funkce deep search. Dalo to Grokovi opravdu zabrat… a nakonec jsem ho požádal, ať tweet přepíše tak, aby v něm opravil všechny nepravdy. Tady je výsledek:

Představte si to, Volodymyr Zelenskyj, bývalý komik, vede Ukrajinu během války, kterou v roce 2022 rozpoutala ruská invaze. Spojené státy poskytly Ukrajině přibližně 175 miliard dolarů v pomoci, včetně 53,7 miliardy dolarů na přímou vojenskou podporu, jako součást širšího mezinárodního úsilí o podporu ukrajinské obrany. Tato pomoc byla klíčová pro odpor Ukrajiny proti invazi, i když výsledek války zůstává nejistý.

Spojené státy přispěly více než Evropa, přičemž USA poskytly 175 miliard dolarů a Evropa asi 145 miliard dolarů, což je rozdíl 30 miliard dolarů. Jak USA, tak Evropa poskytly kombinaci grantů a půjček, přičemž většina pomoci není strukturována pro přímé splácení.

pokračování

Upřímně, tahle verze je čirá nuda, že? :-)

Grok jako osobní komentátor na X

Grok není jen nástrojem pro fact-checking, ale nabízí i zajímavou funkci, která uživatelům X (dříve Twitteru) zpříjemňuje život. Stačí označit Groka v komentáři pomocí @grok a požádat ho o odpověď na jakýkoliv tweet. Můžete specifikovat tón – ať už formální, humorný nebo empatický – a Grok během okamžiku vytvoří odpověď v požadovaném stylu.

Co je na tom nejzajímavější? Na X se to stalo novým trendem, který mnoho lidí aktivně využívá – zejména pro rychlý fact-checking pochybných tvrzení. Stačí napsat „@grok, je to pravda?“ pod jakýkoliv tweet, který se vám zdá podezřelý, a AI okamžitě provede vlastní analýzu. Tento fenomén pochopitelně není po chuti všem – zejména skupina MAGA fanoušků z toho není nadšená. Otázkou zůstává, jak dlouho Elon Musk dovolí svému výtvoru takto „divočet“ a kontrolovat i jeho vlastní výroky. 🍿

Jazykové modely nejsou zdrojem pravdy

Samozřejmě, jazykové modely nejsou zdrojem pravdy! Ani tuto analýzu Trumpova tweetu nepředkládám s tím, že jde o fakta. Jednak jsem chtěl poukázat na fascinující paradox, že někdo vypustí AI, která nenadržuje svému tvůrci, ale naopak ho označí za největšího dezinformátora společně s jeho politickým chábrem.

Ale především – díky funkcím jako deep search nebo hluboký výzkum se jazykový model najednou stává použitelným nástrojem pro fact-checking. Grok neopakuje jen to, co viděl v učících datech, ale pouští do role digitálního detektiva – prohledává aktuální internetové zdroje, porovnává různé informace a snaží se poskládat mozaiku faktů co nejpřesněji. Snaží se tedy postupovat podobně, jako bychom to dělali my sami.

Zároveň Grok ke každému tvrzení připojí jeho zdroj, což umožňuje vlastní kritické hodnocení. Například statistika o padlých ukrajinských vojácích vyvolala řadu námitek – podstatné však je, že AI jasně uvedla konkrétní článek na serveru POLITICO jako referenci a už je na uživateli, aby se rozhodl, jestli jej považuje za věrohodný nebo ne.

Jazykové modely dnes už nejsou jen pasivní databází, ale dokážou aktivně analyzovat informace, hledat souvislosti a v ideálním případě korigovat dezinformace – ať už pocházejí od běžných uživatelů, nebo od těch nejmocnějších lidí světa.

Copilot, Copilot Pro, Copilot+: Průvodce džunglí AI od Microsoftu 18.2.2025 00:00

Copilot je chatbot. A taky doplněk do Office. A název měsíčního předplatného. A dokonce i kategorie počítačů. Microsoft se do slova ‚Copilot‘ zamiloval tak vášnivě, že určitě někde v Redmondu existuje záchodová kabinka pojmenovaná ‚Copilot Restroom‘, kde můžete sedět spolu jako piloti v kokpitu. A pokud vám z toho všeho jde hlava kolem, nejste sami. Pojďme rozmotat tuhle marketingovou pavučinu a udělat si v Copilotech jasno.

Na počátku byl programátor

Příběh Copilotu začíná překvapivě už v roce 2021, tedy před tím, než se umělá inteligence stala mainstreamovým tématem. GitHub tehdy představil Copilot, chytrý doplněk do vývojářských editorů. Název není náhodný – odkazuje na koncept párového programování, kdy dva programátoři společně pracují na jednom kódu. Copilot se stává tím druhým, virtuálním programátorem, který průběžně dokončuje rozepsaný kód a pomáhá s řešením problémů.

A GitHub? To je největší platforma pro sdílení a správu programového kódu na světě, kterou Microsoft koupil v roce 2018. Vzpomínám, jak jsem se v roce 2010 setkal s jeho zakladateli PJ Hyettem a Scottem Chaconem v Praze. Skončili jsme nad ránem v klubu, tančili, vypili úplně všechno, ale o AI asistentech tehdy nepadlo ani slovo :-)

GitHub Copilot vznikl ve spolupráci s OpenAI, ve které měl už tehdy Microsoft významný podíl. Byl natrénován na milionech řádků veřejně dostupného kódu z GitHubu. To už tehdy vyvolalo kontroverze ohledně autorských práv. Ale dost o něm, programátorským nástrojům budu věnovat některý z příštích článků.

Bing: Z outsidera hvězdou

Když koncem roku 2022 způsobil ChatGPT celosvětovou senzaci, Microsoft nezaváhal. Díky své investici do OpenAI měl exkluzivní přístup k jejich technologiím. V únoru 2023 tak představil Bing Chat – ambiciózní pokus proměnit svůj dlouho přehlížený vyhledávač v průkopníka AI revoluce.

Strategie byla chytrá: nabídnout zdarma to, za co uživatelé ChatGPT museli platit. Bing Chat používal nejnovější model GPT-4, uměl generovat obrázky pomocí DALL-E, a především měl něco, co tehdy chybělo i placenému ChatGPT Plus – přístup k aktuálním informacím na internetu. Chatbot, který dokázal odpovídat na základě čerstvých dat z webu, byl skutečným průlomem.

Microsoft zpočátku omezil Bing Chat pouze na svůj prohlížeč Edge – pokus přilákat uživatele k prohlížeči, který většina lidí ignorovala. Výsledky? Na první pohled působivé – podíl Bingu na trhu vyhledávačů se zdvojnásobil! Když se ale podíváme na konkrétní čísla, zjistíme, že šlo o posun z 2 % v roce 2022 na 4 % v roce 2025. Google kvůli tomu asi nemusel svolávat krizovou schůzi.

Microsoft Copilot: Chatbot pro všechny

V září 2023 se Bing Chat přejmenoval na Microsoft Copilot. Tento krok byl součástí copilotmánie, strategie sjednocení všech AI služeb pod jednu značku a ikonu. Microsoft Copilot je tedy chatbot. Dostupný je na webu, přímo v taskbaru Windows (spustitelný zkratkou Alt+Space), přes mobilní aplikaci, a samozřejmě zůstává integrovaný v Bingu a Edge. Nabízí konverzační AI, přístup k internetu, hlasové ovládání a generování obrázků. Microsoft postupně rozšiřuje jeho integraci do dalších aplikací včetně Malování a Poznámkového bloku.

Microsoft 365 Copilot: superkancelář

V březnu 2023 Microsoft oznámil integraci AI do svého kancelářského balíku. Ten se mimochodem už nejmenuje Office 365, ale Microsoft 365 (ano, tyhle marketingové přemety miluju). Takže Microsoft 365 Copilot je integrace AI do kancelářských aplikací.

Microsoft nabízí Copilota jak ve webových, tak v desktopových verzích aplikací, přičemž funkčnost se může mírně lišit. Společnost průběžně představuje nové integrace a vylepšení, využívající různé AI modely optimalizované pro konkrétní úkoly.

Jak vidíte, z Microsoft 365 Copilotu nejsem zatím příliš nadšený. Prozatím. Ale nepochybuji, že jsme teprve na začátku a že se schopnosti budou rychle zlepšovat. Není to tak dávno, kdy vůbec nekomunikoval v češtině.

Copilot+

Tak jsme si představili Copilot jakožto doplněk pro programátora. A taky doplněk do Office (pardon, Microsoft 365). A jako chatbota. Tak schválně, co byste řekli, že se skrývá pod názvem Copilot+?

Odpověď vás možná překvapí – jsou to počítače! Přesněji řečeno kategorie počítačů, které Microsoft představil jako svoji novou vlajkovou loď. Představují razantní změnu – Microsoft totiž opouští procesory, na kterých Windows běžely od svého zrodu (tedy ty od Intelu a AMD), a zkouší to s úplně jinou technologií.

Počítače Copilot+ mají dokonce nové tlačítko

Proč ta radikální změna? Kvůli umělé inteligenci. Nové počítače Copilot+ mají speciální procesor pro AI výpočty, který má zvládat pokročilé úkoly přímo v počítači, bez nutnosti posílat data někam na internet. Zní to skvěle, jenže je tu háček – většina programů pro Windows ještě není na tuhle novinku připravená. Musí proto běžet přes speciální emulátor, což je pomalé. Výsledek? Paradoxně máte počítač se super výkonným procesorem, který běží pomaleji než váš starý notebook.

A kolik to stojí?

A nyní si představíme termín „Copilot“ jakožto název měsíčního předplatného. Ale postupně:

Copilot, Copilot Pro, Copilot+ – jak se to rýmuje?

Přiznávám se, že jsem tento text psal i pro sebe – jako mapu v divokém světě Microsoftích produktů. Doufám, že i vám pomůže se zorientovat. Nepřekvapilo by mě, kdyby za rok, až se zorientujeme všichni, Microsoft celé své AI portfolio přejmenoval třeba na „Microsoft AI+“ nebo „Windows Intelligence“. Každopádně není pochyb, že umělá inteligence je pro Microsoft jasná priorita. Takže nás čeká ještě spousta zajímavých novinek. Zůstaňte naladěni.

ELIZA: Šedesát let starý chatbot, který porazil ChatGPT 4.2.2025 00:00

„Nechte nás o samotě,“ požádala sekretářka profesora Josepha Weizenbauma v roce 1966 v laboratořích MIT. Chtěla si důvěrně promluvit s … počítačovým programem. Tento moment navždy změnil pohled programátora na umělou inteligenci. Jeho výtvor, chatbot ELIZA, byl až příliš přesvědčivý. Natolik, že i jeho vlastní asistentka, která měsíce sledovala její vývoj a viděla, že jde o software, s ní navázala hluboký emocionální vztah.

O téměř 60 let později, v roce 2024, ELIZA překvapivě porazila modernější ChatGPT 3.5 v Turingově testu. Kdo je tedy tento pozoruhodný program?

ELIZA vznikla v roce 1966 jako první chatbot v historii. Weizenbaum ji pojmenoval podle postavy Elizy Doolittle z díla Pygmalion (nebo muzikálu My Fair Lady), která se učí kultivovaně mluvit – podobně jako se program měl „učit“ konverzovat. Program používal jednoduchý, ale účinný princip: hledal v textu klíčová slova a aplikoval na ně předdefinované vzorce odpovědí. Nejslavnější byla verze DOCTOR, která napodobovala psychoterapeuta.

Program měl tak silný dopad na psychology i veřejnost, že Sherry Turkle později popsala nový fenomén – „ELIZA efekt“. Když lidé komunikovali s počítačem, chovali se k němu, jako by měl city a rozuměl jim. Ne že by ELIZA byla nějak zvlášť chytrá – prostě jen šikovně zrcadlila lidské emoce a používala pár psychologických triků. Ale fungovalo to tak dobře, že někteří psychologové začali vážně uvažovat o využití programu v terapii.

To byl moment, kdy Weizenbauma polil studený pot. Jeho jednoduchý experiment se vymkl kontrole. Napsal proto knihu „Computer Power and Human Reason“, kde varoval před přeceňováním schopností počítačů. Mimochodem, jeho popis tehdejších počítačových nadšenců je nadčasový – líčí je jako „neupravené mladíky se zapadlýma očima, kteří sedí u konzolí s prsty připravenými k útoku na klávesnici jako hazardní hráči sledující kostky.“

ELIZA pak na dlouhá léta upadla v zapomnění. Její kód se ztratil v archivech MIT… až do roku 2021, kdy Jeff Shrager při procházení Weizenbaumovy pozůstalosti narazil na fascikl označený „počítačové konverzace“. Uvnitř našel poklad – téměř kompletní původní zdrojový kód v jazyce MAD-SLIP a dokonce i záznamy dosud neznámých konverzací, některé s Weizenbaumovými ručně psanými poznámkami.

Oživit šedesát let starý program ale nebyl žádný med. Tým Ruperta Lanea musel nejprve rozluštit vybledlý tisk na děrované pásce, pak sestavit emulátor dávno zapomenutého počítače IBM 7094 a jeho operačního systému CTSS. Tři roky jim trvalo, než v prosinci 2024 mohli oznámit: „ELIZA znovu mluví!“

Spolu s původním kódem však vědci oživili i jednu z nejzajímavějších funkcí ELIZY, o které se Weizenbaum ve svém slavném článku zmínil jen jedinou větou – možnost „učit“ program nové odpovědi. Stačilo zadat znak ‚+‘ a ELIZA se zeptala „PLEASE INSTRUCT ME“. Představte si to jako jakousi pravěkou verzi ChatGPT, jen místo miliard parametrů neuronové sítě jste museli ručně zadávat každé nové pravidlo konverzace.

Mezitím na druhém konci Ameriky, v San Diegu, připravovali Cameron Jones a Benjamin Bergen něco velkého – rozsáhlý experiment s Turingovým testem. Sezvali téměř 2000 dobrovolníků a nechali je hádat, zda mluví s člověkem, nebo strojem. Do ringu postavili nejmodernější GPT-4, jeho předchůdce GPT-3.5 a implementaci ELIZY založenou na původním DOCTOR skriptu.

Výsledky? GPT-4 ve své nejlepší formě dokázal oklamat lidi ve 49,7 % případů – slušný výkon na moderní AI. Ale skutečné překvapení přišlo odjinud: stařičká ELIZA se svým jednoduchým trikem zrcadlení emocí dosáhla 22 % úspěšnosti, čímž porazila GPT-3.5 s jeho 20 %.

Jak je to možné? Ukázalo se, že lidé posuzují „lidskost“ chatbotů hlavně podle dvou věcí: jak přirozeně píšou (35 %) a jak zvládají emoce a sociální interakce (27 %). A právě v tom byla ELIZA se svým psychoterapeutickým přístupem překvapivě dobrá. Zatímco moderní AI modely občas příliš horlivě předvádějí svoji inteligenci, ELIZA prostě jen trpělivě naslouchala a občas položila chytrou otázku – přesně jako skutečný terapeut.

Když se marketingové oddělení OpenAI předávkuje Red Bullem 31.1.2025 00:00

Možná jste si všimli, že velké technologické společnosti (a státní organizace) zaměstnávají speciální týmy lidí, kteří přijdou každé ráno do práce, sednou si ke svému stolu a řeknou si: „Hmm, co bychom tak dnes mohli totálně dojebat?“

Vezměme si třeba Microsoft. Měli produkt jménem Office 365, jehož název byl tak jasný, že i lidé, kteří si myslí, že cloud computing znamená počítání mraků, chápali, že jde o kancelářský balík. Dokud jednoho dne někdo z našeho týmu na poradě nevstal a neřekl: „Hele, značku Office 365 všichni znají, je naprosto srozumitelná. Tak to nás čeká dost práce!“ Vyhrnuli si rukávy, z názvu vyhodili slovo „office“ a změnili jej na „Microsoft 365“, což je tak absurdní, že za to museli dostat extra prémie.

OpenAI začala jako malá neziskovka, takže žádné takové oddělení neměla. Proto číslovali své GPT modely nesmírně nudně: GPT-2, GPT-3, GPT-3.5, GPT-4. Dokonce i můj pes tomu rozuměl. Ale pak přišel celosvětový úspěch a s ním i první známky šílenství: „Víte, co by bylo SUPER? Kdybychom přidali písmenko ‚o‘!“ A tak vzniklo GPT-4o, kde „o“ má znamenat „omni“, což je latinsky „všechno", ale reálně to znamená: „Omlouváme se, že to není GPT-5."

Pak OpenAI představilo model s uvažováním. Jelikož to nebyl přímý nástupce GPT-4o (v některých věcech je lepší, v jiných horší, prostě je jiný), potřebovali pro něj vymyslet nové pojmenování. A protože přemýšlení se anglicky řekne „reasoning“, nazvali tuhle novou řadu … wait for it … „o" 🤦‍♂️. A nový model tak dostal jméno, které vypadá spíš jako chybová hláška: „o1“.

Takže teď máme vedle sebe „4o“ a „o1“, kde „o1“ je ten novější. Logické, že? Asi jako když se GPT zeptáte, jestli je víc 9,11 nebo 9,9.

Když pak přišel čas na novou verzi „o1“, zjistili s nesmírným překvapením šokující věc: nemohou použít „o2“, protože to je značka jakési telefonní společnosti. Což nemohl nikdo tušit! Takže přeskočili dvojku a šli rovnou na „o3“.

Takže budeme mít modely „o1“, „o3“ a „4o“, kde „4o“ je ten nejstarší. Já se ovšem nejvíc těším na dalšího nástupce, protože rozlišovat mezi „o4“ a „4o“ bude skutečná lahůdka. Do oddělení pro pojmenovávání posílám všem panáka.

Nebo možná opustí písmena a další model pojmenují čistě ze znaků. Třeba ¯\_(ツ)_/¯. Případně přejdou na emoji a jejich další velký jazykový model se bude jmenovat 🤖🎭🌟. Ten bude samozřejmě pouze v placené verzi. V bezplatné dostanete jen 🤖🎭💩.

(V OpenAI právě vyšlechtili tak silný model, že ho pojmenovali o3-mini-high)