Обнаружение кодирования и сделать все UTF-8

Я читаю много текстов из различных RSS-каналов и вставляю их в свою базу данных.

конечно, в лентах используется несколько различных кодировок символов, например UTF-8 и ISO-8859-1.

к сожалению, иногда возникают проблемы с кодировками текстов. Пример:

  1. "ß" в "Fußball"должен выглядеть так в моей базе данных: "Ÿ". Если это "Ÿ", он отображается правильно.

  2. иногда "ß" в "Fußball"выглядит так в моей базе данных: "ß". Тогда, конечно, он отображается неправильно.

  3. в других случаях "ß" сохраняется как "ß" - так, без каких-либо изменений. Затем он также отображается неправильно.

что я могу сделать, чтобы избежать случаев 2 и 3?

как я могу сделать все то же кодирование, предпочтительно UTF-8? Когда я должен использовать utf8_encode(), когда я должен использовать utf8_decode() (понятно, что эффект есть, но когда я должен использовать функции?) и когда я должен ничего не делать с вводом?

вы можете помочь мне и сказать мне, как сделать все то же самое кодирование? Возможно, с функцией mb_detect_encoding()? Могу ли я написать функцию для этого? Так что мои проблемы:

  1. как узнать, какую кодировку использует текст?
  2. как преобразовать его в UTF-8-независимо от старой кодировки?

будет ли такая функция работа?

function correct_encoding($text) {
    $current_encoding = mb_detect_encoding($text, 'auto');
    $text = iconv($current_encoding, 'UTF-8', $text);
    return $text;
}

Я проверил его, но он не работает. Что с ним не так?

24 ответов


если вы примените utf8_encode() к уже строке UTF8 он вернет искаженный вывод UTF8.

Я сделал функцию, которая решает все эти вопросы. Его называют Encoding::toUTF8().

вам не нужно знать, что кодировка строк. Это может быть Latin1 (iso 8859-1), Windows-1252 или UTF8, или строка может иметь их сочетание. Encoding::toUTF8() преобразует все в UTF8.

Я сделал это, потому что служба давала мне поток данных все перепутал, смешивая Utf8 и латинских типа 1 в той же строке.

использование:

require_once('Encoding.php'); 
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);

$latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);

скачать:

https://github.com/neitanod/forceutf8

обновление:

Я включил другую функцию, Encoding::fixUFT8(), который исправит каждую строку UTF8, которая выглядит искаженной.

использование:

require_once('Encoding.php'); 
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::fixUTF8($garbled_utf8_string);

примеры:

echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");

вывод:

Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football

Update: я преобразовал функцию (forceUTF8) в семейство статических функций в классе с именем Encoding. Новая функция Encoding::toUTF8().


сначала вам нужно определить, какая кодировка была использована. Поскольку вы анализируете RSS-каналы (возможно, через HTTP), вы должны прочитать кодировку из на Content-Type поле заголовка HTTP. Если его нет, прочитайте кодировку из на инструкция по обработке XML. Если это тоже отсутствует,используйте UTF-8, как определено в спецификации.


редактировать вот что я наверное, сделал бы:

Я хотел бы использовать cURL для отправки и получения ответа. Это позволяет вам установить определенные поля заголовка и получить заголовок ответа. После получения ответа вы должны проанализировать HTTP-ответ и разделить его на заголовок и тело. Затем заголовок должен содержать Content-Type поле заголовка, содержащее тип MIME и (надеюсь)charset параметр с кодировкой / кодировкой тоже. Если нет, мы проанализируем XML PI на наличие encoding атрибут и получить кодировку оттуда. Если это также отсутствует, спецификации XML определяют использование UTF-8 в качестве кодировки.

$url = 'http://www.lr-online.de/storage/rss/rss/sport.xml';

$accept = array(
    'type' => array('application/rss+xml', 'application/xml', 'application/rdf+xml', 'text/xml'),
    'charset' => array_diff(mb_list_encodings(), array('pass', 'auto', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit'))
);
$header = array(
    'Accept: '.implode(', ', $accept['type']),
    'Accept-Charset: '.implode(', ', $accept['charset']),
);
$encoding = null;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($curl);
if (!$response) {
    // error fetching the response
} else {
    $offset = strpos($response, "\r\n\r\n");
    $header = substr($response, 0, $offset);
    if (!$header || !preg_match('/^Content-Type:\s+([^;]+)(?:;\s*charset=(.*))?/im', $header, $match)) {
        // error parsing the response
    } else {
        if (!in_array(strtolower($match[1]), array_map('strtolower', $accept['type']))) {
            // type not accepted
        }
        $encoding = trim($match[2], '"\'');
    }
    if (!$encoding) {
        $body = substr($response, $offset + 4);
        if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $body, $match)) {
            $encoding = trim($match[1], '"\'');
        }
    }
    if (!$encoding) {
        $encoding = 'utf-8';
    } else {
        if (!in_array($encoding, array_map('strtolower', $accept['charset']))) {
            // encoding not accepted
        }
        if ($encoding != 'utf-8') {
            $body = mb_convert_encoding($body, 'utf-8', $encoding);
        }
    }
    $simpleXML = simplexml_load_string($body, null, LIBXML_NOERROR);
    if (!$simpleXML) {
        // parse error
    } else {
        echo $simpleXML->asXML();
    }
}

обнаружение кодировки трудно.

mb_detect_encoding работает, угадывая, на основе ряда кандидатов, которые вы передаете. В некоторых кодировках некоторые байтовые последовательности недопустимы, поэтому он может различать различные кандидаты. К сожалению, существует множество кодировок, в которых действительны одни и те же байты (но разные). В этих случаях нет способа определить кодировку; вы можете реализовать свою собственную логику, чтобы сделать догадки в этих случаях. Например, данные с японского сайта, скорее всего, будет японская кодировка.

пока вы имеете дело только с западноевропейскими языками, три основные кодировки для рассмотрения -utf-8, iso-8859-1 и cp-1252. Поскольку это значения по умолчанию для многих платформ, они также, скорее всего, будут сообщены неправильно. Например. если люди используют разные кодировки, они, вероятно, будут откровенны об этом, так как иначе их программное обеспечение будет ломаться очень часто. Поэтому, лучше доверять провайдер, если кодировка не сообщается как одна из этих трех. Вы все равно должны дважды проверить, что он действительно действителен, используя mb_check_encoding (заметим, что действительный - это не то же самое как будучи - тот же вход может быть действителен для многих кодировок). Если это один из них, вы можете использовать mb_detect_encoding чтобы различать их. К счастью, это довольно детерминировано; вам просто нужно использовать правильную последовательность обнаружения, которая UTF-8,ISO-8859-1,WINDOWS-1252.

как только вы обнаружите кодирование вам нужно преобразовать его во внутреннее представление (UTF-8 это единственный разумный выбор). Функция utf8_encode преобразование ISO-8859-1 to UTF-8, поэтому он может использоваться только для данного типа ввода. Для других кодировок, используйте mb_convert_encoding.


в этом cheatsheet перечислены некоторые общие предостережения, связанные с обработкой UTF-8 в PHP: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

эта функция обнаружения многобайтовых символов в строке также может оказаться полезной (источник):


function detectUTF8($string)
{
    return preg_match('%(?:
        [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
        |\xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
        |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
        |\xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
        |\xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
        |[\xF1-\xF3][\x80-\xBF]{3}         # planes 4-15
        |\xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
        )+%xs', 
    $string);
}

A действительно хороший способ реализации isUTF8 - функция может быть найдена на php.net:

function isUTF8($string) {
    return (utf8_encode(utf8_decode($string)) == $string);
}

немного выше, вы сказали, что" ß "должен отображаться как" Ÿ " в вашей базе данных.

Это наверное потому, что вы используете базу данных с латинских типа 1 кодировка или возможно, ваш PHP-MySQL связи установлен неправильно, это, что PHP считает, что ваш MySQL устанавливается, чтобы использовать UTF-8, поэтому он отправляет данные в кодировке utf8, а ваш MySQL верит, что PHP отправка данных, закодированных в кодировке ISO-8859-1, так что может еще раз попробовать закодировать отправил данные как UTF-8, в результате такого рода беда.

взгляните на это, может помочь вам: http://php.net/manual/en/function.mysql-set-charset.php


ваша кодировка выглядит так, как будто вы закодированы в UTF-8 два раза, то есть из какой-то другой кодировки в UTF-8, и снова в UTF-8. Как если бы у вас был iso-8859-1, преобразованный из iso-8859-1 в utf-8, и обработал новую строку как iso-8859-1 для другого преобразования в UTF-8.

вот некоторый псевдокод того, что вы сделали:

$inputstring = getFromUser();
$utf8string = iconv($current_encoding, 'utf-8', $inputstring);
$flawedstring = iconv($current_encoding, 'utf-8', $utf8string);

вы должны попробовать:

  1. обнаружение кодировки с помощью mb_detect_encoding() или все, что вы хотите использовать
  2. если это UTF-8, преобразовать в iso-8859-1 и повторить Шаг 1
  3. наконец, преобразовать обратно в UTF-8

это предполагает, что в" среднем " преобразовании вы использовали iso-8859-1. Если вы использовали windows-1252, то конвертируйте в windows-1252 (latin1). Исходная исходная кодировка не важна; та, которую вы использовали в ошибочном, втором преобразовании.

Это моя догадка о том, что произошло; очень мало что еще вы могли бы сделать, чтобы получить четыре байта вместо одного расширенный байт ASCII.

немецкий язык также использует iso-8859-2 и windows-1250 (latin2).


вам нужно проверить кодировку на входе, так как ответы могут кодироваться с разными кодировками.
Я заставляю все содержимое отправляться в UTF-8, выполняя обнаружение и перевод, используя следующую функцию:

function fixRequestCharset()
{
  $ref = array( &$_GET, &$_POST, &$_REQUEST );
  foreach ( $ref as &$var )
  {
    foreach ( $var as $key => $val )
    {
      $encoding = mb_detect_encoding( $var[ $key ], mb_detect_order(), true );
      if ( !$encoding ) continue;
      if ( strcasecmp( $encoding, 'UTF-8' ) != 0 )
      {
        $encoding = iconv( $encoding, 'UTF-8', $var[ $key ] );
        if ( $encoding === false ) continue;
        $var[ $key ] = $encoding;
      }
    }
  }
}

эта процедура превратит все переменные PHP, поступающие с удаленного хоста, в UTF-8.
или игнорировать значение, если кодировка не может быть обнаружена или преобразована.
Вы можете настроить его под свои нужды.
просто вызовите его перед использованием переменная.


самое интересное о mb_detect_encoding и mb_convert_encoding является ли порядок кодировок, которые вы предлагаете, имеет значение:

// $input is actually UTF-8

mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8");
// ISO-8859-9 (WRONG!)

mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9");
// UTF-8 (OK)

поэтому вы можете использовать определенный порядок при указании ожидаемых кодировок. Тем не менее, имейте в виду, что это не является надежным.


Это просто: когда вы получаете что-то, что не UTF8, вы должны закодировать это в utf8.

Итак, когда вы получаете определенный канал, который является ISO-8859-1, проанализируйте его через utf8_encode.

однако, если вы получаете канал UTF8, вам ничего не нужно делать.


разработка кодировки символов RSS-каналов, кажется,сложные. Даже обычные веб-страницы часто пропускают или лгут об их кодировке.

таким образом, вы можете попытаться использовать правильный способ обнаружения кодировки, а затем вернуться к какой-то форме автоматического обнаружения (угадывания).


Я знаю, что это более старый вопрос, но я считаю, что полезный ответ никогда не повредит. У меня были проблемы с моей кодировкой между настольным приложением, SQLite и переменными GET/POST. Некоторые из них будут в UTF-8, некоторые будут в ASCII, и в основном все будет испорчено, когда иностранные персонажи будут вовлечены.

вот мое решение. Он очищает ваш GET/POST / REQUEST (я опустил куки, но вы можете добавить их, если хотите) на каждой загрузке страницы перед обработкой. Он хорошо работает в заголовок. PHP будет выдавать предупреждения, если он не может автоматически обнаружить исходную кодировку, поэтому эти предупреждения подавляются с помощью @ ' s.

//Convert everything in our vars to UTF-8 for playing nice with the database...
//Use some auto detection here to help us not double-encode...
//Suppress possible warnings with @'s for when encoding cannot be detected
try
{
    $process = array(&$_GET, &$_POST, &$_REQUEST);
    while (list($key, $val) = each($process)) {
        foreach ($val as $k => $v) {
            unset($process[$key][$k]);
            if (is_array($v)) {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = $v;
                $process[] = &$process[$key][@mb_convert_encoding($k,'UTF-8','auto')];
            } else {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = @mb_convert_encoding($v,'UTF-8','auto');
            }
        }
    }
    unset($process);
}
catch(Exception $ex){}

я проверял решения для кодирования с возрастом, и эта страница, вероятно, является заключением лет поиска! Я проверил некоторые из предложений, которые вы упомянули, и вот мои заметки:

Это моя тестовая строка:

это строка" wròng wrìtten "bùt I nèed to pù 'sòme' special chàrs, чтобы увидеть thèm, convertèd by fùnctìon!! и это все!

Я делаю вставку, чтобы сохранить эту строку в БД в поле, которое установлено как utf8_general_ci

кодировка моей страницы UTF-8

если я сделаю вставку просто так, в моей БД у меня есть некоторые символы, вероятно, с Марса... поэтому мне нужно преобразовать их в какой-то "нормальный" UTF-8. Я пытался!--2--> но все же чужие символы вторгались в мою базу данных...

поэтому я попытался использовать функцию forceUTF8 опубликовано на номер 8, но на DB сохраненная строка выглядит так:

это строка" wrÃ2ng wrÃtten "bÃ1t I nÃed для pÃ1'sÃ2me специальный chà rs, чтобы увидеть thÃm, convertÃd по fÃ1nctÃon!! и это все!

Итак, собирая еще несколько инфос на этой странице и объединяя их с другими инфос на других страницах, я решил свою проблему с этим решением:

$finallyIDidIt = mb_convert_encoding(
  $string,
  mysql_client_encoding($resourceID),
  mb_detect_encoding($string)
);

сейчас в моей базе данных у меня есть строка с правильной кодировкой.

Примечание: Только обратите внимание, чтобы заботиться о is on function mysql_client_encoding! Вы должны быть подключены к БД, потому что эта функция нужна как идентификатор ресурса параметр.

но хорошо, я просто делаю это перекодирование перед моей вставкой, поэтому для меня это не проблема.

Я надеюсь, что это поможет кому-то, как эта страница помогла мне!

спасибо всем!

Мауро


php.net/mb_detect_encoding

echo mb_detect_encoding($str, "auto");

или

echo mb_detect_encoding($str, "UTF-8, ASCII, ISO-8859-1");

Я действительно не знаю, каковы результаты, но я бы предложил вам просто взять некоторые из ваших каналов с разными кодировками и попробовать, если mb_detect_encoding работает или нет.

обновление
авто-это сокращение от "в ASCII,стандарт JIS,кодировка UTF-8,СКП-Джей-Пи,кодировка sjis". он возвращает обнаруженную кодировку, которую можно использовать для преобразования строки в utf-8 с помощью iconv.

<?php
function convertToUTF8($str) {
    $enc = mb_detect_encoding($str);

    if ($enc && $enc != 'UTF-8') {
        return iconv($enc, 'UTF-8', $str);
    } else {
        return $str;
    }
}
?>

Я не протестировал, так что никаких гарантий. а может есть более простой способ.


@harpax, который работал на меня. В моем случае этого достаточно:

if (isUTF8($str)) { 
    echo $str; 
}
else
{
    echo iconv("ISO-8859-1", "UTF-8//TRANSLIT", $str);
}

после сортировки ваших PHP-скриптов не забудьте сообщить mysql, какую кодировку вы передаете и хотели бы recceive.

пример: набор символов utf8

передача данных utf8 в таблице латинских типа 1 в латинских типа 1 ввода/вывода сессии дает эти неприятные birdfeets. Я вижу это каждый день в магазинах интернет-магазина. Задняя и четвертая могут показаться верными. Но phpmyadmin покажет правду. Сообщая mysql, какую кодировку вы передаете, он будет обрабатывать преобразование данных mysql для тебя.

Как восстановить существующие скремблированные данные mysql-еще один поток для обсуждения. :)


эта версия для немецкого языка, но вы можете изменить $CHARSETS и $TESTCHARS

class CharsetDetector
{
private static $CHARSETS = array(
"ISO_8859-1",
"ISO_8859-15",
"CP850"
);
private static $TESTCHARS = array(
"€",
"ä",
"Ä",
"ö",
"Ö",
"ü",
"Ü",
"ß"
);
public static function convert($string)
{
    return self::__iconv($string, self::getCharset($string));
}
public static function getCharset($string)
{
    $normalized = self::__normalize($string);
    if(!strlen($normalized))return "UTF-8";
    $best = "UTF-8";
    $charcountbest = 0;
    foreach (self::$CHARSETS as $charset) {
        $str = self::__iconv($normalized, $charset);
        $charcount = 0;
        $stop   = mb_strlen( $str, "UTF-8");

        for( $idx = 0; $idx < $stop; $idx++)
        {
            $char = mb_substr( $str, $idx, 1, "UTF-8");
            foreach (self::$TESTCHARS as $testchar) {

                if($char == $testchar)
                {

                    $charcount++;
                    break;
                }
            }
        }
        if($charcount>$charcountbest)
        {
            $charcountbest=$charcount;
            $best=$charset;
        }
        //echo $text."<br />";
    }
    return $best;
}
private static function __normalize($str)
{

$len = strlen($str);
$ret = "";
for($i = 0; $i < $len; $i++){
    $c = ord($str[$i]);
    if ($c > 128) {
        if (($c > 247)) $ret .=$str[$i];
        elseif ($c > 239) $bytes = 4;
        elseif ($c > 223) $bytes = 3;
        elseif ($c > 191) $bytes = 2;
        else $ret .=$str[$i];
        if (($i + $bytes) > $len) $ret .=$str[$i];
        $ret2=$str[$i];
        while ($bytes > 1) {
            $i++;
            $b = ord($str[$i]);
            if ($b < 128 || $b > 191) {$ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;}
            else $ret2.=$str[$i];
            $bytes--;
        }
    }
}
return $ret; 
}
private static function __iconv($string, $charset)
{
    return iconv ( $charset, "UTF-8" , $string );
}
}


получить кодировку из заголовков и преобразовать ее в utf-8.

$post_url='http://website.domain';

/// Get headers ////////////////////////////////////////////////////////////
function get_headers_curl($url) 
{ 
    $ch = curl_init(); 

    curl_setopt($ch, CURLOPT_URL,            $url); 
    curl_setopt($ch, CURLOPT_HEADER,         true); 
    curl_setopt($ch, CURLOPT_NOBODY,         true); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_TIMEOUT,        15); 

    $r = curl_exec($ch); 
    return $r; 
}
$the_header = get_headers_curl($post_url);
/// check for redirect /////////////////////////////////////////////////
if (preg_match("/Location:/i", $the_header)) {
    $arr = explode('Location:', $the_header);
    $location = $arr[1];

    $location=explode(chr(10), $location);
    $location = $location[0];

$the_header = get_headers_curl(trim($location));
}
/// Get charset /////////////////////////////////////////////////////////////////////
if (preg_match("/charset=/i", $the_header)) {
    $arr = explode('charset=', $the_header);
    $charset = $arr[1];

    $charset=explode(chr(10), $charset);
    $charset = $charset[0];
    }
///////////////////////////////////////////////////////////////////////////////
// echo $charset;

if($charset && $charset!='UTF-8') { $html = iconv($charset, "UTF-8", $html); }

Ÿ это Mojibake для ß. В вашей базе данных может быть hex

DF if the column is "latin1",
C39F if the column is utf8 -- OR -- it is latin1, but "double-encoded"
C383C5B8 if double-encoded into a utf8 column

вы должны не используйте любые функции кодирования / декодирования в PHP; вместо этого вы должны правильно настроить базу данных и соединение с ней.

Если MySQL участвует, см.: проблема с символами utf8; то, что я вижу, не то, что я сохранил


Я нахожу решение здесь http://deer.org.ua/2009/10/06/1/

class Encoding
{
    /**
     * http://deer.org.ua/2009/10/06/1/
     * @param $string
     * @return null
     */
    public static function detect_encoding($string)
    {
        static $list = ['utf-8', 'windows-1251'];

        foreach ($list as $item) {
            try {
                $sample = iconv($item, $item, $string);
            } catch (\Exception $e) {
                continue;
            }
            if (md5($sample) == md5($string)) {
                return $item;
            }
        }
        return null;
    }
}

$content = file_get_contents($file['tmp_name']);
$encoding = Encoding::detect_encoding($content);
if ($encoding != 'utf-8') {
    $result = iconv($encoding, 'utf-8', $content);
} else {
    $result = $content;
}

думаю, что @ плохое решение, и внести некоторые изменения в решение из deer.org.ua;


самый проголосовавший ответ не работает. Вот мой и надеюсь, что это поможет.

function toUTF8($raw) {
    try{
        return mb_convert_encoding($raw, "UTF-8", "auto"); 
    }catch(\Exception $e){
        return mb_convert_encoding($raw, "UTF-8", "GBK"); 
    }
}

когда вы пытаетесь обрабатывать несколько языков, таких как японский и корейский, вы можете попасть в беду. mb_convert_encoding с параметром "auto" не работает хорошо. Установка mb_detect_order ('ASCII, UTF-8,JIS,EUC-JP,SJIS,EUC-KR, UHC') не помогает, так как он будет обнаруживать EUC-* неправильно.

Я пришел к выводу, что пока входные строки поступают из HTML, он должен использовать 'charset' в мета-элементе. Я использую простой парсер HTML DOM потому что он поддерживает недопустимый HTML.

в ниже фрагмент извлекает элемент заголовка из веб-страницы. Если вы хотите преобразовать всю страницу, вы можете удалить некоторые строки.

<?php
require_once 'simple_html_dom.php';

echo convert_title_to_utf8(file_get_contents($argv[1])), PHP_EOL;

function convert_title_to_utf8($contents)
{
    $dom = str_get_html($contents);
    $title = $dom->find('title', 0);
    if (empty($title)) {
        return null;
    }
    $title = $title->plaintext;
    $metas = $dom->find('meta');
    $charset = 'auto';
    foreach ($metas as $meta) {
        if (!empty($meta->charset)) { // html5
            $charset = $meta->charset;
        } else if (preg_match('@charset=(.+)@', $meta->content, $match)) {
            $charset = $match[1];
        }
    }
    if (!in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) {
        $charset = 'auto';
    }
    return mb_convert_encoding($title, 'UTF-8', $charset);
}

у меня была такая же проблема с phpQuery (ISO-8859-1 вместо UTF-8) и этот хак помог мне:

$html = '<?xml version="1.0" encoding="UTF-8" ?>' . $html;

mb_internal_encoding('UTF-8'), phpQuery::newDocumentHTML($html, 'utf-8'), mbstring.internal_encoding и другие манипуляции не дали никакого эффекта.


попробуйте без "auto"

что есть:

mb_detect_encoding($text)

вместо:

mb_detect_encoding($text, 'auto')

более подробную информацию можно найти здесь: mb_detect_encoding