Я создаю 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: Обнаружил ошибку в проблемном тексте и изменил пример.
В заголовках моих ответов: Content-Type: text/xml; кодировка=UTF-8. Он уже в UTF-8. Попробовал ваше предложение, тот же результат.
Хорошо, вы можете попробовать вместо этого установить ISO 8859 и настроить свой код, чтобы он соответствовал. Мне интересно, если веб-сервер по умолчанию использует какое-то значение, которое не соответствует вашим данным.
Кодировка базы данных связь важна. Убедитесь, что он установлен в 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> ä foobar';
// strip tags and replace some specific entities with spaces
$stripped = str_replace([' ', ' '], ' ', 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äöü ä"
Однако я бы предложил использовать DOM. Он может загружать HTML и позволяет использовать на нем выражения Xpath.
// have some string with HTML and entities
$text = 'Hello<b>äöü</b> ä 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 procuradores da Lava Jato em Curitiba, que estão sendo Investigados por um suposto acordo faffulento com a Petrobras eo Departamento de Justi..." в строке 118 по-прежнему выдает ошибку кодирования. Но, благодаря вашему совету, я обнаружил, что "
" скрыто в тексте, вызывая ошибку. � должен быть «ç», разделенным пополам усечением.
Таким образом, содержимое описания представляет собой закодированный HTML, и простая подстрока PHP может привести к неполным объектам и более важным неверным символам UTF-8. Вот несколько способов обойти это.
Я добавил два решения для усечения.
Загрузка в виде HTML не мешает структуре XML? Я имею в виду, как это сейчас, я получаю rss xml и читаю его как файл XML, если я визуализирую как HTML, это не будет мешать этому чтению?
Он загружается как HTML и усекается перед добавлением в RSS. Для синтаксического анализатора XML это просто текстовое содержимое, только после того, как значение считано из RSS-XML, снова обрабатывается как фрагмент HTML. Если вы хотите, чтобы это было лучше/четче, используйте Atom. Но вы все равно убираете теги из описания.
Может быть, попробовать установить кодировку с заголовком HTTP перед выводом XML?
header("Content-Type: application/rss+xml; charset=utf-8");