Мне нужно загрузить XML-документ в PHP, который поступает из внешнего источника. XML не объявляет его кодировку и содержит незаконные символы, такие как &
. Если я попытаюсь загрузить XML-документ непосредственно в браузере, я получаю такие ошибки, как «Недопустимый символ был найден в текстовом контенте» также при загрузке файла в PHP. Я получаю множество предупреждений вроде: xmlParseEntityRef: no name in Entity
и Input is not proper UTF-8, indicate encoding ! Bytes: 0x9C 0x31 0x21 0x3C
Input is not proper UTF-8, indicate encoding ! Bytes: 0x9C 0x31 0x21 0x3C
.
Понятно, что XML не очень хорошо сформирован и содержит незаконные символы, которые должны быть преобразованы в объекты XML.
Это связано с тем, что XML-канал состоит из данных, предоставленных множеством других пользователей, и ясно, что он не проверяется и не переформатирован, прежде чем я его получу.
Я говорил с поставщиком XML-фида, и они говорят, что пытаются заставить контент-провайдеры разобраться в этом, но это кажется глупым, поскольку они должны сначала подтвердить ввод.
Мне в основном нужно исправить XML, исправляя любые ошибки кодирования и преобразовывая любые незаконные символы в XML-объекты, чтобы проблема XML загружалась при использовании функций DOMDocument PHP.
В настоящее время мой код выглядит так:
$feedURL = '3704017_14022010_050004.xml'; $dom = new DOMDocument(); $dom->load($feedURL);
Пример файла XML, показывающего проблему с кодировкой (нажмите, чтобы загрузить): feed.xml
Пример XML, который содержит символы, которые не были преобразованы в объекты XML:
<?xml version="1.0"?> <feed> <RECORD> <ID>117387</ID> <ADVERTISERNAME>Test</ADVERTISERNAME> <AID>10544740</AID> <NAME>This & This</NAME> <DESCRIPTION>For one day only this is > than this.</DESCRIPTION> </RECORD> </feed>
Попробуйте использовать библиотеку Tidy, которая может использоваться для очистки плохого HTML и XML http://php.net/manual/en/book.tidy.php
Чистое PHP-решение для исправления некоторых XML:
<?xml version="1.0"?> <feed> <RECORD> <ID>117387</ID> <ADVERTISERNAME>Test < texter</ADVERTISERNAME> <AID>10544740</AID> <NAME>This & This</NAME> <DESCRIPTION>For one day only this is > than this.</DESCRIPTION> </RECORD> </feed>
Было бы примерно так:
function cleanupXML($xml) { $xmlOut = ''; $inTag = false; $xmlLen = strlen($xml); for($i=0; $i < $xmlLen; ++$i) { $char = $xml[$i]; // $nextChar = $xml[$i+1]; switch ($char) { case '<': if (!$inTag) { // Seek forward for the next tag boundry for($j = $i+1; $j < $xmlLen; ++$j) { $nextChar = $xml[$j]; switch($nextChar) { case '<': // Means a < in text $char = htmlentities($char); break 2; case '>': // Means we are in a tag $inTag = true; break 2; } } } else { $char = htmlentities($char); } break; case '>': if (!$inTag) { // No need to seek ahead here $char = htmlentities($char); } else { $inTag = false; } break; default: if (!$inTag) { $char = htmlentities($char); } break; } $xmlOut .= $char; } return $xmlOut; }
Это простой конечный автомат, отмечающий, находятся ли мы в теге или нет, а если нет, то кодируем текст, используя htmlentities.
Стоит отметить, что это будет голод на больших файлах, поэтому вы можете переписать его как плагин потока или предварительный процессор.
Чтобы решить эту проблему, установите для свойства восстановления DomDocument значение TRUE
перед загрузкой XML-документа
$dom->recover = TRUE;
Попробуйте этот код:
$feedURL = '3704017_14022010_050004.xml'; $dom = new DOMDocument(); $dom->recover = TRUE; $dom->load($feedURL);
Если опциональное расширение не является вариантом, вы можете рассмотреть htmlpurifier .