【问题标题】:SimpleXML, please do not expand entities [closed]SimpleXML,请不要扩展实体 [关闭]
【发布时间】:2013-11-14 00:25:40
【问题描述】:

我正在使用 SimpleXML 尝试解析带有 <!ENTITY 声明的 large XML file。不幸的是,SimpleXML 似乎太急于继续扩展这些实体,我宁愿不这样做,因为实体符号很短,易于解析,理论上不会在新版本的文件中改变,而扩展的实体是可能会改变的英文句子。有什么方法可以告诉 SimpleXML 将其关闭?

在将文件内容传递给 XML 解析器之前,我曾想过“预解析”XML 文件以去除 <!ENTITY 位,但这感觉很麻烦,而且由于它是一个巨大的文件,我宁愿尽量少摆弄它。

(请原谅上面的任何错误术语;我已经有一段时间没有做过这种级别的 XML 工作了。)

【问题讨论】:

  • 是否可以创建一个显示不良行为的 XML 和 PHP 代码的小样本?这将极大地帮助其他人理解问题并提出解决方案。
  • @IMSoP:很好的评论,最终没有多大意义,即使这是可能的。刚刚留下了一个广泛的答案。

标签: php xml simplexml


【解决方案1】:

看起来可能是这样,但事实并非如此(除非你指定了我猜你没有在代码中显示你所做的事情的标志)。只是 SimpleXML 只能在您使用 ->asXML() 方法而不是通过 to-string-implementation 时返回给您。

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

<!ENTITY n "noun (common) (futsuumeishi)">

所以让我们选择第一个 &lt;pos&gt; 元素,因为它包含一个 &amp;n; 实体:

$xml = simplexml_load_file($file);
$pos = $xml->entry->sense->pos;

变量 $pos 现在是 &lt;pos&gt; 元素节点的 SimpleXMLElement。让我们输出它来看看解析器对&amp;n; 实体做了什么:

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

输出是:

SimpleXML value (string): noun (common) (futsuumeishi)
SimpleXML value (XML)   : <pos>&n;</pos>

如本例所示,&amp;n; 仍然存在 (&lt;pos&gt;&amp;n;&lt;/pos&gt;),只是在您将其作为字符串值 (noun (common) (futsuumeishi)) 访问时它会被扩展。

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

您甚至可以通过指定 LIBXML_NOENT 选项来控制此行为:

$xml = simplexml_load_file($file, NULL, LIBXML_NOENT);

这实际上会按照您当时的假设进行,实体现在已展开,XML 输出确实 不 不再包含实体:

SimpleXML value (string): noun (common) (futsuumeishi)
SimpleXML value (XML)   : <pos>noun (common) (futsuumeishi)</pos>

所以现在双问号怎么做你要找的?嗯,PHP 中实际上有实体模型的 XML 解析器是 DOMDocument。它是 SimpleXML 的姐妹库,内部共享相同的内存对象。这是没有和有LIBXML_NOENT 的这两种模式的同一对象的输出(更准确地说:它的唯一子节点):

Mode 1:
DOMDocument Class       : DOMEntityReference
DOMDocument value(XML)  : &n;
DOMDocument ->nodeName  : n

Mode 2 (LIBXML_NOENT):
DOMDocument Class       : DOMText
DOMDocument value(XML)  : noun (common) (futsuumeishi)
DOMDocument ->nodeName  : #text

这是由以下代码创建的,它应该使给定输出背后的内容更加可见:

$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_import_simplexml 将 $pos 转换为 DOMElement,我们需要遍历它的子代,我们知道它是有问题的实体引用。

所以现在这开始变得非常有意义:由于 SimpleXML 不能表示 实体引用,它只能提供扩展的字符串值或包含实体的 XML。

否则会有什么不同的字符串值的方式

<pos>&n;</pos>
<pos><![CDATA[&n;]]></pos>

?所以你所要求的只有有限的意义。然而,这并不意味着我们无法处理这个问题,因此可以通过扩展 SimpleXML 来欺骗 SimpleXML 来做到这一点。假设每个仅包含单个实体的子元素都应返回 so。否则应使用标准 SimpleXML 字符串化:

/**
 * Class EntityPreserveXML
 */
class EntityPreserveXML extends SimpleXMLElement
{
    /**
     * @return string
     */
    public function __toString()
    {
        $dom = dom_import_simplexml($this);
        if (
            !$dom instanceof DOMElement
            || $dom->childNodes->length !== 1
            || ! $dom->firstChild instanceof DOMEntityReference
        ) {
            return parent::__toString();
        }

        return $dom->ownerDocument->saveXML($dom->firstChild);
    }
}

让我们在上面的示例中运行它:

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 value (string): &n;
SimpleXML value (XML)   : <pos>&n;</pos>

&amp;n; 作为唯一的子元素现在保留在 SimpleXMLElement 的字符串转换中。但仅仅因为这个工作并不意味着你应该使用它,它打破了以文本形式解析的 XML 和文档模型意义上的 XML 之间的编码边界。

您可能只是在寻找 DOMDocument?这是一个包含更多细节的模型,如果有的话,您可以使用DOMEntityReferences。

【讨论】:

  • 对一个相当模糊的问题的非常彻底的回答!在某些方面,它是this one I wrote a while back 的好伴侣,了解LIBXML_NOCDATA 的实际含义以及那 与实体处理的关系。
  • 我可以在这里使用 DOMDocument 吗?嗯,我没有考虑它,因为我的印象是它仅用于处理 HTML 或其他行为类似于浏览器。也许我会调查一下。或者也许我会坚持使用 SimpleXML,使用 asXML(),并使用(喘气!)正则表达式从结果中提取符号。感谢您在这里分享您对我的选择的了解。
  • @IMSoP:谢谢,这是一个很好的交联。有一天,我们可能会收集很多更好的答案。我想这些年来我们肯定已经有很多了。
  • @GarrettAlbright:您可以根据需要在此处同时使用 SimpleXML 和 DOMDocument。而且我认为不需要正则表达式,您应该更彻底地考虑具体需要什么。这是我能给出的最好建议。答案只是解释了它是如何工作的。
猜你喜欢
  • 1970-01-01
  • 2014-04-20
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
  • 2021-03-22
  • 2016-06-18
  • 1970-01-01
相关资源
最近更新 更多