Переводим сайт на XML, XSLT и UTF8

Почему XML и XSLT?

Жил был сайт. Обычный самописанный сайт. Пока он был маленький, мало обновлялся и плохо посещался, то особой нужды что-то менять не было. Сайт работал на cp1251, никого не трогал... Но в один прекрасный момент информация, которая скапливалась, как пыль за монитором, вдруг стала нуждаться в структуризации и более грамотном представлении. Нужно было кардинально менять устаревший движок, а если точнее, то прикрутить шаблоны.

Порывшись в закромах памяти и поизучав инет, я обрисовал для себя два типа шаблонизаторов - PHP-зависимые и XSLT.

PHP-зависимые шаблонизаторы — это программы для махинаций над шаблонами произвольного формата, в результате, которых получается рабочий PHP-сценарий с нужным функционалом. Самым ярким представителем таких шаблонизаторов является, конечно же, Smarty. Такие шаблонизаторы отличает очень высокая скорость работы, гибкий синтаксис и полная зависимость от пхп.

XSLT-шаблоны — это XML-файлы, которые содержат правила обработки исходного XML-файла. В результате обработок может получиться текстовый документ любого формата, хоть HTML, хоть тот же пхп. Обработкой таких шаблонов занимается отдельный модуль, при этом тратится достаточно много ресурсов.

Однако, несмотря на сравнительно большие ресурсозатраты, использование XSLT позволяет избавиться от PHP-зависимости и чётко отделить шаблон от данных. Кроме того, и XML, и XSLT стандартизированы, а их поддержка внедрена далеко за пределы пхп. То есть, однажды сформированный шаблон, можно использовать как угодно и где угодно.

Ещё одним немаловажным плюсом XSLT является его полная нетерпимость к опечаткам и структурным ошибкам. То есть, если шаблон рабочий, то он и будет работать, независимо от входных данных. Если же шаблон содержит какую-то ошибку, то вы узнаете об этом сразу.

Взесив все «за» и «против», я рассудил так — XSLT, это удобный, хорошо документированный язык шаблонов, который поддерживается большинством современных браузеров и позволяет вывести обработку данных на совершенно новый уровень.

После ряда экспериментов на локальной машине, решение использовать XSLT было принято окончательно и бесповоротно.

Почему UTF-8?

Изначально сайт прекрасно работал на windows-1251, и менять эту кодировку я не хотел. Да и зачем что-то менять, если и так всё работает?...

В локальных тестах с XML никаких проблем с windows-1251 не наблюдалось. Но костыли не заставили себя долго ждать. При портировании XML на пхп обрисовались некоторые траблы.

Пока код был таким, проблем не было:

Код - Рабочий код обработки XSLT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
// Объект исходного XML-документа
$xml = new DOMDocument(null, 'windows-1251');
$xml->load('test.xml');

// Объект стиля
$xsl = new DOMDocument(null, 'windows-1251');
$xsl->load('test.xsl');  // test.xsl создан в windows-1251

// Создание парсера
$proc = new XSLTProcessor();

// Подключение стиля к парсеру
$proc->importStylesheet($xsl);

// Обработка парсером исходного XML-документа
$parsed = $proc->transformToXml($xml);

// Вывод результирующего кода
echo $parsed;
?>

Но как только исходный XML-документ стал формироваться средствами PHP, то, как по волшебству, нарисовались непонятные ошибки.

Код - Нерабочий код формирования XML-документа
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
//
// Файл в windows-1251
//

// ДОМ
$xml = new DOMDocument('1.0', 'windows-1251');

// Корневой эелемент
$page = $xml->createElement('page');

$xml->appendChild( $page );

// Два элемента
$title   = $page->appendChild( $xml->createElement('title'  ) );
$content = $page->appendChild( $xml->createElement('content') );

// Заголовок
$title->appendChild($xml->createCDATASection('Заголовок'));

// Содержимое
$content->appendChild($xml->createCDATASection('Содержимое'));

echo $xml->saveXML();

// На выходе будет ошибка
?>
Warning:  DOMDocument::saveXML() [domdocument.savexml]: 
     output conversion failed due to conv error, bytes 0xC7 0xE0 0xE3 0xEE ...

Чтобы разобраться, чем не угодили 0xC7 0xE0 0xE3 0xEE, пришлось провести ряд экспериментов. В результате выяснилась одна простая, но очень важная вещь. Кодировка, которая была указана при создании объекта документа, это не исходная, как я наивно думал, а результирующая. То есть, пока строки 'Заголовок' и 'Содержимое' были в windows-1251 (при лобой кодировке DOMDocument), ничего хорошего не было. Но стоило их перевести в UTF-8, как всё работало на ура.

Разобравшись с кодировкой при создании, перерыл всю документацию относительно DOMDocument, в надежде, что как-то можно задать исходную кодировку. В итоге, ничего нового не удалось отыскать.

Вывод, который пришлось сделать, был неутешительным — хочешь работать с DOMDOcument, работай в UTF-8. Кстати, SimpleXML тоже не исключение, его рацион входных данных должен быть исключительно из UTF-8.

Поэтому вопрос о кодировке сайта был решён однозначно — только UTF-8.

Переводим все файлы сайта в UTF-8

Легко сказать "только UTF-8", надо же ещё перевести каким-то образом все исходные файлы из windows-1251 в UTF-8. Казалось бы, почти все редакторы умеют работать с UTF-8, какие тут могут быть проблемы? Однако, проблема образовалась, и вполне реальная. Сайт содержал порядка 50 файлов, размазанных по разным папкам.

Многие скажут: «50 файлов?!… — баран чихнул», мол, большие сайты состоят из тысяч файлов. И действительно, 50 файлов — это двадцать минут работы. Но... у меня закралась мысль, что сие действо, возможно, уже кто-то автоматизировал до меня.

Погуглив просторы интернета, я обнаружил, что программ, которые меня интересовали, выдаётся только две — одна под консоль, вторая под .NET. Причём первая не поддерживала UTF-8, а вторая просто не запускалась — вредоносный фрэймвок не был установлен.

От безысходности и нежелания заниматься пересохранением по методу многократного тыка, пришлось взять в руки Visual Basic и написать нужную программу самому. В результате получилась утилита под названием recoder.

Вооружившись recoder, я перевёл все нужные файлы из windows-1251 в UTF-8 за пару секунд. Казалось, что цель достигнута. Однако, нашлось ещё одно однако... — recoder сработал ровно на 100% и при сохранении файлов добавил сигнатуру UTF-8, т.н. BOM.

Сигнатура BOM — это три специальных байта, которые идут в начале файла и должны сигнализировать, что сам файл содержит UTF-8. Но проблема в том, что BOM носит опциональный характер и может быть, а может и не быть. При этом PHP знать не знает, что это за зверь, и как с ним работать. Поэтому мой проект оказался в плачевном состоянии — при подключении любого файла вылазила никому не нужная сигнатура UTF-8.

Для решения проблемы с BOM, пришлось опять засучить рукава и писать ещё одну утилиту. Так родилась программа bom-remover. Возможно, подобных программ больше, чем перекодировщиков, но, как говорится, гулять так гулять!

И вот уже после шлифовки bom-remover, сайт формально был переведён на UTF-8. Чтобы окончательно попрощаться с windows-1251, надо было просто сменить локаль и выставить кодировки.

Настройки для UTF-8 получились такими:

Код - файл общих настроек
1
2
3
4
5
6
7
8
9
10
<?php
// Кодировка страницы
header('Content-Type: text/html; charset=UTF-8', true);

// Локаль
setlocale(LC_ALL, 'ru_RU.UTF-8', 'Russian_Russia.65001', 'UTF-8');

// Кодировка на соединение с базой данных
mysql_query('SET names "UTF8"');
?>

Заключение

Перевод сайта на XML, XSLT и UTF-8, это достаточно большая работа. Чтобы с ней справиться, пришлось разбить все действия на этапы:

  1. Создание макета на XHTML
  2. Проработка структуры исходного XML-документа
  3. Создание XSL-стиля на основе XHTML-макета и исходного XML-документа
  4. Проработка PHP-функционала для формирования исходного XML-документа через движок
  5. Перевод исходных файлов на UTF-8 (удаление BOM)
  6. Изменение настроек движка

Первые три этапа подробно расписаны в статье «XSLT первый шаг».

Кусочек четвёртого этапа был приведён здесь.

Шестой этап подробно расписан в статье «Локали и кодировки».

По вопросам кодировки базы данных, если вдруг возникнут проблемы, есть статья «Кодировки в MySQL».