Сразу хочу сказать, что в 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 CHARACTER SET UTF8;
SET NAMES UTF8;
Но, как по мне, всякий лишний запрос к базе — это вообще не комильфо, да и «костыли» в коде фреймворка тоже. Поэтому поначалу я попытался решить вопрос через техподдержку.
Ребята из техподдержки хост-компании рассказали мне, что перенести базу на сервер, сконфигурированный под utf8 они не могут, т.к. все сервера у них работают с одинаковой конфигурацией на cp1251, а менять настройки сервера из-за одного проекта никто не будет, т.к.
изменения коснуться всех баз на этом сервере (я их прекрасно понимаю). Но проблему нужно было как-то решать…
Решение:
Однако, решение на Symfony оказалось довольно изящным, что и послужило поводом к написанию данной статьи.
На помощь мне пришёл крохотный EventListener, который я повесил на событие postConnect.
Для его создания нужно внести в файле app/config/config.yml (если используется YAML) в разделе «services» запись вида:
services: onconnect.listener: class: DevSomeBundleEventListenerOnConnect tags: - { name: doctrine.event_listener, event: postConnect }
Теперь в нашем Bundle, нужно создать файл listener'a, который соответствует заданному в конфигурации выше namespace (DevSomeBundleEventListenerOnConnect).
т.е. Ложим в папку Dev/SomeBundle/EventListener файл OnConnect.php следующего содержания:
<?php
//file src/Dev/SomeBundle/EventListener/OnConnect.php
namespace DevSomeBundleEventListener;
use DoctrineCommonPersistenceEventLifecycleEventArgs;
use DoctrineORMEntityManager;
class OnConnect
{
public function postConnect( $event )
{
$conn = $event->getConnection();
$conn->executeQuery("SET NAMES UTF8");
}
}
В моём случае проблема с кодровкой была полностью устранена, и к тому же, мне не пришлось вставлять какие-либо «хаки» в doctrine или код фреймворка. Механизм прослушивания событий широко применяется в Symfony для решения самых различных задач. В описанном выше примере использовался Symfony 2.2.0 (Standard Edition).
Выводы:
- Обращайте внимание на пареметры настройки MySQL сервера, — некоторые сервера по умолчанию могут работать с другой кодировкой.
- Если у вас нет возможности настроить MySQL сервер должным образом, можно воспользоваться приёмом, описанным выше.
Автор: dimmask
Такая же проблема была при размещении на хостинге. Спасибо за предоставленное решение!
Только записи нэймспейсов надо было при помощи “/” разделять, и название класса в config.yml тоже. Видимо, от версии PHP и Symfony зависит.