Ошибки simplexml_load_string в больших файлах происходят в одной системе, но не в другой

Я имею дело с третьей стороной библиотеки PHP, которую я не могу редактировать, и она работает нормально почти год. Он использует simplexml_load_string для ответа с удаленного сервера. В последнее время он задыхается от больших ответов. Это фид данных для списков недвижимости, и формат выглядит примерно так:

 <?xml version="1.0"?> <RETS ReplyCode="0" ReplyText="Operation Successful Reference ID: 9bac803e-b507-49b7-ac7c-d8e8e3f3aa89"> <COUNT Records="9506" /> <DELIMITER value="09" /> <COLUMNS> sysid 1 2 3 4 5 6 </COLUMNS> <DATA> 252370080 Residential 0.160 No ADDR0 06051</DATA> <DATA> 252370081 Residential 0.440 Yes ADDR0 06043</DATA> <DATA> 252370082 Residential 1.010 No ADDR0 06023</DATA> <DATA>More tab delimited text</DATA> <!-- snip 9000+ lines --> </RETS> 

Я загрузил образец файла ответа (около 22 МБ), вот где я закончил свою отладку и здравомыслие. На обоих серверах работает PHP Version 5.3.8, но обратите внимание на разные результаты. Я уверен, что оба файла одинаковы (я полагаю, что разные типы файлов, strlen и последние 50 символов могут быть объяснены с помощью новых строк Windows, имеющих дополнительный символ возврата каретки). Тестовый скрипт:

 error_reporting(-1); ini_set('display_errors', 1); $file = 'error-example.xml'; $xml = file_get_contents($file); echo 'filesize: '; var_dump(filesize($file)); echo 'strlen: '; var_dump(strlen($xml)); echo 'simplexml object? '; var_dump(is_object(simplexml_load_string($xml))); echo 'Last 50 characters: '; var_dump(substr($xml, -50)); 

Вывод локально в Windows:

 filesize: int(21893604) strlen: int(21893604) simplexml object? bool(true) Last 50 characters: string(50) "RD DR CT Watertown 203-555-5555 </DATA> </RETS>" 

Вывод на удаленном сервере UNIX:

 filesize: int(21884093) strlen: int(21884093) simplexml object? Warning: simplexml_load_string(): Entity: line 9511: parser error : internal error in /path/to/test.php on line 19 Warning: simplexml_load_string(): AULTED CEILING IN FOYER, BRICK FP IN FR, NEW FLOORING IN LR DR FR FOYER KITCHEN in /path/to/test.php on line 19 Warning: simplexml_load_string(): ^ in /path/to/test.php on line 19 Warning: simplexml_load_string(): Entity: line 9511: parser error : Extra content at the end of the document in /path/to/test.php on line 19 Warning: simplexml_load_string(): AULTED CEILING IN FOYER, BRICK FP IN FR, NEW FLOORING IN LR DR FR FOYER KITCHEN in /path/to/test.php on line 19 Warning: simplexml_load_string(): ^ in /path/to/test.php on line 19 bool(false) Last 50 characters: string(50) "ORD DR CT Watertown 203-555-5555 </DATA> </RETS>" 

Некоторые ответы на комментарии и дополнительную информацию:

  • Сам XML представляется действительным, насколько я могу судить (и он работает в моей системе).

  • magic_quotes_runtime определенно выключен.

  • Рабочий сервер имеет libxml Version 2.7.7, а другой – 2.7.6. Может ли это действительно изменить ситуацию? Я не мог найти журнал изменений libxml, но это кажется маловероятным.

  • Это происходит только тогда, когда ответ / файл превышает определенный размер, и ошибка всегда возникает в следующей строке.

  • Я не сталкиваюсь с проблемами памяти, тестовый скрипт запускается мгновенно.

Есть различия в конфигурациях PHP, которые я могу опубликовать, если бы знал, какие из них релевантны. Любая идея, что может быть проблемой, или знать что-нибудь еще, что я могу проверить?

В списке изменений libxml2 содержится «608773 добавить недостающую проверку в xmlGROW (Daniel Veillard)» , которая, похоже, связана с буферизацией ввода. Примечание. Я ничего не знаю о внутренних функциях libxml2, но кажется, что вы заметили ошибку 2.7.6, зафиксированную в 2.7.7.

Проверьте, не отличается ли поведение при использовании simplexml_load_file() напрямую, и попробуйте установить параметры, связанные с парсером libxml, например

 simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_COMPACT | LIBXML_PARSEHUGE) 

В частности, вы можете попробовать флаг LIBXML_PARSEHUGE .

http://php.net/manual/en/libxml.constants.php
Флаг XML_PARSE_HUGE ослабляет любой жестко определенный предел из анализатора. Это влияет на ограничения, такие как максимальная глубина документа или рекурсия сущности, а также пределы размера текстовых узлов.

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

У вас должен быть ТОЛЬКО ОДИН КРУТ.

то есть. Все должно быть внутри ваших тегов:

 <?xml version="1.0"?> <RETS> ... </RETS> 

У вас есть несколько корней в XML, что вызовет проблему 🙂

Попробуйте обернуть все это в корневой узел и посмотреть, работает ли он.

 <?xml version="1.0"?> <rootNode> <RETS> ... </RETS> <count bla="99" /> </rootNode> 

Я не уверен, будет ли это различие в libxml или другой уровень сообщений об ошибках, позволяющий ему работать на одном, а не на другом, но это похоже на проблему.

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

Однако одной из потенциальных проблем является тот факт, что в преамбуле XML отсутствует атрибут «encoding»: в зависимости от вашей версии libxml2, я предполагаю, что следующий сценарий может быть возможен: сервер проверяет атрибут кодирования, при отсутствии которого сервер возвращается к некоторое значение по умолчанию (настройка конфигурации). Возможно, более старые версии библиотеки не проверяют спецификацию.

Также см. Эту ссылку. У них была аналогичная проблема с кодировкой libxml: https://stackoverflow.com/questions/4724241/utf-8-problems-with-php-dom-on-debian-server

суть которого в том, что обновление библиотеки libxml действительно может решить проблему. В качестве альтернативы, возможно, стоит проверить настройки кодировки по умолчанию в конфигурации.

Согласно моему XMLSpy, файл закодирован в utf-8 – в качестве теста, возможно, стоит проверить, если указать

 <?xml version="1.0" encoding="UTF-8"?> 

поскольку преамбула файла останавливает ваш Unix-сервер от удушья.