我正在处理一个无法编辑的第三方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>
我下载了一个响应的示例文件(大约22MB),在这里我完成了调试并保持了理智。两台服务器都运行的是PHP 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版本为2.7.7,而另一个服务器的版本为2.7.6。这真的会有什么不同吗?我找不到libxml更改日志,但似乎不太可能。
这似乎只在响应/文件超过一定大小时才会发生,并且错误总是发生在倒数第二行。
我没有遇到内存问题,测试脚本会立即运行。
如果我知道哪些配置是相关的,我可以发布PHP配置中的差异。知道可能是什么问题吗,或者知道我可能想检查的其他事情吗?
libxml2变更日志包含"608773在xmlGROW(Daniel Veillard)中添加一个丢失的检查",这似乎与输入缓冲有关。注意,我对libxml2内部结构一无所知,但可以想象,您已经解决了2.7.7中修复的2.7.6错误。
检查直接使用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中的差异,还是不同级别的错误报告允许它处理其中一个而不是另一个,但这对我来说似乎是个问题。
My XMLSpy确认您的XML文件(我从您提供的链接下载的)没有问题,并且格式良好。
然而,一个潜在的问题是XML前导中缺少"encoding"属性:根据您的libxml2版本,我猜可能会出现以下情况:服务器检查编码属性,如果没有,则返回到某个默认值(配置设置)。也许较旧的库版本不检查BOM表。
另请参阅此链接,他们在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服务器阻塞。