Php SimpleXML,请不要展开实体

Php SimpleXML,请不要展开实体,php,xml,simplexml,Php,Xml,Simplexml,我正在使用SimpleXML尝试解析带有声明的。不幸的是,SimpleXML似乎太急于展开这些实体了,我希望它没有,因为实体符号很短,很容易解析,理论上在新版本的文件中不会改变,而展开的实体是英语句子,可能会改变。有没有办法让SimpleXML停止 在将文件内容传递给XML解析器之前,我曾想过对XML文件进行“预解析”以去除位,但这感觉有点不对劲,因为它是一个巨大的文件,所以我宁愿尽可能少地修改它 (请原谅上面的任何错误术语;我很久没有做过这种级别的XML工作了。)看起来可能是这样,但事实并非如

我正在使用SimpleXML尝试解析带有
声明的。不幸的是,SimpleXML似乎太急于展开这些实体了,我希望它没有,因为实体符号很短,很容易解析,理论上在新版本的文件中不会改变,而展开的实体是英语句子,可能会改变。有没有办法让SimpleXML停止

在将文件内容传递给XML解析器之前,我曾想过对XML文件进行“预解析”以去除
位,但这感觉有点不对劲,因为它是一个巨大的文件,所以我宁愿尽可能少地修改它


(请原谅上面的任何错误术语;我很久没有做过这种级别的XML工作了。)

看起来可能是这样,但事实并非如此(除非您指定了标志,我想您不会指定,尽管您没有在代码中显示您所做的事情)。只是如果您使用的是
->asXML()
方法,而不是通过to字符串实现,SimpleXML只能将其返回给您

让我们做一些例子来演示它是如何工作的。我从DTD中选择了这个简单的实体:

<!ENTITY n "noun (common) (futsuumeishi)">
变量
$pos
现在是
元素节点的SimpleXMLElement。让我们输出它,看看解析器如何处理
&n实体:

$xml = simplexml_load_file($file);
$pos = $xml->entry->sense->pos;
echo  "SimpleXML value (string): ", $pos         , "\n"
    , "SimpleXML value (XML)   : ", $pos->asXML(), "\n";
输出为:

SimpleXML值(字符串):名词(普通)(futsuumeishi)
SimpleXML值(XML):&n;
如本例所示,
&n
仍然存在(
&n;
),只是当您将其作为字符串值访问时,它将被展开(
名词(公共)(futsuumeishi)

顺便说一下,这是完全可以的,XML规范在这里说,由解析器决定是否扩展这些实体。对于SimpleXML的设计目的,在读取字符串值时,这完全可以扩展

您甚至可以通过指定
LIBXML\u NOENT
选项来控制此行为:

$xml = simplexml_load_file($file, NULL, LIBXML_NOENT);
这将实际执行您当时假设的操作,实体现在已展开,XML输出不再包含实体:

SimpleXML值(字符串):名词(普通)(futsuumeishi)
SimpleXML值(XML):名词(普通)(futsuumeishi)
所以现在双重问号,如何做你想要的?PHP中的XML解析器实际上有一个实体模型,它就是DOMDocument。它是SimpleXML的姐妹库,两者在内部共享相同的内存对象。下面是同一对象(更精确地说:它唯一的子节点)在不使用和使用
LIBXML\NOENT
的两种模式下的输出:

模式1:
DOMDocument类:domentyReference
DOMDocument值(XML):&n;
DOMDocument->nodeName:n
模式2(LIBXML\u NOENT):
DOMDocument类:DOMText
DOMDocument值(XML):名词(通用)(futsuumeishi)
DOMDocument->nodeName:#文本
这是由以下代码创建的,这些代码应使给定输出后面的内容更为可见:

$node   = dom_import_simplexml($pos);
$doc    = $node->ownerDocument;
$entity = $node->firstChild;

echo  "DOMDocument Class       : ", get_class($entity)    , "\n"
    , "DOMDocument value(XML)  : ", $doc->saveXML($entity), "\n"
    , "DOMDocument ->nodeName  : ", $entity->nodeName     , "\n";
如本文所述,它是一个姐妹库,
dom\u import\u simplexml
$pos
转换为一个
domeElement
,我们需要遍历它的子元素,我们知道它是有问题的实体引用

因此,现在这就开始有了完美的意义:因为SimpleXML不能表示实体引用,它只能提供扩展字符串值或包含实体的XML

否则,将如何区别

<pos>&n;</pos>
<pos><![CDATA[&n;]]></pos>
让我们从上面的例子来看:

require('EntityPreserveXML.php');
$xml = simplexml_load_file($file, 'EntityPreserveXML');
$pos = $xml->entry->sense->pos;

echo  "SimpleXML value (string): ", $pos         , "\n"
    , "SimpleXML value (XML)   : ", $pos->asXML(), "\n";
SimpleXML现在正在使用扩展类,该类将按预期提供:

SimpleXML值(字符串):&n;
SimpleXML值(XML):&n;
&n因为它是唯一的子元素,所以现在保存在SimpleXMLElement的到字符串转换中。但仅仅因为这样做并不意味着您应该使用它,它打破了以文本形式解析的XML和文档模型意义上的XML之间的编码边界


也许你只是在找文件?这是一个包含更多细节的模型,您可以从中使用
DOMEntityReference
s(如果有的话)。

是否可以创建一个小样本的XML和PHP代码来显示不希望出现的行为?这将极大地帮助其他人理解问题并提出解决方案。@IMSoP:好的评论,即使这是可能的,但最终也没有多大意义。刚刚留下了一个广泛的答案。可能是对一个坦率而含糊的问题的一个非常彻底的答案的复制品!在某些方面,它可以很好地帮助我们了解
LIBXML\u NOCDATA
的实际含义,以及它与实体处理的关系?嗯,我没有考虑过,因为我的印象是,它只适用于HTML或其他类似浏览器的行为。也许我会调查一下。或者,我可能会坚持使用SimpleXML,使用asXML(),并使用(gasp!)regex从结果中提取符号。谢谢你在这里分享我的选择。@IMSoP:谢谢,这是一个很好的交叉链接。总有一天,我们可能会收集大量更好的答案。我想这些年来我们确实已经有了很多了。@GarrettAlbright:您可以在这里同时使用SimpleXML和DOMDocument,这取决于您需要做什么。我不认为有必要使用正则表达式,你应该更彻底地思考你需要什么,具体是什么。这是我能给出的最好的建议。答案只是解释了它是如何工作的。
require('EntityPreserveXML.php');
$xml = simplexml_load_file($file, 'EntityPreserveXML');
$pos = $xml->entry->sense->pos;

echo  "SimpleXML value (string): ", $pos         , "\n"
    , "SimpleXML value (XML)   : ", $pos->asXML(), "\n";