Ошибка кодирования XML, но кодировка XML и входного текста - это utf-8 в php - PullRequest
1 голос
/ 10 апреля 2019

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

Я создаю элемент 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: Обнаружена ошибка в проблемном текстеи изменил пример.

1 Ответ

1 голос
/ 12 апреля 2019

Кодировка базы данных подключение важно.Убедитесь, что установлено 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.

...