Решение проблемы с кодировкой данных из MySQL в Symfony

от автора

Сразу хочу сказать, что в Symfony и Doctrine я новичок и с подобного рода проблемой именно при использовании Symfony столкнулся впервые, но думаю, что и мой опыт может быть кому-то полезен при решении аналогичных или схожих задач.

Предыстория:

Довелось мне не так давно выкладывать некий проект на Symfony2 на хост площадку, но, как это довольно часто бывает, на живом сервере приложение работать отказалось, и включив debug, я увидел уведомление примерно следующего плана:

Twig_Error_Runtime: An exception has been thrown during the rendering of a template
(«Warning: htmlspecialchars() [function.htmlspecialchars]: Invalid multibyte sequence in argument in
/…/app/cache/prod/classes.php line …») in "…" at line …


Причина этой ошибки кроется в том, что Twig по умолчанию экранирует всякий вывод, в том числе и с помощью функции htmlspecialchars(), которая в данном случае спотыкается о некую Invalid multibyte sequence. И решив, что было бы неплохо посмотреть эту самую Invalid multibyte sequence, я пропустил в шаблоне вывод соответствующих переменных через фильтр raw, примерно вот так:
{{ sometext|raw }}
Выяснилось, что кириллические текстовые данные из базы почему-то приходят в кодировке cp1251, хотя кодировка импортированной базы, таблиц и соотв. полей, из которых взяты значения была utf8. В phpMyAdmin, который был мне предоставлен хостером, на вкладке «Variables» моё внимание привлекли следующие значения параметров конфигурации mysql сервера:

 init connect      SET NAMES cp1251 collation database      cp1251_general_ci collation      servercp1251_general_ci ... 

Проблема:

Очевидно, что причина была именно в init connect, который при соединении выполнял SET NAMES cp1251, потому все значения передавались в приложение как cp1251.

SET NAMES

Напомню, что SET NAMES определяет кодировку в которой клиент отправляет данные на сервер а также кодировку, в которой сервер отправляет обратный ответ клиенту.

Подобного рода проблемы обычно решаются довольно просто — сразу после коннекта к базе в своём приложении выполняем запрос или группу запросов типа:
SET CHARACTER SET UTF8; SET NAMES UTF8;
Но, как по мне, всякий лишний запрос к базе — это вообще не комильфо, да и «костыли» в коде фреймворка тоже. Поэтому поначалу я попытался решить вопрос через техподдержку.
Ребята из техподдержки хост-компании рассказали мне, что перенести базу на сервер, сконфигурированный под utf8 они не могут, т.к. все сервера у них работают с одинаковой конфигурацией на cp1251, а менять настройки сервера из-за одного проекта никто не будет, т.к.
изменения коснуться всех баз на этом сервере (я их прекрасно понимаю). Но проблему нужно было как-то решать…

Решение:

Однако, решение на Symfony оказалось довольно изящным, что и послужило поводом к написанию данной статьи.
На помощь мне пришёл крохотный EventListener, который я повесил на событие postConnect.

Для его создания нужно внести в файле app/config/config.yml (если используется YAML) в разделе «services» запись вида:

services:     onconnect.listener:         class: Dev\SomeBundle\EventListener\OnConnect         tags:              - { name: doctrine.event_listener, event: postConnect }

Теперь в нашем Bundle, нужно создать файл listener’a, который соответствует заданному в конфигурации выше namespace (Dev\SomeBundle\EventListener\OnConnect).
т.е. Ложим в папку Dev/SomeBundle/EventListener файл OnConnect.php следующего содержания:

<?php //file src/Dev/SomeBundle/EventListener/OnConnect.php  namespace Dev\SomeBundle\EventListener; use Doctrine\Common\Persistence\Event\LifecycleEventArgs; use Doctrine\ORM\EntityManager;  class OnConnect { 	public function postConnect( $event ) 	{ 		$conn = $event->getConnection(); 		$conn->executeQuery("SET NAMES UTF8"); 	} } 

В моём случае проблема с кодровкой была полностью устранена, и к тому же, мне не пришлось вставлять какие-либо «хаки» в doctrine или код фреймворка. Механизм прослушивания событий широко применяется в Symfony для решения самых различных задач. В описанном выше примере использовался Symfony 2.2.0 (Standard Edition).

Выводы:

  1. Обращайте внимание на пареметры настройки MySQL сервера, — некоторые сервера по умолчанию могут работать с другой кодировкой.
  2. Если у вас нет возможности настроить MySQL сервер должным образом, можно воспользоваться приёмом, описанным выше.

ссылка на оригинал статьи http://habrahabr.ru/post/176463/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *