Некорректное отображение текста сайтов на PHP версии 5.6 и выше
07 сентября 2016 года
С введением новых серверов с версией PHP по умолчанию 5.6, к нам стали обращаться клиенты с проблемой отображения сайтов в кодировке WINDOWS-1251. Решение проблемы достаточно простое.
Несмотря на добавление в файл .htaccess кодировки по умолчанию для Apache и указанием кодировки в мета-тегах HTML, проблема не решалась. В заголовках от сервера приходил ответ с UTF-8 и браузер выбирал его.
# Кодировка по умолчанию в файле .htaccess AddDefaultCharset windows—1251 |
# Мета-тег в HTML коде страницы <meta http—equiv=«Content-Type» content=«text/html; charset=windows-1251»/> |
# Заголовок приходящий от сервера Content—Type:text/html; charset=utf—8 |
Проблема появилась из-за изменения кодировки по умолчанию в PHP, начиная с версии 5.6: http://php.net/manual/ru/ini.core.php#ini.default-charset, теперь она жестко задана в UTF-8, а в более ранних версиях просто не задавалась и её можно было легко устанавливать через мета-тег или файл .htaccess
Решением проблемы является добавление строчки в файл .htaccess, которая меняет кодировку на windows-1251:
# Установка кодировки для PHP php_value default_charset windows—1251 |
Данной проблеме подвержены все сайты, кодировка которых отличается от UTF-8 и скрипт в процессе работы не устанавливает параметр default_charset. К нам в основном обращаются с проблемой после установки сайтов на DLE (данная CMS работает в windows-1251).
Если Вы ищете тестовый хостинг для сайта wordpress, то LITE.HOST это то, что Вам нужно. Мы также оказываем услуги по хостингу php и не только. Переходите в раздел «Хостинг» и выбирайте удобный для Вас тариф.
Доброго всем дня. Давайте по порядку)
Вячеслав Любовин, ваш сайт на windows-1251 кодировке когда был создан на какой версии машины? (важно, потому что были доработки, не факт что у вас они есть, потому уточняю).
Цитата |
---|
Андрей Николаев написал: Для Bitrix Env смотри там: Код/etc/php.d/bitrixenv.ini |
Вообще менять тут как минимум неверно) Это общие параметры всей машины. По умолчанию там должно быть:
Код |
---|
mbstring.func_overload = 2 default_charset = UTF-8 |
Переопределять их нужно или в конфигурации httpd этого сайта или в .htaccess файле.
Так же зависит от типа вашей машины: стабильная 7.4.3 или бета 7.4.11. В последней бете правили создание сайтов в кодировке windows-1251.
В версии 7.4.3 можно посмотреть в конфиг файл httpd для сайта по пути /etc/httpd/bx/conf/bx_ext_<ДОМЕН_САЙТА>.conf (сайт не дефолт, для дефолта аналогично), там увидите:
Код |
---|
... php_admin_value mbstring.func_overload 0 php_admin_value default_charset latin ... |
latin — тут ошибка, она исправлена в 7.4.11. Если посмотреть в 7.4.11 тот же конфиг /etc/httpd/bx/conf/bx_ext_<ДОМЕН_САЙТА>.conf
Код |
---|
... php_admin_value mbstring.func_overload 0 php_admin_value default_charset cp1251 ... |
то видим cp1251 — верное значение.
Если посмотрите конфиг файлы httpd для UTF-8 сайтов, то там этих строк не будет. Эти опции будут браться из файла /etc/php.d/bitrixenv.ini.
Кроме того чтобы система обновлений проекта работала после обновления для проекта в кодировке windows-1251 нужно для этого проекта в файле /bitrix/php_interface/dbconn.php добавить строку:
Код |
---|
mb_internal_encoding('windows-1251'); |
Это подправим в следующей бете 7.4.12.
Вячеслав Любовин, то есть вам надо вернуть php.ini к дефолтному состоянию, проверить httpd конфиг сайта, внести правки если нужно и добавить строку в dbconn.php. А параметр mbstring.internal_encoding скорей всего у вас в .htaccess, его закоментировать в этом файле.
Цитата |
---|
Александр Панишев написал: интересно а как быть в ситуации когда на серваке есть сайты и utf 8 и 1251 кодировки default_charset для всех один ведь и получается сайты надо переводить с 1251 на utf-8 |
Александр Панишев, как видите на одной машине могут работать сайты в двух кодировках без проблем, ничего переводить не надо)
Кодировки
версия для печати
Мои сайты сделаны в кодировке windows-1251, включая БД. Возникла необходимость получить файл в кодировке UTF-8 (собираю dump-файл базы). Как оказалось, преобразование из одной кодировки в другую — задача не тривиальная. В этой статье я попытаюсь разложить по полочкам, что к чему. Поле деятельности: Apache/2.2.15 (Win32) PHP/5.3.5. Во внимание беру только две кодировки, UTF-8 и Windows-1251 (aka cp1251), но многое из сказаного относится к преобразованиям в любых кодовых страницах.
Прежде чем хвататься за клаву и «превращать воду в вино»..
часть Рекомендательная
Настройка кодировки PHP-препроцессора (php.ini или .htaccess), параметр default_charset. Он нужен для указания браузеру, в какой кодировке передается ответ (не обязательно html-страница). Это указание передается в заголовках (HTTP headers) ответа. Причем в мета-теге html-страницы может быть другая кодировка или не быть вообще, это отдельная история. Если в заголовках кодировка не указана, нормальный браузер (типа FireFox) пытается сам ее определить по содержимому или по мета-тегу, если он есть.
Аналогичная картина наблюдается для web-сервера Apache. Кодировка ставится директивой addDefaultcharset. Ее можно указывать в конфиге httpd-vhosts.conf (секция <directory>, потребуется перезапуск сервера), или в .htaccess сайта. Зачем нужна отдельная директива Apache? Это очевидно: на PHP свет клином не сошелся, web-сервер может и без него формировать ответы на запросы браузера, вот для таких ответов можно указывать конкретную кодировку.
Приведу несколько примеров получающихся заголовков.
Настройки PHP: windows-1251 (php.ini: default_charset=windows-1251)
HTTP/1.1 200 OK
…
Content-Type: text/html; charset=windows-1251
Настройки Apache: UTF-8 (.htaccess: addDefaultcharset UTF-8)
HTTP/1.1 200 OK
…
Content-Type: text/html; charset=UTF-8
Настройки PHP и Apache: не указана кодировка
HTTP/1.1 200 OK
…
Content-Type: text/html;
часть Функциональная (PHP)
Переключить кодировку PHP-препроцессора на время выполнения скрипта можно так:
ini_set(‘default_charset’,’UTF-8′);
С другой стороны, перед передачей ответа можно явно указать заголовок с кодировкой:
header (‘Content-Type: text/html; charset=windows-1251’);
Кодировка файла скрипта. PHP-файл — это обычный текстовик, все символы в нем кодируются набором байт с учетом установленной кодировки текста. Если в скрипте используются функции типа echo(), fwrite() и т.п., причем текст их параметров записан в этом же скрипте, то здесь так же важна кодировка самого файла скрипта! Пример файла some.php:
<?php echo 'Pусский текст в UTF-8'; ?>
Чтобы в браузере этот текст был действительно в UTF-8, кодировка файла с приведеным кодом должна быть UTF-8. Посмотреть и исправить кодировку файла можно, например, в Notepad++. У меня php-редактор (Blumentals Rapid PHP 2010) так же устанавливает кодировку для новых файлов.
БД, внешние файлы. Если кодировка текста, полученного из таблицы/файла/сайта, отличается от нужной, то придется перекодировать. В этом месте наконец-то можно использовать функции типа iconv(), utf8_encode(), ob_iconv_handler(), mb_output_handler() и т.п., а так же пользовательские функции перекодирования.
Назначение всех этих функций разное. Например, utf8_encode() поддерживает только следующее преобразование: ISO-8859-1 (aka Latin-1) > UTF-8. Две других функции, ob_iconv_handler() и mb_
output_handler(), можно использовать только как callback-функции для буфферизированного вывода (этот момент до меня долго доходил). Пример использования: здесь источником текста является сам скрипт (кодировка файла: UTF-8), настройки web-сервера и PHP не суть важны, в браузере можно переключиться.
echo 'русский текст в UTF-8<BR>'; iconv_set_encoding("internal_encoding", "UTF-8"); //"входящая" кодировка iconv_set_encoding("output_encoding", "WINDOWS-1251");//кодировка перед выдачей из буфера ob_start('ob_iconv_handler'); //назначаем доп. обработчик echo 'русский текст в WINDOWS-1251<BR>'; ob_end_flush();
Результатом в браузере будут две строки, первая будет читаться при выборе кодировки страницы «UTF-8» (см. в настройках браузера), вторая — «Кириллица (Windows-1251)». Это был учебный пример, реальное применение перекодирования «на лету»: нужно вернуть пользователю html-страницу в другой кодировке.
Замечание: если callback-функции в случае ошибки вернут «false» вместо новой строки, вывод информации не остановится и сообщений не будет, получится текст без перекодирования.
В моей задаче (получение UTF8-файла) пришлось применять функцию iconv() к каждой записываемой в файл строке.
Создание файла с нужной кодировкой. Тут вроде особых проблем нет, используем iconv() или сразу пишем в нужной кодировке. Зато есть ньюанс с UTF-8 файлами. Следующий код создает файл с кодировкой UTF-8 (файл скрипта в той же кодировке):
$text="русский текст, english text\nВторая строка"; $file=fopen('some.txt','w+b'); //fwrite($file, pack("CCC",0xEF,0xBB,0xBF)); //Пишем сигнатуру UTF-8 fwrite($file, $text); fclose($file);
Некоторые программы не корректно определяют кодировку полученного файла. Выход: дописывать сигнатуру UTF-8 в начале файла. Для этого нужно раскоментировать строку (3). Что такое «сигнатура UTF-8«, цитата из Википедии:
«Порядок байтов (BOM, сигнатура)
Многие программы Windows (включая Блокнот) добавляют байты 0xEF, 0xBB, 0xBF в начале любого документа, сохраняемого как UTF-8. Это метка порядка байтов Юникода (англ. Byte Order Mark, BOM), также её часто называют сигнатурой (соответственно, UTF-8 и UTF-8 with Signature). По наличию сигнатуры программы могут автоматически определить, является ли файл закодированным в UTF-8, однако файлы с такой сигнатурой могут некорректно обрабатываться старыми программами, в частности xml-анализаторами. Такие редакторы, как Notepad++, Notepad2 и Kate позволяют явно указывать, следует ли добавлять сигнатуру при сохранении UTF-файлов.»
Post Scriptum
Нашел на форумах пользовательскую функцию преобразования windows-1251 <-> UTF-8. Может пригодится когда-нибудь. У меня она работала криво в направлении Win -> UTF. Пофиксил, все работает:)
function Encode($str, $type='w2u'){ // 'u2w' - кодировать из UTF в win // 'w2u' - кодировать из win в UTF $conv=array (); for ($x=128; $x<=143; $x++){ $conv['utf'][]=chr(209).chr($x); $conv['win'][]=chr($x+112); } for ($x=144; $x<=191; $x++){ $conv['utf'][]=chr(208).chr($x); $conv['win'][]=chr($x+48); } $conv['utf'][]=chr(208).chr(129); $conv['win'][]=chr(168); $conv['utf'][]=chr(209).chr(145); $conv['win'][]=chr(184); if ($type=='u2w') return str_replace ($conv['utf'], $conv['win'], $str); elseif ($type=='w2u'){ //return str_replace ($conv['win'], $conv['utf'], $str); //баг $arr=array_combine($conv['win'], $conv['utf']);//корректно работает return strtr($str,$arr); } else return $str; n}
Баг был в
строке (20): функция str_replace() гонит на половине алфавита. Заменяет русскую win-букву на двухбайтное UTF-значение, а потом а этой замене опять заменяет первый байт(!), который в win-1251 так же соответствует букве, на UTF-значение. Причем другая половина алфавита нормально заменяется.
Таблица соответствия Unicode/UTF-8 <-> Windows-1251 (и не только) http://www.utf8-chartable.de
[1oo%, EoF]
Понравилась статья? Расскажите о ней друзьям:
-
#1
default_charset в php.ini
Народ подскажите плз, как в этом параметре (default_charset) прописывать виндовую кодировку. Если koi8-r так скорее всего и пишется, то как прописывать win-1251 или cp1251, и как юникод? И вообще можно ли это делать?
Заранее спасибо
RomikChef
Guest
-
#2
ЧЕГОООО?!
какой еще дефолт чарсет в php.ini ???
-
#3
>И вообще можно ли это делать?
Для php-gtk можно делать.
-
#4
2: RomikChef
; As of 4.0b4, PHP always outputs a character encoding by default in
; the Content-type: header. To disable sending of the charset, simply
; set it to be empty.
;
; PHP’s built-in default is text/html
default_mimetype = «text/html»
default_charset = «windows-1251»
2: hussar
А зачем оно тебе вообще надо? Это только лишь определяет кодировку по умолчанию, если ты сам не сделал этого при помощи META или соотв. хэдера. Т.ч. чем мучать php.ini, выдай любыми подручными средствами php (да хоть echo) META HTTP-EQUIV=»Content-Type» CONTENT=»text/html; charset=windows-1251″ или, для юникода, charset=utf-8.
Apache Manager
Guest
-
#5
Очень рото все , если юзаешь апач, то в файле .htaccess
пропиши вот это
AddDefaultCharset windows-1251 тока в той папке де у тя скрипты лежат =)
-
#6
Ок, спасибо за объяснения Попробую сделать. А надо мне это для того, что я пытаюсь понять в каком месте у меня сбиваются кодировки. Есть программа которая через форму которая передает данные в MS SQL SERVER 2000, данные передаются и даже выводятся обратно, но в самой базе при просмотре таблиц — это нечитаемые кракозябры , и иногда при выводе тоже некоторые кирилические буквы заменяются на латинские. Вот такая проблема и я не могу понят в чем тут дело то-ли в базе то ли в кодировке страницы, то-ли в PHP.
Если кто-то сталкивался с подобной проблемой, буду очень благодарен за совет
Вывод ошибки MySQL будет зависеть от самой сборки, LANG на сервере (+еще некоторое количество всякой фигни) и того, как отображает у тебя text WEB-сервер
Я бы посоветовал начать отладку с установки в .htaccess
параметра
AddDefaultCharset utf-8
В 90% случаев на этом все и заканчивается
Если не поможет, иди по шагам:
- На какой ОС все это работает и какая кодировка в этой ОС по умолчанию
- В какой кодировке живет сервер SQL
- В какой кодировке живет WEB-сервер
Кстати говоря, полученный текст можешь взять, скопировать в буфер и вставить вот сюда: https://www.artlebedev.ru/decoder/ — классная штука, позволяет быстро и просто понять, что происходит
Только копируй прям сразу где увидел, а не из поста выше, например. Символ �
— вполне себе печатный символ UTF-8, кодировать его куда-то еще не получится ) Так случается после вставки кракозябр куда-то, где контент в UTF-8.. они просто преобразуются в обычные UTF-8 символы.
UPD. Я провел эксперимент:
- Взял вашу исходную предполагаемую строчку
mysqli_connect(): (HY000/2002): Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение.
-
Записал ее в файл в кодировке UTF-8:
$ env | grep LANG
LANG=ru_RU.UTF-8$ echo «mysqli_connect(): (HY000/2002): Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение.» > q686819.txt
-
Полученный файл перегнал из UTF-8 в CP-1251 (она же windows-1251)
$ iconv -f utf-8 -t windows-1251 ./q686819.txt
mysqli_connect(): (HY000/2002): ����������� �� �����������, �.�. �������� ��������� ������ ������ �� �����������.
Консоль у меня отображает вывод в UTF-8, но выводимые символы имеют кодировку CP-1251
Отсюда можно сделать следующий полезный для вас вывод: исходный текст ваша сборка MySQL выводит в кодировке windows-1251 (скорее всего потому что собрана под Windows, а там в русской локализации по умолчанию именно windows-1251). А вопросики вы видите потому, что смотрите на символы через призму кодировку utf-8
Решение: либо поменять сборку MySQL на EN, без локализации на русском (возможно там в параметрах можно поменять LANG для ошибок или что-то вроде того — обычно такие пакеты делают мультиязычными, а язык выбирается из параметров ОС), либо ничего не менять и выводить в windows-1251 (как сейчас), но смотреть на это через призму той же кодировки windows-1251, например в .htaccess
AddDefaultCharset windows-1251
Попробуйте, тут нужны эксперименты именно с вашей сборкой и на Windows. К сожалению подробнее я вам не подскажу — пользуюсь Linux и обычными Apache httpd и mariadb из репозиториев. Но направление определенно верное