Предыстория:
Довелось мне не так давно выкладывать некий проект на 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 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).
Выводы:
- Обращайте внимание на пареметры настройки MySQL сервера, — некоторые сервера по умолчанию могут работать с другой кодировкой.
- Если у вас нет возможности настроить MySQL сервер должным образом, можно воспользоваться приёмом, описанным выше.
ссылка на оригинал статьи http://habrahabr.ru/post/176463/
Добавить комментарий