Friday, February 2, 2007

Encoding Problem?

Отново намерих време да драсна. Чудех се за какво, но тъй като тази седмица нямаше нищо интересно в web пространството, реших да споделя малко опит в решаването на най-дразнещия неамерикански проблем - кодирането на информацията. По времето на HTML4, това не беше никакъв проблем. Просто сменяш кодирането в <meta> тага, сменяш файла и това е. После обаче PHP стана толкова масово явление, че изведнъж всеки започна да разхвърля информация по всякакви $_POST-ове и $_GET-ове и тази информация не винаги се показваше в правилният формат накрая. А когато към всичко това добавим charset & collation на MySQL сървъра, таблицата и текущата база данни, плюс AJAX и сървър настройките, картинката става вече наистина потресаваща.
При всички случаи ни трябва стандарт. Трябва ни нещо, в което да можем да си сейвнем файловете, и после да го нагласим във всички останали хедъри и настройки. UTF-8 би бил перфектният вариянт, но за съжаление PHP и UTF-8 не работят пълноценно. Проблемът идва от хедъра на файловете, които са сейвнати в UTF-8 енкодинг. Там, в самото начало, изскачат няколко байта повече, които PHP интерпретира като output и затова не ползволява да се пращат никакви хедъри (вкл. сесии). Отвратително. Това, обаче, ще се промени с излизането на PHP6, и аз ще съм един от най-щастливите хора на земята.
До тогава мисля да процедирам по следният начин.
  1. Избира се един алтернативен енкодинг (например cp-1251 за кирилица, ISO-8859-1 за латиница и т..н);
  2. В този енкодинг се записват файловете и информацията;
  3. Същият енкодинг се изпраща и от сървъра.
  4. Същият енкодинг се пише в <meta> тага (ДА НЕ СЕ ЗАБРАВЯ!)
По този начин сме сигурни, че всичко ще се показва правилно и ще достига правилно до браузъра, а пък FireFox няма да се прави на умен и да пренаписва хедърите. (Не знам защо "Лисицата" не харесва UTF хедъри... но такъв е животът.) Сега остава да решим проблема със записването и транспортирането на информацията. Ако пишем добър код, следвайки препоръките за MVC (за които постнах тук), би трябвало за тази част от сайта да се грижат други скриптове. Затова предприемаме следните стъпки:
  1. В началото на всеки файл пренаписваме хедъра на сървъра на UTF-8, а където играе MySQL - "set names utf-8";
  2. default charset, енкодинг и колация на MySQL се настройват за UTF-8;
  3. Ако на фронтенда ползваме AJAX, енкодираме всичките променливи в UTF-8 & URL encoding. За целта препоръчвам WebToolKit. Ако не - при филтрирането на $_REQUEST-а използваме utf8_encode();
  4. Съответно, при извеждане на информацията, използваме utf8_decode() или iconv(), така че да имаме данните готови за показване в другия енкодинг.
Надявам се да съм помогнал и да съм спестил мъки на много хора.

9 comments:

dimodi said...

> "няколко байта повече, които PHP интерпретира като output и затова не ползволява да се пращат никакви хедъри (вкл. сесии)."

Може би нещо не те разбирам правилно, но със Zend Studio и сайтове на UTF-8 никога не съм имал такъв проблем ?

Anonymous said...

Проблем с нежеланите символи се получава, ако е оставена конфигурацията на Apache уеб сървъра да изпраща encoding header по-подразбиране.
Ако тази настройка се изключи и още в php-то се указва правилния енкодинг-а:
header('Content-type: text/html; charset=UTF-8');
за UTF-8 сайта ще работи нормално.
Разбира се, файловете трябва да записани, като UTF-8. Ако има база данни е желателно и тя да е в същия енкодинг. Ако случайно остане enecoding по подразбиране, то ако се използва set names:
mysql_query("set names 'utf8'");
след mysql_connect(...);
сайта ще си работи нормално.
(за MySQL енкодинг по подразбиране се задава в конфига, но при други бази е различно).
Дано съм бил полезен и да нямаш ядове занапред :)

Anonymous said...

На скоро имах същия проблем с изпращане на encoding header по подразбиране.
Това, което ми направи впечатление в този случай е, че ако ползвате html файлове някъде из сайта си няма как да им сложите нужния енкодинг. Meta тага бива override-ван от HTTP хедъра, а header функцията от PHP не се интерпретира. Така че решението е преименуване на .html файловете на .php

vas said...

Допусната вече тази грешка в базата е запазена ценна информация, която изглежда по този начин ВалентиР. Може ли да бъде върната в четим вид?

vas said...
This comment has been removed by the author.
Georgi Mateev said...

ami zavisi dali ne e konvertirana oshte edin pyt sled tova, koeto totalno da e omazalo neshtata. Inache s iconv() ot PHP mojesh da se opitash da q vyrnesh obratno, mislq che CP-1251 e bilo konvertirano v UTF-8 kato gledam kakvo se e poluchilo nakraq, tyi che iconv po obratniq red bi trqbvalo da moje da gi vyrne. Na DB nivo funktiite sa COLLATE() i mislq CONVERT() ili pak ICONV() shte te izlyja. Ako ne se chuvstvate siguren, nai-dobre naemete specialist, dori da struva 100lv na chas za 10 minuti shte moje da vi kaje v konkretniq sluchai dali ednoto ili drugo reshenie shte pomognat!

Georgi Mateev said...

stana, tekstyt koito si kopiral e "Валенти"

vas said...

Благодаря ти много!
Интересното е, че header_а в php-то си беше utf, както и базата. Но чак когато добавих ред, mysql_query("SET CHARACTER SET utf8") в php файла започна коректно да записва кирилицата.

Ангел Ангелов said...

Нямаш си на представа колко ми помогна статията ти, жив да си!!! Оттърва ме от 2 часа нечовешки мъки и чудения :-)