Přestože je kódování UTF-8 již delší dobu považováno ve světě IT za nepsaný standard, jeho podpora v nejrozšířenějším webovém jazyku současnosti není zrovna nejdokonalejší.
Pojďme se tedy podívat na jaké problémy můžeme při použití UTF-8 v PHP narazit.
Unicode Byte-Order-Mark (BOM)
Unicodová kódování umožňují vložit na začátek souboru speciální znak, který usnadňuje detekci pořadí bytů (big-endian/little-endian) a zvolené kódování souboru. Tento neviditelný znak ale způsobuje značné komplikace. Pokud ho vložíme na začátek PHP skriptu, je tento znak odeslán (jako jakékoliv HTML) na výstup, což znamená, že jsou odeslány i HTTP hlavičky. Díky tomu nám náhle ve skriptu přestane fungovat volání funkce header(), session_start() a další. Díky tomu, že je znak neviditelný, bývá odhalení příčiny pěkně zapeklité.
Takže BOM rozhodně nepoužívat.
MySQL
MySQL ve výchozím nastavení používá kódování ISO-8859-1 (latin1), které nepodporuje značnou část našich znaků (ěčř...). Pokud do takto nastavené databáze uložíme text kódovaný UTF-8, některé české znaky budou nahrazeny otazníky. Je tedy nutné převést databázi, tabulky i všechny sloupce do kódování, které české znaky podporuje (utf8, cp1250, latin2).
Při komunikaci PHP interpretu s MySQL démonem může docházet k převodům mezi kódováními. Tyto převody můžeme snadno nastavit jedním ze dvou SQL příkazů:
SET NAMES UTF8;
SET CHARACTER SET UTF8;
První způsob nastaví na zvolené kódování proměnné character_set_client, character_set_connection a character_set_results. Druhý způsob nastaví jen proměnné character_set_client a character_set_results. Character_set_connection je nastaveno na hodnotu proměnné character_set_database.
Často stačí použít SET CHARACTER SET. SET NAMES musíme použít jen tehdy, když se proměnná character_set_database neshoduje s kódováním, které jsme nastavili pro naši databázi. SET NAMES pak totiž nastaví správně i proměnnou character_set_connection, která způsobí převod námi zasílaných dotazů do správného kódování před jejich provedením.
Práce s UTF-8 řetězci
Klasické funkce pro práci s řetězci zpracovávají každý byte vstupního řetězce jako samostatný znak. Proto pokud napíšeme v UTF8:
echo strlen("ěščřž");
Dostaneme výsledek 10 místo očekávaných 5. Ještě horší situace nastane, pokud budeme chtít z řetězce nějakou jeho podčást. Substr nám klidně vrátí výsledek, který končí nebo začíná půlkou dvoubytového znaku.
Řešením je používat rozšíření mbstring (--enable-mbstring), které ve většině instalací PHP už je. Mbstring nám nabízí alternativy ke klasickým řetězcovým funkcím, které ale umějí pracovat s vícebytovými kódováními. Všechny mbstring funkce začínají předponou mb_ za kterou následuje jméno původní funkce. Před začátkem práce musíme mbstring nejprve říct, v jakém kódování budou řetězce, které bude zpracovávat. Takže například:
mb_internal_encoding("UTF-8");
echo mb_strlen("ěščřž");
Nyní už dostaneme očekávaný výsledek 5.
Závěr
Podpora UTF-8 sice není do PHP moc dobře začleněná, ale pokud si zvykneme používat multibytové funkce, dáme si pozor na BOM a dopíše do třídy pro práci s MySQL jeden řádek, bude soužití s UTF-8 bezproblémové. Jednou, třeba až budeme potřebovat na webu zobrazit pár znaků v klingonštině, se nám vynaložené úsilí vrátí :)
V novějších verzí PHP jsou k dispozici funkce
a , které nastaví kódování tak, že ho zohledňuje i resp. .Častěji než extenze MB bývá k dispozici extenze iconv, která je chudší, ale délku řetězce zvládne.
Klingonština se do Unicode nedostala (je jen v oblasti pro soukromé použití, tam si ale může dát kdo chce co chce).