Кодировки MySQL и бинарные данные в полях типа BLOB и ТEXT — проблема из-за которой я не спал целую ночь

Идейно нелюбимые мной «Битрикс» и Joomla намного лучше любой студийной CMS. Исключение наступает лишь в тот момент, когда студийная CMS становится популярным opensource-продуктом, в котором ковыряются разные люди никак не связанные со студией. Потому что только в этом случае вырастает сообщество, проходящее строем по всем типовым граблям, набивающее шишки и заполняющее интернет ответами на типовые вопросы и рассказами о типовых проблемах. Вот таким публично испробованным продуктом уже можно смело пользоваться. Иначе — вечная зависимость от студии.

Лирическое вступление посвящается студийной CMS — S.Builder 3.7, чья админка работает только в Internet Explorer. На форуме разработчики на многие вопросы отвечают примерно так: «пришлите нам FTP-доступ к вашему хосту, мы проблему поправим». То есть пользы от форума не сильно больше, чем от закрытой тикет-системы. При переносе сайта с одного хоста на другой я упёрся лбом в проблему с кодировками. Увидев на странице часть русского текста и часть «кракозябров» привычно вздохнул: проблема типовая, сейчас втисну куда-нибудь запрос SET NAMES cp1251 и всё заработает. Но, не тут-то было.

Скудная поддержка — не главный грех упомянутой CMS, лютый ад открывается в тот момент, когда обнаруживается, что S.Builder контент страниц хранит в базе (это ок), но в бинарном виде (в полях типа BLOB в MySQL). Зачем исконно plain-текстовый HTML-код паковать в бинарные данные — я не знаю.

Но дамп данных полон вот такой вот фигни:

INSERT INTO `binn_menu` (`mn_id`, `mn_name`, `mn_text`, `mn_url`, `mn_title`, `mn_target`, `mn_type`, `mn_prop`, `mn_visible`, `mn_left`, `mn_right`, `mn_level`, `mn_menu_id`) VALUES
(16, 'Услуги', 0xd09dd090d0a8d09820d0a3d0a1d09bd0a3d093d098, '/ru/services/index.php', 'Наши клиенты', '', 0, '', '', 14, 435, 1, 81);

Поле mn_text имеет тип TEXT. Вообще у таблицы такая вот структура (выдержка из того же дампа сделанного phpMyAdmin`ом):

DROP TABLE IF EXISTS `binn_menu`;
CREATE TABLE IF NOT EXISTS `binn_menu` (
`mn_id` int(11) NOT NULL auto_increment,
`mn_name` varchar(100) collate cp1251_bin NOT NULL default '',
`mn_text` text collate cp1251_bin NOT NULL,
`mn_url` varchar(255) collate cp1251_bin NOT NULL default '',
`mn_title` varchar(255) collate cp1251_bin NOT NULL default '',
`mn_target` varchar(20) collate cp1251_bin NOT NULL default '',
`mn_type` tinyint(1) NOT NULL default '0',
`mn_prop` varchar(255) collate cp1251_bin NOT NULL default '',
`mn_visible` char(1) collate cp1251_bin NOT NULL default '0',
`mn_left` int(11) NOT NULL default '0',
`mn_right` int(11) NOT NULL default '0',
`mn_level` smallint(3) NOT NULL default '0',
`mn_menu_id` int(11) NOT NULL default '0',
PRIMARY KEY (`mn_id`),
KEY `mn_left` (`mn_left`),
KEY `mn_right` (`mn_right`),
KEY `mn_level` (`mn_level`),
KEY `mn_menu_id` (`mn_menu_id`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 COLLATE=cp1251_bin AUTO_INCREMENT=666;

Заливая такой дамп на другой хост я упорно получал «кракозябры» в полях типа TEXT и BLOB, и нормальные данные — в остальных полях. И тут как бы становится понятно, что SET NAMES cp1251 — не поможет. Даже не потому, что программный код S.Builder закрыт Zend`ом, а потому — что получается в одном дампе живут данные в разных кодировках.

Мучился я долго (целую ночь) и ругался под утро уже достаточно громко, но в итоге спас вот какой ход: я всё, что можно, начал обрабатывать при заливке дампа в юникоде (предположив, что, может быть, при экспорте/импорте дампа установленные кодировки в таблицах как-то влияют на текстовые данные, но совсем никак — на бинарные).

Итак, в дампе надо везде выпилить cp1251, заменив её на utf8, на примере выше упомянутой таблицы это будет выглядеть так:

DROP TABLE IF EXISTS `binn_menu`;
CREATE TABLE IF NOT EXISTS `binn_menu` (
`mn_id` int(11) NOT NULL auto_increment,
`mn_name` varchar(100) collate utf8_bin NOT NULL default '',
`mn_text` text collate utf8_bin NOT NULL,
`mn_url` varchar(255) collate utf8_bin NOT NULL default '',
`mn_title` varchar(255) collate utf8_bin NOT NULL default '',
`mn_target` varchar(20) collate utf8_bin NOT NULL default '',
`mn_type` tinyint(1) NOT NULL default '0',
`mn_prop` varchar(255) collate utf8_bin NOT NULL default '',
`mn_visible` char(1) collate utf8_bin NOT NULL default '0',
`mn_left` int(11) NOT NULL default '0',
`mn_right` int(11) NOT NULL default '0',
`mn_level` smallint(3) NOT NULL default '0',
`mn_menu_id` int(11) NOT NULL default '0',
PRIMARY KEY (`mn_id`),
KEY `mn_left` (`mn_left`),
KEY `mn_right` (`mn_right`),
KEY `mn_level` (`mn_level`),
KEY `mn_menu_id` (`mn_menu_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=666 ;

После таких решительных замен файл надо сохранить в UTF-8 (с BOM). Разобраться с режимами кодировки под Windows вам поможет, например, Notepad++, который, кстати, один из немногих справляется с открытием дампов на 30-50 мегабайт (а под Linux — поможет консольный iconv или convert)

В Notepad++ сохраняйте преобразовывайте файл вот во что:

UTF-8 в Notepad++

Затем надо файл сохранить и этот файл импортировать в phpMyAdmin тоже с настройкой кодировки utf8:

Импорт дампа с двоичными данными в UTF-8

И, знаете, после этого всё начинает работать.

Притом, что S.Builder 3.7 выводит данные в браузер в кодировке windows-1251, и притом что в настройках в файле /cms/admin/config.ini.php (где задаются реквизиты доступа к базе данных) есть некий мифический параметр $binn_utf8 = false; (установленный у меня сейчас именно в false).

Заглядывать глубже в ситуацию со старой и закрытой CMS — нет желания и возможности. Заработала — и чёрт с ней. Справедливости ради отмечу, что S.Builder существует уже в версии 4 (про ней, по крайней мере, что-то пишут на форуме). Может там всё хорошо (текстовые данные хранятся в текстовом виде). Но своё неизменное отношение к студийным CMS — я уже озвучил в начале поста.

Leave a Reply

*