HTML::Mason, mod_perl, čeština a utf-8

HTML::Mason je jednou z výborných (řekl bych nejlepších, ale dost jsem jich ještě nevyzkoušel) voleb při vývoji webových aplikací v Perlu. Výhod je spousta – komponenty, přehledná syntax, jednoduché na naučení. Dokud je vyvýjená aplikace v angličtině a pro anglicky mluvící, je všechno krásné, funkční a jednoduché. Problém začne až ve chvíli, kdy se na web začne míchat Unicode.

Podle dokumentace funguje Unicode v Perlu tak nějak automaticky a interní reprezentace řetězců je vždy UTF-8. To je sice pravda, ale jen z části. Retězce totiž Perlu interpretuje buď jako řetězec (a pak je interně v Unicode), nebo jako pole bytů. Pak pochopitelně v Unicode není. Dokud pracujeme jen s jedním typem řetězců (a nevadí drobnosti jako nesprávné délky řetězce), není nutné situaci řešit, prostě to nějak funguje a výstup je správně. Horší to je ve chvíli, kdy se začnou kombinovat Unicode řetězce s poli bytů – v takové chvíli je nutné překódovat oba řetězce do stejného formátu. Perl si vybere Unicode jako menší zlo, ale obě možné volby jsou špatně. Obzvlášť pikantní situace nastane ve chvíli, kdy bytový řetězec už ve skutečnosti je v Unicode: při konverzi nedojde k rozpoznání multibyte znaků a každý byte je interpretován jako samostatný znak (a díky tomu z něj v Unicode reprezentaci opět vznikne multibyte znak). Typicky se tento problém projevuje na místech, kde se kombinují data ze dvou různých zdrojů:

První problém: soubory s Masonovskými komponentami obsahují HTML kód (z pochopitelných důvodů psaný jako UTF-8) a je potřeba Perl přesvědčit, že je tak také má interpretovat. V “normálním” skriptu to lze udělat například přidáním řádku

use utf8;

na začátek souboru. Masonovská šablona sice normální skript není, ale Mason šablony při zpracování překládá na skripty (takže text+html v šabloně padne do řetězcových konstant) a ty pak spouští. Takže zbývá jen odhalit, jakým způsobem do nich propašovat výše uvedený řádek. Přímočerá možnost je uvést ho do sekce <%init> u každé komponenty. Nepraktické, že? Naštěstí jde Masonu v konfiguraci Apache předat kus kódu, který bude přidán na začátek kódu každé komponentu. Při použití přes mod_perl stačí do konfigurace Apache přidat řádek:

PerlSetVar MasonPreamble "use utf8;"

Podobně pro (Fast)CGI.

Druhý problém: Apache2::Request nenastavuje interní Unicode flag automaticky, přestože z HTTP hlaviček ví, že data v Unicode jsou (kvůli možným útokům přes neplatné UTF-8 znaky). Při normální práci přes mod_perl není problém parametry správně překódovat nebo zpracovat ručně, ne přes Apache2::Request. s Masonem to je trochu horší – parametry si zpracovává sám pomocí Apache2::Request a nepodařilo se mi najít jednoduché řešení, jak zasáhnout přímo do zpracování parametrů, ale bez změn ve zdrojových kódech Masonu. Naštěstí se k parametrům dá dostat v Masonovských autohandlerech ještě předtím, než se k nim dostane libovolná komponenta. Vložení následujícího kódu do autohandleru v kořenovém adresáři aplikace

<%init>
%ARGS = map { 	$_ = Encode::decode('utf8', $_); }%ARGS;
$m->call_next(%ARGS);
<%init>

způsobí překódování parametrů do UTF tak, že všechny ostatní komponenty je dostanou ve správném tvaru.

Třetí, čtvrtý, … problém: řetězce z databáze, souborů a dalších zdrojů. Ale to až někdy jindy. A nebo na to přijdete sami :)

Bookmark and Share

Leave a Reply