Ошибка кодирования XML, но и XML, и кодировка входного текста имеют значение utf-8 в php

Я создаю XML Dom с DomDocument в php, содержащий некоторые новости, с заголовком, датой, ссылками и описанием. Проблема возникает в описании одних новостей, но не в других, и в обоих есть акценты и седиль.

Я создаю элемент XML Dom в UTF-8:

$dom = new \DOMDocument("1.0", "UTF-8");

Затем я извлекаю свой текст из базы данных MySQL, который закодирован в латинице-1, и после того, как я проверил кодировку с помощью mb_detect_encoding, он возвращает UTF-8.

Я пробовал следующее:

iconv('UTF-8', 'ISO-8859-1', $descricao);
iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $descricao);
iconv('ISO-8859-1', 'UTF-8', $descricao);
iconv('ISO-8859-1//TRANSLIT', 'UTF-8', $descricao);
mb_convert_encoding($descricao, 'ISO-8859-1', 'UTF-8');
mb_convert_encoding($descricao, 'UTF-8', 'ISO-8859-1');
mb_convert_encoding($descricao, 'UTF-8', 'UTF-8'); //that makes no sense, but who knows

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

Это полный метод, который генерирует XML:

$informativos = Informativo::where('inf_ativo','S')->orderBy('inf_data','DESC')->take(20)->get();
$dom = new \DOMDocument("1.0", "UTF-8");
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$rss = $dom->createElement("rss");

$channel = $dom->createElement("channel");
$title = $dom->createElement("title", "Informativos");
$link = $dom->createElement("link", "http://example.com/informativos");

$channel->appendChild($title);
$channel->appendChild($link);

foreach ($informativos as $informativo) {
    $item = $dom->createElement("item");

    $itemTitle = $dom->createElement("title", $informativo->inf_titulo);
    $itemImage = $dom->createElement("image", "http://example.com/".$informativo->inf_ilustracao);
    $itemLink = $dom->createElement("link", "http://example.com/informativo/".$informativo->informativo_id);
    $descricao = strip_tags($informativo->inf_descricao);
    $descricao = str_replace(" ", " ", $descricao);
    $descricao = str_replace("
", " ", $descricao);
    $descricao = substr($descricao, 0, 150).'...';
    $itemDesc = $dom->createElement("description", $descricao);
    $itemDate = $dom->createElement("pubDate", $informativo->inf_data);

    $item->appendChild($itemTitle);
    $item->appendChild($itemImage);
    $item->appendChild($itemLink);
    $item->appendChild($itemDesc);
    $item->appendChild($itemDate);

    $channel->appendChild($item);
}

$rss->appendChild($channel);

$dom->appendChild($rss);

return $dom->saveXML();

Вот пример успешного вывода:

Segundo a instituição, número de pessoas que vivem na pobreza subiu 7,3 milhões desde 2014, atingindo 21% da população, ou 43,5 milhões de br

И пример, который дает ошибку кодирования:

procuradores da Lava Jato em Curitiba, que estão sendo investigados por um
suposto acordo fraudulento com a Petrobras e o Departamento de Justi�...

Все отображается нормально, пока вышеприведенный проблемный текст описания не дает мне:

"Эта страница содержит следующие ошибки: ошибка в строке 118 в столбце 20: ошибка кодирования Ниже представлен рендеринг страницы до первой ошибки».

Вероятно, проблема здесь в 
. Поскольку я не могу контролировать, есть ли это в тексте, мне нужно правильно отображать эти специальные символы.

ОБНОВЛЕНИЕ 2019-04-12: Обнаружил ошибку в проблемном тексте и изменил пример.

Может быть, попробовать установить кодировку с заголовком HTTP перед выводом XML? header("Content-Type: application/rss+xml; charset=utf-8");

miken32 10.04.2019 02:30

В заголовках моих ответов: Content-Type: text/xml; кодировка=UTF-8. Он уже в UTF-8. Попробовал ваше предложение, тот же результат.

Leno Oliveira 10.04.2019 23:10

Хорошо, вы можете попробовать вместо этого установить ISO 8859 и настроить свой код, чтобы он соответствовал. Мне интересно, если веб-сервер по умолчанию использует какое-то значение, которое не соответствует вашим данным.

miken32 10.04.2019 23:17
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
3
1 178
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Кодировка базы данных связь важна. Убедитесь, что он установлен в UTF-8. В большинстве случаев рекомендуется использовать UTF-8 (для ваших полей). Наборы символов, такие как ISO-8859-1, имеют очень ограниченное количество символов. Поэтому, если в них будет закодирована строка Unicode, это может привести к потере данных.

Второй аргумент DOMDocument::createElement() не работает. In кодирует только специальные символы немного, но не &. Во избежание проблем создавайте и добавляйте содержимое как отдельный текстовый узел. Однако DOMNode::appendChild() возвращает узел добавления, поэтому методы DOMElement::create* могут быть вложенными и связанными.

$data = [
  [
    'inf_titulo' => 'Foo',
    'inf_ilustracao' => 'foo.jpg',
    'informativo_id' => 42,
    'inf_descricao' => 'Some content',
    'inf_data' => 'a-date'
  ]  
];
$informativos = json_decode(json_encode($data));

function stripTagsAndTruncate($text) {
    $text = strip_tags($text);
    $text = str_replace([" ", "
"], " ", $text);
    return substr($text, 0, 150).'...';
}

$dom = new DOMDocument('1.0', 'UTF-8');
$rss = $dom->appendChild($dom->createElement('rss'));
$channel = $rss->appendChild($dom->createElement("channel"));
$channel
  ->appendChild($dom->createElement("title"))
  ->appendChild($dom->createTextNode("Informativos"));
$channel
  ->appendChild($dom->createElement("link"))
  ->appendChild($dom->createTextNode("http://example.com/informativos"));

foreach ($informativos as $informativo) {
    $item = $channel->appendChild($dom->createElement("item"));

    $item
      ->appendChild($dom->createElement("title"))
      ->appendChild($dom->createTextNode($informativo->inf_titulo));
    $item
      ->appendChild($dom->createElement("image"))
      ->appendChild($dom->createTextNode("http://example.com/".$informativo->inf_ilustracao));
    $item
      ->appendChild($dom->createElement("link"))
      ->appendChild($dom->createTextNode("http://example.com/informativo/".$informativo->informativo_id));
    $item
      ->appendChild($dom->createElement("description"))
      ->appendChild($dom->createTextNode(stripTagsAndTruncate($informativo->inf_descricao)));
    $item
      ->appendChild($dom->createElement("pubDate"))
      ->appendChild($dom->createTextNode($informativo->inf_data));
}
$dom->formatOutput = TRUE;
echo $dom->saveXML();

Выход:

<?xml version = "1.0" encoding = "UTF-8"?> 
<rss>
  <channel>
    <title>Informativos</title> 
    <link>http://example.com/informativos</link> 
    <item> 
      <title>Foo</title> 
      <image>http://example.com/foo.jpg</image> 
      <link>http://example.com/informativo/42</link> 
      <description>Some content...</description> 
      <pubDate>a-date</pubDate> 
    </item> 
  </channel> 
</rss>

Усечение фрагмента HTML может привести к повреждению объектов и кодовых точек (если вы не используете строковую функцию, поддерживающую UTF-8). Вот два подхода к ее решению.

Вы можете использовать PCRE в режиме UTF-8 и сопоставить n объектов/кодовых точек:

// have some string with HTML and entities
$text = 'Hello<b>äöü</b>&nbsp;&auml;&#13; foobar';

// strip tags and replace some specific entities with spaces
$stripped = str_replace(['&nbsp;', '&#13;'], ' ', strip_tags($text));
// match 0-10 entities or unicode codepoints
preg_match('(^(?:&[^;]+;|\\X){0,10})u', $stripped, $match);
var_dump($match[0]);

Выход:

string(18) "Helloäöü &auml;"

Однако я бы предложил использовать DOM. Он может загружать HTML и позволяет использовать на нем выражения Xpath.

// have some string with HTML and entities
$text = 'Hello<b>äöü</b>&nbsp;&auml;&#13; foobar';

$document = new DOMDocument();
// force UTF-8 and load
$document->loadHTML('<?xml encoding = "UTF-8"?>'.$text);
$xpath = new DOMXpath($document);
// use xpath to fetch the first 10 characters of the text content
var_dump($xpath->evaluate('substring(//body, 1, 10)'));

Выход:

string(15) "Helloäöü ä"

DOM обычно обрабатывает все строки как UTF-8. Так что кодовые точки - это не проблема. Xpaths substring() работает с текстовым содержимым первого совпадающего узла. Аргументом являются позиции символов (не индекс), поэтому они начинаются с 1.

DOMDocument::loadHTML() добавит теги html и body и декодирует объекты. Результаты будут немного чище, чем при подходе PCRE.

Я изменил свой код на ваш пример, но у него все еще есть проблемы с "&". Этот текст: "Os&#13; procuradores da Lava Jato em Curitiba, que estão sendo Investigados por um&#13; suposto acordo faffulento com a Petrobras eo Departamento de Justi..." в строке 118 по-прежнему выдает ошибку кодирования. Но, благодаря вашему совету, я обнаружил, что "&#13" скрыто в тексте, вызывая ошибку. � должен быть «ç», разделенным пополам усечением.

Leno Oliveira 12.04.2019 22:01

Таким образом, содержимое описания представляет собой закодированный HTML, и простая подстрока PHP может привести к неполным объектам и более важным неверным символам UTF-8. Вот несколько способов обойти это.

ThW 12.04.2019 23:09

Я добавил два решения для усечения.

ThW 12.04.2019 23:23

Загрузка в виде HTML не мешает структуре XML? Я имею в виду, как это сейчас, я получаю rss xml и читаю его как файл XML, если я визуализирую как HTML, это не будет мешать этому чтению?

Leno Oliveira 16.04.2019 21:31

Он загружается как HTML и усекается перед добавлением в RSS. Для синтаксического анализатора XML это просто текстовое содержимое, только после того, как значение считано из RSS-XML, снова обрабатывается как фрагмент HTML. Если вы хотите, чтобы это было лучше/четче, используйте Atom. Но вы все равно убираете теги из описания.

ThW 17.04.2019 04:36

Другие вопросы по теме